Merge "Update GenericAtom definition to decouple from metrics_constants proto"
diff --git a/Android.bp b/Android.bp
index 3957f7e..21b8404 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,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",
@@ -576,6 +577,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",
@@ -1001,6 +1003,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.
@@ -1008,7 +1011,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",
@@ -1058,7 +1061,7 @@
 
 droiddoc {
     name: "api-stubs-docs",
-    defaults: ["framework-docs-default"],
+    defaults: ["api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
         ":api-version-xml",
@@ -1090,7 +1093,7 @@
 
 droiddoc {
     name: "system-api-stubs-docs",
-    defaults: ["framework-docs-default"],
+    defaults: ["api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
         ":api-version-xml",
@@ -1124,7 +1127,7 @@
 
 droiddoc {
     name: "test-api-stubs-docs",
-    defaults: ["framework-docs-default"],
+    defaults: ["api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
         ":api-version-xml",
@@ -1149,6 +1152,286 @@
     },
 }
 
+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: [
+        "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: [
@@ -1182,12 +1465,13 @@
 
 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",
@@ -1202,12 +1486,13 @@
 
 droiddoc {
     name: "hiddenapi-mappings",
-    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_mapping_filename: "dex-mapping.txt",
     args: framework_docs_args +
@@ -1251,7 +1536,7 @@
     "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
 
 doc_defaults {
-    name: "metalava-framework-docs-default",
+    name: "metalava-api-stubs-default",
     srcs: [
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
@@ -1282,12 +1567,14 @@
     metalava_enabled: true,
     metalava_annotations_enabled: true,
     metalava_previous_api: ":public-api-for-metalava-annotations",
-    metalava_merge_annotations_dir: "tools/metalava/manual",
+    metalava_merge_annotations_dirs: [
+        "tools/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",
@@ -1300,7 +1587,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",
@@ -1314,7 +1601,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 7890983..f81f756 100644
--- a/Android.mk
+++ b/Android.mk
@@ -311,364 +311,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)
@@ -772,7 +421,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))) \
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 2b8b8f2..9fad19f 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -22,6 +22,7 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal.CallSession;
 
 import java.util.Random;
 
@@ -61,7 +62,7 @@
         Binder b = new Binder();
         int i = 0;
         while (state.keepRunning()) {
-            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, i % 100);
+            CallSession s = mBinderCallsStats.callStarted(b, i % 100);
             mBinderCallsStats.callEnded(s, 0, 0);
             i++;
         }
@@ -73,7 +74,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         Binder b = new Binder();
         while (state.keepRunning()) {
-            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
+            CallSession s = mBinderCallsStats.callStarted(b, 0);
             mBinderCallsStats.callEnded(s, 0, 0);
         }
     }
diff --git a/api/current.txt b/api/current.txt
index 9a47a66..abf1224 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3951,22 +3951,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 +4024,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 {
@@ -6091,6 +6082,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);
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/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 160d6e8..e6e8455 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -174,10 +174,16 @@
         {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}},
         // binder_calls
         {android::util::BINDER_CALLS,
-         {{4, 5, 6, 8},
-          {2, 3, 7, 9, 10, 11},
+         {{4, 5, 6, 8, 12},
+          {2, 3, 7, 9, 10, 11, 13},
           1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BINDER_CALLS)}}
+          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) {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 3e9077b..4484c8f 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -111,13 +111,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
@@ -936,6 +929,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
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 43b1a0e..a07843b 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -1,7 +1,6 @@
 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
@@ -40,6 +39,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
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3f579bc..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;
@@ -1323,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
@@ -1526,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(" }");
+            }
         }
     }
 
@@ -1596,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>() {
@@ -1719,10 +1563,6 @@
                 return new RunningTaskInfo[size];
             }
         };
-
-        private RunningTaskInfo(Parcel source) {
-            readFromParcel(source);
-        }
     }
 
     /**
@@ -2559,7 +2399,6 @@
         return clearApplicationUserData(mContext.getPackageName(), null);
     }
 
-
     /**
      * Permits an application to get the persistent URI permissions granted to another.
      *
@@ -2571,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);
     }
 
     /**
@@ -2592,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);
     }
 
     /**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 2e4404c..c895978 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -50,13 +50,6 @@
     public static final int ALLOW_FULL_ONLY = 2;
 
     /**
-     * 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);
-
-    /**
      * Verify that calling app has access to the given provider.
      */
     public abstract String checkContentProviderAccess(String authority, int userId);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2daa577..151e9a5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2731,7 +2731,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
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/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 193f933..b5295d1 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -66,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.
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 19d7c83..285f83b 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -243,12 +243,6 @@
     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,
@@ -363,9 +357,6 @@
     ActivityManager.StackInfo getFocusedStackInfo();
     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);
     boolean setProcessMemoryTrimLevel(in String process, int uid, int level);
@@ -441,12 +432,6 @@
     void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
-    // 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 removeStack(int stackId);
     void makePackageIdle(String packageName, int userId);
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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b432baa..003f364 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -226,6 +226,14 @@
                         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
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/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/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 2099e75..51372c4 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...
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 2c1e59b..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,32 +165,6 @@
         ObjectPool.recycle(this);
     }
 
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder(64);
-        sb.append("ClientTransaction{");
-        if (mActivityToken != null) {
-            sb.append(" a:").append(Integer.toHexString(System.identityHashCode(mActivityToken)));
-        }
-        if (mActivityCallbacks != null && !mActivityCallbacks.isEmpty()) {
-            sb.append(" c:");
-            final int size = mActivityCallbacks.size();
-            for (int i = 0; i < size; i++) {
-                sb.append(mActivityCallbacks.get(i).getClass().getSimpleName());
-                if (i < size - 1) {
-                    sb.append(",");
-                }
-            }
-        }
-        if (mLifecycleStateRequest != null) {
-            sb.append(" s:");
-            sb.append(mLifecycleStateRequest.getClass().getSimpleName());
-        }
-        sb.append(" }");
-        return sb.toString();
-    }
-
-
     // Parcelable implementation
 
     /** Write to Parcel. */
@@ -262,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/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 503e18b6..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;
@@ -63,6 +67,8 @@
      * 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();
         if (token != null) {
             final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
@@ -77,18 +83,20 @@
                 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, "Skip pre-destroyed " + transaction);
+                    Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
+                            + transactionToString(transaction, mTransactionHandler));
                     return;
                 }
             }
         }
-        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
+
+        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. */
@@ -99,7 +107,7 @@
             // 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);
@@ -116,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);
@@ -135,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);
             }
         }
     }
@@ -147,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.
@@ -158,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);
@@ -167,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);
     }
 
     /**
@@ -176,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,
@@ -225,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/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/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f923738..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;
@@ -2143,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) {
@@ -2159,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) {
@@ -2179,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) {
@@ -2199,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();
@@ -2215,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/Context.java b/core/java/android/content/Context.java
index c515bce..6abe777 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3340,6 +3340,16 @@
 
     /**
      * 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.
      *
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 2f3bf63..f99c52f4 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/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index c6c676f..2d45b14 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 com.android.internal.util.ArrayUtils;
+
+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;
 
@@ -95,9 +104,6 @@
         if (mWhereClause == null) {
             mWhereClause = new StringBuilder(inWhere.length() + 16);
         }
-        if (mWhereClause.length() == 0) {
-            mWhereClause.append('(');
-        }
         mWhereClause.append(inWhere);
     }
 
@@ -115,9 +121,6 @@
         if (mWhereClause == null) {
             mWhereClause = new StringBuilder(inWhere.length() + 16);
         }
-        if (mWhereClause.length() == 0) {
-            mWhereClause.append('(');
-        }
         DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
     }
 
@@ -376,6 +379,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 +392,162 @@
             // 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
+     * @hide
+     */
+    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;
+        }
+
+        final ArrayMap<String, Object> rawValues = values.getValues();
+        final String[] updateArgs = new String[rawValues.size()];
+        for (int i = 0; i < updateArgs.length; i++) {
+            final Object arg = rawValues.valueAt(i);
+            updateArgs[i] = (arg != null) ? arg.toString() : null;
+        }
+
+        final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs, 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);
+    }
+
+    /**
+     * 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
+     * @hide
+     */
+    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 +579,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 +599,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 +808,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/hardware/face/Face.java b/core/java/android/hardware/face/Face.java
index 6a508ac..d6724d7 100644
--- a/core/java/android/hardware/face/Face.java
+++ b/core/java/android/hardware/face/Face.java
@@ -26,47 +26,13 @@
  * @hide
  */
 public final class Face extends BiometricAuthenticator.Identifier {
-    private CharSequence mName;
-    private int mFaceId;
-    private long mDeviceId; // physical device this face is associated with
 
     public Face(CharSequence name, int faceId, long deviceId) {
-        mName = name;
-        mFaceId = faceId;
-        mDeviceId = deviceId;
+        super(name, faceId, deviceId);
     }
 
     private Face(Parcel in) {
-        mName = in.readString();
-        mFaceId = 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 getFaceId() {
-        return mFaceId;
-    }
-
-    /**
-     * Device this face belongs to.
-     *
-     * @hide
-     */
-    public long getDeviceId() {
-        return mDeviceId;
+        super(in.readString(), in.readInt(), in.readLong());
     }
 
     /**
@@ -83,9 +49,9 @@
      * @param flags Additional flags about how the object should be written.
      */
     public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mName.toString());
-        out.writeInt(mFaceId);
-        out.writeLong(mDeviceId);
+        out.writeString(getName().toString());
+        out.writeInt(getBiometricId());
+        out.writeLong(getDeviceId());
     }
 
     public static final Parcelable.Creator<Face> CREATOR = new Parcelable.Creator<Face>() {
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 3de9de3..6a3dd7d 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -418,7 +418,7 @@
             try {
                 mRemovalCallback = callback;
                 mRemovalFace = face;
-                mService.remove(mToken, face.getFaceId(), userId, mServiceReceiver);
+                mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver);
             } catch (RemoteException e) {
                 Log.w(TAG, "Remote exception in remove: ", e);
                 if (callback != null) {
@@ -796,6 +796,18 @@
          */
         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()));
+        }
+
     }
 
     /**
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/os/Binder.java b/core/java/android/os/Binder.java
index 175b405..662d130 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,8 +22,8 @@
 import android.util.Log;
 import android.util.Slog;
 
-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;
@@ -93,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;
@@ -106,6 +111,7 @@
                 Binder.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
     }
 
+
     // Transaction tracking code.
 
     /**
@@ -151,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;
 
@@ -721,8 +736,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,
@@ -738,7 +755,9 @@
             }
             res = onTransact(code, data, reply, flags);
         } catch (RemoteException|RuntimeException e) {
-            binderCallsStats.callThrewException(callSession, e);
+            if (observer != null) {
+                observer.callThrewException(callSession, e);
+            }
             if (LOG_RUNTIME_EXCEPTION) {
                 Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
             }
@@ -770,9 +789,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;
     }
 }
-
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 8481387..ebd90bd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12067,6 +12067,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
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/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index eee3630..f2429bd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3922,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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 41daf9e..b5c7364 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;
@@ -202,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;
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/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index af50420..76f9a8d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -13181,7 +13181,7 @@
         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;
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 63c7dd0..dc30205 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BinderInternal.CallSession;
 import com.android.internal.util.Preconditions;
 
 import java.io.PrintWriter;
@@ -47,8 +48,8 @@
  * Collects statistics about CPU time spent per binder call across multiple dimensions, e.g.
  * per thread, uid or call description.
  */
-public class BinderCallsStats {
-    public static final boolean ENABLED_DEFAULT = true;
+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 = 10;
 
@@ -57,12 +58,9 @@
     private static final int PERIODIC_SAMPLING_INTERVAL = 10;
     private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
     private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
-    private static final CallSession NOT_ENABLED = new CallSession();
-    private static final BinderCallsStats sInstance = new BinderCallsStats(new Random());
 
-    private volatile boolean mEnabled = ENABLED_DEFAULT;
-    private volatile boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
-    private volatile int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
+    private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
+    private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
     @GuardedBy("mLock")
     private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
     @GuardedBy("mLock")
@@ -72,28 +70,24 @@
     private final Random mRandom;
     private long mStartTime = System.currentTimeMillis();
 
-    @VisibleForTesting  // Use getInstance() instead.
     public BinderCallsStats(Random random) {
         this.mRandom = random;
     }
 
+    @Override
     public CallSession callStarted(Binder binder, int code) {
         return callStarted(binder.getClass().getName(), code, binder.getTransactionName(code));
     }
 
     private CallSession callStarted(String className, int code, @Nullable String methodName) {
-        if (!mEnabled) {
-            return NOT_ENABLED;
-        }
-
         CallSession s = mCallSessionsPool.poll();
         if (s == null) {
             s = new CallSession();
         }
 
-        s.callStat.className = className;
-        s.callStat.msg = code;
-        s.callStat.methodName = methodName;
+        s.className = className;
+        s.transactionCode = code;
+        s.methodName = methodName;
         s.exceptionThrown = false;
         s.cpuTimeStarted = -1;
         s.timeStarted = -1;
@@ -109,9 +103,9 @@
         return s;
     }
 
-    public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
-        Preconditions.checkNotNull(s);
-        if (s == NOT_ENABLED) {
+    @Override
+    public void callEnded(@Nullable CallSession s, int parcelRequestSize, int parcelReplySize) {
+        if (s == null) {
             return;
         }
 
@@ -124,10 +118,6 @@
 
     private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
         synchronized (mLock) {
-            if (!mEnabled) {
-                return;
-            }
-
             final int callingUid = getCallingUid();
             UidEntry uidEntry = mUidEntries.get(callingUid);
             if (uidEntry == null) {
@@ -135,7 +125,7 @@
                 mUidEntries.put(callingUid, uidEntry);
             }
             uidEntry.callCount++;
-            CallStat callStat = uidEntry.getOrCreate(s.callStat);
+            CallStat callStat = uidEntry.getOrCreate(s.className, s.transactionCode);
             callStat.callCount++;
 
             // Non-negative time signals we need to record data for this call.
@@ -147,7 +137,7 @@
                 uidEntry.recordedCallCount++;
 
                 callStat.recordedCallCount++;
-                callStat.methodName = s.callStat.methodName;
+                callStat.methodName = s.methodName;
                 callStat.cpuTimeMicros += duration;
                 callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
                 callStat.latencyMicros += latencyDuration;
@@ -164,16 +154,9 @@
         }
     }
 
-    /**
-     * 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) {
-        Preconditions.checkNotNull(s);
-        if (!mEnabled) {
+    @Override
+    public void callThrewException(@Nullable CallSession s, Exception exception) {
+        if (s == null) {
             return;
         }
         s.exceptionThrown = true;
@@ -208,7 +191,7 @@
                     exported.uid = entry.uid;
                     exported.className = stat.className;
                     exported.methodName = stat.methodName == null
-                            ? String.valueOf(stat.msg) : stat.methodName;
+                            ? String.valueOf(stat.transactionCode) : stat.methodName;
                     exported.cpuTimeMicros = stat.cpuTimeMicros;
                     exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
                     exported.latencyMicros = stat.latencyMicros;
@@ -226,6 +209,13 @@
         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) {
         synchronized (mLock) {
             dumpLocked(pw, appIdToPkgNameMap, verbose);
@@ -233,11 +223,6 @@
     }
 
     private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
-        if (!mEnabled) {
-            pw.println("Binder calls stats disabled.");
-            return;
-        }
-
         long totalCallsCount = 0;
         long totalRecordedCallsCount = 0;
         long totalCpuTime = 0;
@@ -340,10 +325,6 @@
         return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
     }
 
-    public static BinderCallsStats getInstance() {
-        return sInstance;
-    }
-
     public void setDetailedTracking(boolean enabled) {
         synchronized (mLock) {
             if (enabled != mDetailedTracking) {
@@ -353,15 +334,6 @@
         }
     }
 
-    public void setEnabled(boolean enabled) {
-        synchronized (mLock) {
-            if (enabled != mEnabled) {
-                mEnabled = enabled;
-                reset();
-            }
-        }
-    }
-
     public void setSamplingInterval(int samplingInterval) {
         synchronized (mLock) {
             if (samplingInterval != mPeriodicSamplingInterval) {
@@ -386,6 +358,7 @@
         public int uid;
         public String className;
         public String methodName;
+        public boolean screenInteractive;
         public long cpuTimeMicros;
         public long maxCpuTimeMicros;
         public long latencyMicros;
@@ -400,7 +373,7 @@
     @VisibleForTesting
     public static class CallStat {
         public String className;
-        public int msg;
+        public int transactionCode;
         // Method name might be null when we cannot resolve the transaction code. For instance, if
         // the binder was not generated by AIDL.
         public @Nullable String methodName;
@@ -427,40 +400,41 @@
         CallStat() {
         }
 
-        CallStat(String className, int msg) {
+        CallStat(String className, int transactionCode) {
             this.className = className;
-            this.msg = msg;
+            this.transactionCode = transactionCode;
         }
 
         @Override
+        public String toString() {
+            return className + "#" + (methodName == null ? transactionCode : methodName);
+        }
+    }
+
+    /** Key used to store CallStat object in a Map. */
+    public static class CallStatKey {
+        public String className;
+        public int transactionCode;
+
+        @Override
         public boolean equals(Object o) {
             if (this == o) {
                 return true;
             }
 
-            CallStat callStat = (CallStat) o;
-            return msg == callStat.msg && (className.equals(callStat.className));
+            CallStatKey key = (CallStatKey) o;
+            return transactionCode == key.transactionCode
+                    && (className.equals(key.className));
         }
 
         @Override
         public int hashCode() {
             int result = className.hashCode();
-            result = 31 * result + msg;
+            result = 31 * result + transactionCode;
             return result;
         }
-
-        @Override
-        public String toString() {
-            return className + "#" + (methodName == null ? msg : methodName);
-        }
     }
 
-    public static class CallSession {
-        long cpuTimeStarted;
-        long timeStarted;
-        boolean exceptionThrown;
-        final CallStat callStat = new CallStat();
-    }
 
     @VisibleForTesting
     public static class UidEntry {
@@ -480,14 +454,21 @@
         }
 
         // 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);
+        CallStat getOrCreate(String className, int transactionCode) {
+            // Use a global temporary key to avoid creating new objects for every lookup.
+            mTempKey.className = className;
+            mTempKey.transactionCode = transactionCode;
+            CallStat mapCallStat = mCallStats.get(mTempKey);
             // 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(className, transactionCode);
+                CallStatKey key = new CallStatKey();
+                key.className = className;
+                key.transactionCode = transactionCode;
+                mCallStats.put(key, mapCallStat);
             }
             return mapCallStat;
         }
@@ -496,7 +477,7 @@
          * Returns list of calls sorted by CPU time
          */
         public List<CallStat> getCallStatsList() {
-            List<CallStat> callStats = new ArrayList<>(mCallStats.keySet());
+            List<CallStat> callStats = new ArrayList<>(mCallStats.values());
             callStats.sort((o1, o2) -> {
                 if (o1.cpuTimeMicros < o2.cpuTimeMicros) {
                     return 1;
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 5bddd2f..4b93c86 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,64 @@
     }
 
     /**
+     * A session used by {@link Observer} in order to keep track of some data.
+     */
+    public static class CallSession {
+        // Binder interface descriptor.
+        public String className;
+        // Binder transaction code.
+        public int transactionCode;
+        // Binder transaction method name.
+        public String methodName;
+        // 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 +142,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 +151,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/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/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 04e5658..ed7316a 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 {
@@ -943,5 +954,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/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index b6a37bc..bdb3e08 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -81,6 +81,22 @@
     }
 
     @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");
 
@@ -93,14 +109,14 @@
         AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
 
         // Check properties on "original" structure
-        assertStructureWithManySmallViews(structure);
+        assertStructureWithManySmallViews(structure, NUMBER_SMALL_VIEWS);
 
         // Check properties on "cloned" structure
         AssistStructure clone = cloneThroughParcel(structure);
-        assertStructureWithManySmallViews(clone);
+        assertStructureWithManySmallViews(clone, NUMBER_SMALL_VIEWS);
     }
 
-    private void assertStructureWithManySmallViews(AssistStructure structure) {
+    private void assertStructureWithManySmallViews(AssistStructure structure, int expectedSize) {
         int i = 0;
         try {
             assertPackageName(structure);
@@ -123,12 +139,12 @@
             // Parent
             ViewNode parent = rootView.getChildAt(1);
             assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
-            assertThat(parent.getChildCount()).isEqualTo(NUMBER_SMALL_VIEWS);
+            assertThat(parent.getChildCount()).isEqualTo(expectedSize);
             assertThat(parent.getIdEntry()).isEqualTo("parent");
             assertThat(parent.getAutofillId()).isNotNull();
 
             // Children
-            for (i = 0; i < NUMBER_SMALL_VIEWS; i++) {
+            for (i = 0; i < expectedSize; i++) {
                 ViewNode smallView = parent.getChildAt(i);
                 assertSmallView(smallView);
             }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 3d114f4..a788a93 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -283,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/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 60e512c..37dec13 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -119,6 +119,9 @@
                     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,
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 07e2af8..20dc872 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -23,6 +23,8 @@
 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;
@@ -52,7 +54,7 @@
         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);
 
@@ -65,7 +67,7 @@
         assertEquals(1, uidEntry.recordedCallCount);
         assertEquals(10, uidEntry.cpuTimeMicros);
         assertEquals(binder.getClass().getName(), callStatsList.get(0).className);
-        assertEquals(1, callStatsList.get(0).msg);
+        assertEquals(1, callStatsList.get(0).transactionCode);
 
         // CPU usage is sampled, should not be tracked here.
         callSession = bcs.callStarted(binder, 1);
@@ -93,7 +95,7 @@
         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);
 
@@ -109,7 +111,7 @@
         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(1, callStatsList.get(0).transactionCode);
 
         callSession = bcs.callStarted(binder, 1);
         bcs.time += 20;
@@ -134,44 +136,21 @@
     }
 
     @Test
-    public void testDisabled() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats();
-        bcs.setEnabled(false);
-
-        Binder binder = new Binder();
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
-        bcs.time += 10;
-        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
-        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
-        assertEquals(0, uidEntries.size());
-    }
-
-    @Test
-    public void testDisableInBetweenCall() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats();
-        bcs.setEnabled(true);
-
-        Binder binder = new Binder();
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
-        bcs.time += 10;
-        bcs.setEnabled(false);
-        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
-        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
-        assertEquals(0, uidEntries.size());
-    }
-
-    @Test
     public void testEnableInBetweenCall() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
-        bcs.setEnabled(false);
-
         Binder binder = new Binder();
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
-        bcs.time += 10;
-        bcs.setEnabled(true);
-        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+        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());
@@ -184,7 +163,7 @@
         bcs.setSamplingInterval(2);
 
         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);
 
@@ -221,7 +200,7 @@
         bcs.setSamplingInterval(2);
 
         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);
 
@@ -261,16 +240,16 @@
         Binder binder = new Binder() {
             @Override
             public String getTransactionName(int code) {
-              return "resolved";
+                return "resolved";
             }
         };
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+        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();
-        assertEquals(1, callStatsList.get(0).msg);
+        assertEquals(1, callStatsList.get(0).transactionCode);
         assertEquals("resolved", callStatsList.get(0).methodName);
     }
 
@@ -279,7 +258,7 @@
         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);
 
@@ -295,7 +274,7 @@
         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 += 50;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
@@ -314,7 +293,7 @@
         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.elapsedTime += 5;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
@@ -341,7 +320,7 @@
         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.callThrewException(callSession, new IllegalStateException());
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
@@ -364,7 +343,7 @@
         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.callThrewException(callSession, new IllegalStateException());
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
@@ -377,7 +356,7 @@
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(false);
         Binder binder = new Binder();
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+        CallSession callSession = bcs.callStarted(binder, 1);
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
         assertEquals(0, bcs.getExportedCallStats().size());
@@ -388,7 +367,7 @@
         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.elapsedTime += 20;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -415,14 +394,14 @@
         long elapsedTime = 0;
 
         TestBinderCallsStats() {
-          // Make random generator not random.
-          super(new Random() {
-            int mCallCount = 0;
+            // Make random generator not random.
+            super(new Random() {
+                int mCallCount = 0;
 
-            public int nextInt() {
-              return mCallCount++;
-            }
-          });
+                public int nextInt() {
+                    return mCallCount++;
+                }
+            });
         }
 
         @Override
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/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 59760ab..8d0b615 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -230,7 +230,6 @@
         "RenderProperties.cpp",
         "ResourceCache.cpp",
         "SkiaCanvas.cpp",
-        "SkiaCanvasProxy.cpp",
         "Snapshot.cpp",
         "Texture.cpp",
         "VectorDrawable.cpp",
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/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 5d380a6..b9af7de2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -74,7 +74,6 @@
 }  // namespace SaveFlags
 
 namespace uirenderer {
-class SkiaCanvasProxy;
 namespace VectorDrawable {
 class Tree;
 };
@@ -305,7 +304,6 @@
 
     friend class DrawTextFunctor;
     friend class DrawTextOnPathFunctor;
-    friend class uirenderer::SkiaCanvasProxy;
 };
 
 };  // namespace android
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 9a72706..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 {
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/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/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/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/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 22a699c..7f4544a 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -42,6 +42,7 @@
     </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>
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7788f7e..04f2411 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1120,4 +1120,5 @@
 
     <!-- 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/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 7611e52..6ab221e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -49,42 +49,27 @@
 
     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 BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
+    private final BroadcastReceiver mProfileBroadcastReceiver = new BluetoothBroadcastReceiver();
+    private final Collection<BluetoothCallback> mCallbacks = new ArrayList<>();
 
-    private final Collection<BluetoothCallback> mCallbacks =
-            new ArrayList<BluetoothCallback>();
-
+    private LocalBluetoothProfileManager mProfileManager;
     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 +94,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 +110,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 +132,98 @@
         }
     }
 
-    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);
+    }
+
+    // Set profile manager after construction due to circular dependency
+    void setProfileManager(LocalBluetoothProfileManager manager) {
+        mProfileManager = manager;
+    }
+
+    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;
+    }
+
+    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 +235,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)
             {
@@ -256,30 +309,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 +342,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 +
@@ -360,24 +389,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 +426,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 +436,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 +460,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 +472,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);
-    }
-}
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 6256922..f91dbcf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -51,8 +51,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
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/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index 3a03644..1cf83ac 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,11 +18,9 @@
 
 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;
@@ -31,9 +29,6 @@
 
 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.
      */
@@ -54,21 +49,14 @@
      */
     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() {
         // Empty
     }
 
+    DashboardCategory(Parcel in) {
+        readFromParcel(in);
+    }
+
     /**
      * Get a copy of the list of the category's children.
      *
@@ -107,22 +95,6 @@
         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.
      */
@@ -190,9 +162,6 @@
         }
     }
 
-    DashboardCategory(Parcel in) {
-        readFromParcel(in);
-    }
 
     public static final Creator<DashboardCategory> CREATOR = new Creator<DashboardCategory>() {
         public DashboardCategory createFromParcel(Parcel source) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index 0c802af..fe8303e 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,
@@ -17,44 +17,48 @@
 package com.android.settingslib.drawer;
 
 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 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.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.
      */
@@ -95,8 +99,15 @@
      */
     public String key;
 
-    public Tile() {
-        // Empty
+
+    private final String mActivityPackage;
+    private final String mActivityName;
+    private ActivityInfo mActivityInfo;
+
+    public Tile(ActivityInfo activityInfo) {
+        mActivityInfo = activityInfo;
+        mActivityPackage = mActivityInfo.packageName;
+        mActivityName = mActivityInfo.name;
     }
 
     @Override
@@ -106,14 +117,10 @@
 
     @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);
@@ -133,13 +140,39 @@
         dest.writeBoolean(isIconTintable);
     }
 
-    public void readFromParcel(Parcel in) {
+    /**
+     * Optional icon to show for this tile.
+     *
+     * @attr ref android.R.styleable#PreferenceHeader_icon
+     */
+    public Icon getIcon(Context context) {
+        if (context == null || metaData == null) {
+            return null;
+        }
+
+        int iconResId = metaData.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 (!metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
+                iconResId = getActivityInfo(context).icon;
+            }
+        }
+        if (iconResId != 0) {
+            return Icon.createWithResource(getActivityInfo(context).packageName, iconResId);
+        } else {
+            return null;
+        }
+    }
+
+    Tile(Parcel in) {
+        mActivityPackage = in.readString();
+        mActivityName = in.readString();
         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();
@@ -154,14 +187,24 @@
         isIconTintable = in.readBoolean();
     }
 
-    Tile(Parcel in) {
-        readFromParcel(in);
+    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];
         }
@@ -169,7 +212,7 @@
 
     public boolean isPrimaryProfileOnly() {
         String profile = metaData != null ?
-            metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+                metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
         profile = (profile != null ? profile : PROFILE_ALL);
         return TextUtils.equals(profile, PROFILE_PRIMARY);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 06f1456..11976d7 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;
@@ -303,7 +302,7 @@
             Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name);
             Tile tile = addedCache.get(key);
             if (tile == null) {
-                tile = new Tile();
+                tile = new Tile(activityInfo);
                 tile.intent = new Intent().setClassName(
                         activityInfo.packageName, activityInfo.name);
                 tile.category = categoryKey;
@@ -329,7 +328,6 @@
             boolean forceTintExternalIcon) {
         if (applicationInfo.isSystemApp()) {
             boolean forceTintIcon = false;
-            int icon = 0;
             CharSequence title = null;
             String summary = null;
             String keyHint = null;
@@ -347,9 +345,6 @@
                 }
 
                 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);
@@ -390,18 +385,6 @@
                 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;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 87f5b4f..eeaa987 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -38,13 +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 {
@@ -136,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;
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
index ac2d759..63f462c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
@@ -16,8 +16,16 @@
 
 package com.android.settingslib.drawer;
 
+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;
+
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -30,12 +38,6 @@
 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 {
@@ -58,7 +60,7 @@
 
     @Test
     public void testUpdateUserHandlesIfNeeded_Normal() {
-        final Tile tile = new Tile();
+        final Tile tile = new Tile(new ActivityInfo());
         tile.intent = new Intent();
         tile.userHandle.add(NORMAL_USER);
 
@@ -71,7 +73,7 @@
 
     @Test
     public void testUpdateUserHandlesIfNeeded_Remove() {
-        final Tile tile = new Tile();
+        final Tile tile = new Tile(new ActivityInfo());
         tile.intent = new Intent();
         tile.userHandle.add(REMOVED_USER);
         tile.userHandle.add(NORMAL_USER);
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/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..c904384 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";
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..77fb272 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
@@ -20,7 +20,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,20 +36,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;
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/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/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index 5d352f0..a9e5aae 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -1,28 +1,37 @@
 package com.android.settingslib.drawer;
 
-import static com.google.common.truth.Truth.assertThat;
-
 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.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 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() {
-        mTile = new Tile();
+        mActivityInfo = new ActivityInfo();
+        mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
+        mActivityInfo.icon = R.drawable.ic_plus;
+        mTile = new Tile(mActivityInfo);
         mTile.metaData = new Bundle();
     }
 
@@ -48,4 +57,34 @@
         mTile.metaData = null;
         assertThat(mTile.isPrimaryProfileOnly()).isFalse();
     }
+
+    @Test
+    public void getIcon_noContextOrMetadata_returnNull() {
+        final Tile tile = new Tile(new ActivityInfo());
+        assertThat(tile.getIcon(null)).isNull();
+        assertThat(tile.getIcon(RuntimeEnvironment.application)).isNull();
+    }
+
+    @Test
+    public void getIcon_providedByUri_returnNull() {
+        mTile.metaData.putString(META_DATA_PREFERENCE_ICON_URI, "content://foobar/icon");
+
+        assertThat(mTile.getIcon(RuntimeEnvironment.application)).isNull();
+    }
+
+    @Test
+    public void getIcon_hasIconMetadata_returnIcon() {
+        mTile.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() {
+        mTile.metaData.putInt(META_DATA_PREFERENCE_ICON, 0);
+
+        assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
+                .isEqualTo(mActivityInfo.icon);
+    }
 }
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 a1c48f0..5f4be21 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
@@ -314,7 +314,7 @@
                 false /* checkCategory */, true /* forceTintExternalIcon */);
 
         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.
@@ -332,7 +332,7 @@
                 false /* checkCategory */, true /* forceTintExternalIcon */);
 
         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");
     }
 
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
index ddadac1..fa64afe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -25,15 +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 org.robolectric.RobolectricTestRunner;
 
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class InputMethodAndSubtypeUtilCompatTest {
 
     private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
@@ -236,7 +237,7 @@
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
                 createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
                 .isFalse();
-   }
+    }
 
     private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
             InputMethodSubtype... subtypes) {
@@ -253,7 +254,7 @@
         si.exported = true;
         si.nonLocalizedLabel = "Dummy IME";
         ri.serviceInfo = si;
-        return new InputMethodInfo(ri, isAuxIme, "",  Arrays.asList(subtypes), 1, false);
+        return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
     }
 
     private static InputMethodSubtype createDummySubtype(
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/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/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 77eb6c4..10c1211 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);
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/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/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/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/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3f63f22..49ee952 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -143,5 +143,8 @@
     <attr name="rotateButtonEndAngle" format="float" />
     <attr name="rotateButtonScaleX" format="float" />
 
+    <!-- Used to style charging animation AVD animation -->
+    <attr name="chargingAnimColor" format="color" />
+
 </resources>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5a03c4a..c0b50ea 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -524,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/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index e11fe4e..747962f 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;
@@ -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..ec150873 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/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/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e8cd32b0..987de0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3287,8 +3287,20 @@
 
     private void generateViewResizeEvent() {
         if (mNeedViewResizeAnimation) {
-            mAnimationEvents.add(
-                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
+            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;
     }
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 22833b8..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;
@@ -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();
                 }
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 6077e79..b7bc577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -886,6 +886,10 @@
 
     public boolean isRotateButtonVisible() { return mShowRotateButton; }
 
+    void hideRecentsOnboarding() {
+        mRecentsOnboarding.hide(true);
+    }
+
     /**
      * @return the button at the given {@param x} and {@param y}.
      */
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 1dfa5b7..be8bf02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1360,9 +1360,7 @@
         mQsExpansionHeight = height;
         updateQsExpansion();
         requestScrollerTopPaddingUpdate(false /* animate */);
-        if (mKeyguardShowing) {
-            updateHeaderKeyguardAlpha();
-        }
+        updateHeaderKeyguardAlpha();
         if (mStatusBarState == StatusBarState.SHADE_LOCKED
                 || mStatusBarState == StatusBarState.KEYGUARD) {
             updateKeyguardBottomAreaAlpha();
@@ -1765,6 +1763,9 @@
     }
 
     private void updateHeaderKeyguardAlpha() {
+        if (!mKeyguardShowing) {
+            return;
+        }
         float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
         mKeyguardStatusBar.setAlpha(Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
                 * mKeyguardStatusBarAnimateAlpha);
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 2516a9b..6cc88bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -510,6 +510,9 @@
     private void resetQuickScrub() {
         mQuickScrubActive = false;
         mAllowGestureDetection = false;
+        if (mCurrentNavigationBarView != null) {
+            mCurrentNavigationBarView.setAlpha(1f);
+        }
         mCurrentNavigationBarView = null;
         updateHighlight();
     }
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 f729120..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) {
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/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 955939c..7855b47 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,7 @@
     private State mState;
     private SafetyWarningDialog mSafetyWarning;
     private boolean mHovering = false;
+    private boolean mShowActiveStreamOnly;
 
     public VolumeDialogImpl(Context context) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
@@ -156,6 +155,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 +193,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 +213,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 +229,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 +240,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 +428,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();
     }
 
@@ -587,29 +602,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 +744,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 +1056,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);
+            }
         }
     };
 
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/proto/src/wifi.proto b/proto/src/wifi.proto
index 15c0468..f7fcf5c 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -473,6 +473,9 @@
 
   // 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.
@@ -1615,3 +1618,31 @@
   // 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/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/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 021fdcd..0610256 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -166,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);
@@ -438,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);
@@ -475,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;
         }
@@ -493,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;
         }
@@ -1289,13 +1326,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/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/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/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8f59ca9..48c2eae 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -37,7 +37,6 @@
 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;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 5ccad66..ec27da9 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -80,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;
@@ -387,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;
     }
@@ -1480,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) {
@@ -1529,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
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/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 7f0030a..ae2a36b 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -47,7 +47,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 +63,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;
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/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index b9b0bcb..551b80f 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -468,7 +468,7 @@
 
             IBackupAgent agent = null;
             try {
-                backupManagerService.getWakelock().setWorkSource(
+                backupManagerService.setWorkSource(
                         new WorkSource(mCurrentPackage.applicationInfo.uid));
                 agent = backupManagerService.bindToAgentSynchronous(mCurrentPackage.applicationInfo,
                         ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
@@ -494,7 +494,7 @@
             backupManagerService.addBackupTrace("no such package");
             mStatus = BackupTransport.AGENT_UNKNOWN;
         } finally {
-            backupManagerService.getWakelock().setWorkSource(null);
+            backupManagerService.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.
@@ -1202,10 +1202,7 @@
         // 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 */ }
+            backupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
         }
     }
 
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 3d779d8..c63b8f4 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -27,17 +27,22 @@
 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 com.android.server.LocalServices;
 
 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 {
 
@@ -53,15 +58,18 @@
         private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
         private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
 
+        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) {
+        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();
         }
@@ -79,20 +87,45 @@
               return;
             }
 
-            BinderCallsStats stats = BinderCallsStats.getInstance();
             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);
             }
-            stats.setEnabled(
-                    mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT));
-            stats.setDetailedTracking(mParser.getBoolean(
+            mBinderCallsStats.setDetailedTracking(mParser.getBoolean(
                     SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT));
-            stats.setSamplingInterval(mParser.getInt(
+            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();
+            }
+        }
+    }
+
+    /**
+     * @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();
         }
     }
 
@@ -105,7 +138,9 @@
 
         @Override
         public void onStart() {
-            mService = new BinderCallsStatsService();
+            BinderCallsStats binderCallsStats = new BinderCallsStats(new Random());
+            mService = new BinderCallsStatsService(binderCallsStats);
+            LocalServices.addService(Internal.class, new Internal(binderCallsStats));
             publishBinderService("binder_calls_stats", mService);
             boolean detailedTrackingEnabled = SystemProperties.getBoolean(
                     PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
@@ -114,7 +149,7 @@
                 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);
+                binderCallsStats.setDetailedTracking(true);
             }
         }
 
@@ -127,14 +162,19 @@
     }
 
     private SettingsObserver mSettingsObserver;
+    private final BinderCallsStats mBinderCallsStats;
 
-    public void systemReady(Context context) {
-        mSettingsObserver = new SettingsObserver(context);
+    BinderCallsStatsService(BinderCallsStats binderCallsStats) {
+        mBinderCallsStats = binderCallsStats;
     }
 
-    public static void reset() {
+    public void systemReady(Context context) {
+        mSettingsObserver = new SettingsObserver(context, mBinderCallsStats);
+    }
+
+    public void reset() {
         Slog.i(TAG, "Resetting stats");
-        BinderCallsStats.getInstance().reset();
+        mBinderCallsStats.reset();
     }
 
     @Override
@@ -150,12 +190,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)) {
@@ -169,7 +209,7 @@
                 }
             }
         }
-        BinderCallsStats.getInstance().dump(pw, getAppIdToPackagesMap(), verbose);
+        mBinderCallsStats.dump(pw, getAppIdToPackagesMap(), verbose);
     }
 
     private Map<Integer, String> getAppIdToPackagesMap() {
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/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 8402087..f15cd2ad 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/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index f5b29e9..a05a3e7 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -69,6 +69,7 @@
 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;
@@ -345,31 +346,76 @@
     }
 
     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);
-        return getApplicationInfoForIntent(cameraIntent, userHandle);
+        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);
+        return getApplicationInfoForIntent(intent, userHandle, false);
     }
 
-    private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle) {
+    private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle,
+            boolean defaultToSystemApp) {
         if (intent == null) {
             return null;
         }
-        ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(intent,
+
+        ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(intent,
                 MATCH_FLAGS, userHandle);
-        if (info == null) {
+
+        // 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;
         }
-        if (isResolverActivity(info.activityInfo)) {
-            return null;
+
+        if (!isResolverActivity(resolveInfo.activityInfo)) {
+            return resolveInfo.activityInfo.applicationInfo;
         }
-        return info.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) {
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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 286e1f0..29937ab 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -65,7 +65,6 @@
 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;
@@ -96,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;
@@ -512,7 +512,7 @@
             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,
@@ -2578,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,
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 552ed16..acdc738 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -421,11 +421,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="
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b5ff8c1..0cf101c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -91,13 +91,9 @@
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.view.Display.DEFAULT_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;
@@ -122,7 +118,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
 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_WHITELISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
@@ -148,8 +143,6 @@
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -172,7 +165,6 @@
 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;
@@ -199,7 +191,6 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
-import android.content.ClipData;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -248,7 +239,6 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.DropBoxManager;
-import android.os.Environment;
 import android.os.FactoryTest;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -281,14 +271,12 @@
 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.text.TextUtils;
 import android.text.format.DateUtils;
 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;
@@ -301,7 +289,6 @@
 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;
@@ -311,8 +298,8 @@
 import android.view.WindowManager;
 import android.view.autofill.AutofillManagerInternal;
 
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
+import com.android.server.uri.GrantUri;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -337,7 +324,6 @@
 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;
@@ -345,7 +331,6 @@
 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;
@@ -372,14 +357,9 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerService;
 
-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;
@@ -389,7 +369,6 @@
 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;
@@ -412,7 +391,6 @@
 import java.util.function.BiFunction;
 
 import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
 public class ActivityManagerService extends IActivityManager.Stub
@@ -495,9 +473,6 @@
     // Must be kept in sync with Am.
     private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
 
-    // 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];
@@ -956,95 +931,7 @@
      * 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;
 
@@ -1492,6 +1379,7 @@
     WindowManagerService mWindowManager;
     ActivityTaskManagerService mActivityTaskManager;
     ActivityTaskManagerInternal mAtmInternal;
+    UriGrantsManagerInternal mUgmInternal;
     final ActivityThread mSystemThread;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1541,7 +1429,6 @@
     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 PERSIST_URI_GRANTS_MSG = 38;
     static final int UPDATE_TIME_PREFERENCE_MSG = 41;
     static final int FINISH_BOOTING_MSG = 45;
     static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
@@ -1953,10 +1840,6 @@
                 thread.start();
                 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.
@@ -2555,7 +2438,6 @@
         mBatteryStatsService = null;
         mCompatModePackages = null;
         mConstants = null;
-        mGrantFile = null;
         mHandler = null;
         mHandlerThread = null;
         mIntentFirewall = null;
@@ -2616,9 +2498,7 @@
         mProviderMap = new ProviderMap(this);
         mAppErrors = new AppErrors(mUiContext, this);
 
-        File dataDir = Environment.getDataDirectory();
-        File systemDir = new File(dataDir, "system");
-        systemDir.mkdirs();
+        final File systemDir = SystemServiceManager.ensureSystemDir();
 
         mAppWarnings = new AppWarnings(this, mUiContext, mHandler, mUiHandler, systemDir);
 
@@ -2635,7 +2515,7 @@
 
         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);
 
@@ -2724,6 +2604,7 @@
         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 access to mProcessCpuTracker from main thread
         // will be blocked during mProcessCpuTracker initialization.
@@ -2924,7 +2805,6 @@
 
     @Override
     public void batteryStatsReset() {
-        BinderCallsStatsService.reset();
         mOomAdjProfiler.reset();
     }
 
@@ -5028,11 +4908,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();
@@ -5726,7 +5604,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--) {
@@ -7217,105 +7095,6 @@
         throw new SecurityException(msg);
     }
 
-    /**
-     * 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)
@@ -7489,67 +7268,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.
@@ -7571,395 +7289,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;
     }
 
     /**
@@ -7990,113 +7321,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.
@@ -8130,497 +7359,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();
-        }
-    }
-
-    /**
-     * @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) {
@@ -8942,7 +7685,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;
@@ -8988,7 +7732,8 @@
                 }
             }
         }
-        if (!checkedGrants && checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
+        if (!checkedGrants
+                && mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
             return null;
         }
 
@@ -9007,41 +7752,6 @@
         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, int callingUid,
             String callingTag, boolean stable) {
@@ -10177,6 +8887,7 @@
                 abiOverride);
     }
 
+    @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             boolean disableHiddenApiChecks, String abiOverride) {
         ProcessRecord app;
@@ -11522,9 +10233,7 @@
 
         retrieveSettings();
         final int currentUserId = mUserController.getCurrentUserId();
-        synchronized (this) {
-            readGrantedUriPermissionsLocked();
-        }
+        mUgmInternal.onSystemReady();
 
         final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
         if (pmi != null) {
@@ -11869,6 +10578,15 @@
         StatsLog.write(StatsLog.WTF_OCCURRED, callingUid, tag, processName,
                 callingPid);
 
+        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("wtf", r, processName, null, null, tag, null, null, crashInfo);
 
         return r;
@@ -12023,6 +10741,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
@@ -12155,6 +10876,7 @@
         return imp;
     }
 
+    @GuardedBy("this")
     private void fillInProcMemInfoLocked(ProcessRecord app,
             ActivityManager.RunningAppProcessInfo outInfo,
             int clientTargetSdk) {
@@ -12405,6 +11127,11 @@
                 pw.println("-------------------------------------------------------------------------------");
             }
             mOomAdjProfiler.dump(pw);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpBinderProxies(pw);
         }
     }
 
@@ -12566,10 +11293,7 @@
                 }
             } else if ("binder-proxies".equals(cmd)) {
                 if (opti >= args.length) {
-                    dumpBinderProxyInterfaceCounts(pw,
-                            "Top proxy interface names held by SYSTEM");
-                    dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(),
-                            "Number of proxies per uid held by SYSTEM");
+                    dumpBinderProxies(pw);
                 } else {
                     String uid = args[opti];
                     opti++;
@@ -12964,7 +11688,8 @@
         }
     }
 
-    boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) {
+    boolean dumpBinderProxiesCounts(PrintWriter pw, String header) {
+        SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
         if(counts != null) {
             pw.println(header);
             for (int i = 0; i < counts.size(); i++) {
@@ -12992,6 +11717,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) {
@@ -14287,48 +13019,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,
@@ -17725,8 +16419,8 @@
                                                 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);
 
                                         mActivityTaskManager.getRecentTasks().removeTasksByPackageName(ssp, userId);
 
@@ -21603,6 +20297,7 @@
         }
     }
 
+    @GuardedBy("this")
     final void trimApplicationsLocked() {
         // First remove any unused application processes whose package
         // has been removed.
@@ -22123,15 +20818,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);
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b17aada..19ea955 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -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;
@@ -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.
@@ -1376,7 +1388,7 @@
 
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
-            uriPermissions = new UriPermissionOwner(service.mAm, this);
+            uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
         }
         return uriPermissions;
     }
@@ -1428,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.mAm.grantUriPermissionFromIntentLocked(callingUid, packageName,
+        service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
                 intent, getUriPermissionsLocked(), userId);
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
@@ -1588,7 +1600,7 @@
 
     void removeUriPermissionsLocked() {
         if (uriPermissions != null) {
-            uriPermissions.removeUriPermissionsLocked();
+            uriPermissions.removeUriPermissions();
             uriPermissions = null;
         }
     }
@@ -2616,6 +2628,15 @@
             startFreezingScreenLocked(app, globalChanges);
             forceNewConfig = false;
             preserveWindow &= isResizeOnlyChange(changes);
+            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);
@@ -2737,6 +2758,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;
@@ -3016,6 +3042,17 @@
         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) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d08784f..058c714 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;
@@ -3368,7 +3370,7 @@
             String resultWho, int requestCode, int resultCode, Intent data) {
 
         if (callingUid > 0) {
-            mService.mAm.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
                     data, r.getUriPermissionsLocked(), r.userId);
         }
 
@@ -3682,7 +3684,7 @@
                 }
             }
             if (r.info.applicationInfo.uid > 0) {
-                mService.mAm.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+                mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
                         resultTo.packageName, resultData,
                         resultTo.getUriPermissionsLocked(), resultTo.userId);
             }
@@ -4483,7 +4485,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;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4e39033..a732575 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -74,6 +74,7 @@
 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;
@@ -2100,6 +2101,10 @@
             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()) {
@@ -2502,8 +2507,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;
                 }
@@ -2548,12 +2552,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();
         }
 
@@ -2575,7 +2574,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(
@@ -2597,7 +2597,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);
@@ -2698,7 +2700,8 @@
             if (displayId == currentFocus) {
                 continue;
             }
-            final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r);
+            final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r,
+                    null /* options */);
             if (stack != null) {
                 return stack;
             }
@@ -3183,7 +3186,7 @@
 
                 if (!proc.shouldKillProcessForRemovedTask(tr)) {
                     // Don't kill process(es) that has an activity in a different task that is also
-                    // in recents.
+                    // in recents, or has an activity not stopped.
                     return;
                 }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 4378ccb..8be5ada 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1409,7 +1409,7 @@
             return result;
         }
 
-        mService.mAm.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
+        mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
         mService.mAm.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
                 mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
@@ -2103,7 +2103,7 @@
             if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
                 // Can't use target display, lets find a stack on the source display.
                 mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
-                        sourceStack.mDisplayId, mStartActivity);
+                        sourceStack.mDisplayId, mStartActivity, mOptions);
             }
             if (mTargetStack == null) {
                 // There are no suitable stacks on the target and source display(s). Look on all
@@ -2326,7 +2326,7 @@
 
         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,
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index de732c7..ab1ba6c 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -135,6 +135,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.server.AppOpsService;
 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;
@@ -259,6 +260,7 @@
     UiHandler mUiHandler;
     ActivityManagerService mAm;
     ActivityManagerInternal mAmInternal;
+    UriGrantsManagerInternal mUgmInternal;
     /* Global service lock used by the package the owns this service. */
     Object mGlobalLock;
     ActivityStackSupervisor mStackSupervisor;
@@ -612,6 +614,7 @@
 
     void onActivityManagerInternalAdded() {
         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
     }
 
     protected ActivityStackSupervisor createStackSupervisor() {
@@ -1192,6 +1195,8 @@
                     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);
@@ -3613,7 +3618,7 @@
                 throw new IllegalArgumentException("Activity does not exist; token="
                         + activityToken);
             }
-            return r.getUriPermissionsLocked().getExternalTokenLocked();
+            return r.getUriPermissionsLocked().getExternalToken();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 27567a7..7652dd4 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -410,6 +410,10 @@
             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) {
@@ -422,6 +426,12 @@
                 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}.
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 1967c76..230810b 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;
@@ -497,7 +499,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);
             }
         }
     }
@@ -589,8 +591,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;
             }
@@ -636,8 +637,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 {
@@ -702,8 +702,7 @@
             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;
@@ -735,11 +734,21 @@
      */
     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.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);
 
@@ -790,7 +799,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
@@ -827,7 +836,7 @@
                 res.add(rti);
             }
         }
-        return new ParceledListSlice<>(res);
+        return res;
     }
 
     /**
@@ -1159,9 +1168,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;
                 }
         }
@@ -1193,8 +1201,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;
@@ -1512,6 +1520,7 @@
             return;
         }
 
+        // Dump raw recent task list
         boolean printedAnything = false;
         boolean printedHeader = false;
         final int size = mTasks.size();
@@ -1534,6 +1543,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)");
         }
@@ -1543,33 +1576,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/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/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8f43620..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;
             }
         }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 11684af..2d0c2a8 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -88,6 +88,7 @@
 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;
@@ -1939,6 +1940,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);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fa670a2..ba604e0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -269,6 +269,7 @@
         });
     }
 
+    @GuardedBy("mLock")
     List<Integer> getRunningUsersLU() {
         ArrayList<Integer> runningUsers = new ArrayList<>();
         for (Integer userId : mUserLru) {
@@ -293,6 +294,7 @@
         return runningUsers;
     }
 
+    @GuardedBy("mLock")
     void stopRunningUsersLU(int maxRunningUsers) {
         List<Integer> currentlyRunning = getRunningUsersLU();
         Iterator<Integer> iterator = currentlyRunning.iterator();
@@ -595,6 +597,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;
@@ -626,6 +629,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);
@@ -783,6 +787,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();
@@ -1368,6 +1373,7 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
+    @GuardedBy("mLock")
     void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
         mCurWaitingUserSwitchCallbacks = null;
         mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
@@ -1581,6 +1587,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void updateStartedUserArrayLU() {
         int num = 0;
         for (int i = 0; i < mStartedUsers.size(); i++) {
@@ -1719,6 +1726,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     UserInfo getCurrentUserLU() {
         int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
         return getUserInfo(userId);
@@ -1730,11 +1738,13 @@
         }
     }
 
+    @GuardedBy("mLock")
     int getCurrentOrTargetUserIdLU() {
         return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
     }
 
 
+    @GuardedBy("mLock")
     int getCurrentUserIdLU() {
         return mCurrentUserId;
     }
@@ -1745,6 +1755,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private boolean isCurrentUserLU(int userId) {
         return userId == getCurrentOrTargetUserIdLU();
     }
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index 817905a..d4c9bcb 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -334,7 +334,12 @@
 
     boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
         for (int k = 0; k < mActivities.size(); k++) {
-            final TaskRecord otherTask = mActivities.get(k).getTask();
+            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.
@@ -431,6 +436,19 @@
         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.
diff --git a/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
index 6e5858a..02cc6d5 100644
--- a/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
@@ -153,11 +153,12 @@
     }
 
     @Override
-    public boolean onAuthenticated(int fingerId, int groupId) {
+    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated) {
         boolean result = false;
-        boolean authenticated = fingerId != 0;
 
         // If the fingerprint dialog is showing, notify authentication succeeded
+        // TODO: this goes to BiometricPrompt, split between biometric modalities
         if (mBundle != null) {
             try {
                 if (authenticated) {
@@ -180,12 +181,18 @@
                 } else {
                     if (DEBUG) {
                         Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
-                                + ", id=" + fingerId + ", gp=" + groupId + ")");
+                                + ", id=" + identifier.getBiometricId());
                     }
-                    Fingerprint fp = !getIsRestricted()
-                            ? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
-                            : null;
-                    listener.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
+
+                    // 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);
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricService.java b/services/core/java/com/android/server/biometrics/common/BiometricService.java
index 41b1575..f54baef 100644
--- a/services/core/java/com/android/server/biometrics/common/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/common/BiometricService.java
@@ -577,20 +577,22 @@
         }
     }
 
-    protected void handleAuthenticated(long deviceId, int biometricId, int groupId,
+    protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
             ArrayList<Byte> token) {
         ClientMonitor client = mCurrentClient;
-        if (biometricId != 0) {
+        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(biometricId, groupId)) {
+        if (client != null && client.onAuthenticated(identifier, authenticated)) {
             removeClient(client);
         }
-        if (biometricId != 0) {
+        if (authenticated) {
             mPerformanceStats.accept++;
         } else {
             mPerformanceStats.reject++;
diff --git a/services/core/java/com/android/server/biometrics/common/ClientMonitor.java b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
index 699fc32..1486754 100644
--- a/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
@@ -127,7 +127,8 @@
     // 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(int biometricId, int groupId);
+    public abstract boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated);
     public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier,
             int remaining);
     public abstract boolean onEnumerationResult(
diff --git a/services/core/java/com/android/server/biometrics/common/EnrollClient.java b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
index 5744fdb..aee772b 100644
--- a/services/core/java/com/android/server/biometrics/common/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
@@ -125,7 +125,8 @@
     }
 
     @Override
-    public boolean onAuthenticated(int biometricId, int groupId) {
+    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
index e51c1c6..ee40ee9 100644
--- a/services/core/java/com/android/server/biometrics/common/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/common/EnumerateClient.java
@@ -93,7 +93,8 @@
     }
 
     @Override
-    public boolean onAuthenticated(int biometricId, int groupId) {
+    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated) {
         if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enumerate!");
         return true; // Invalid for Enumerate.
     }
diff --git a/services/core/java/com/android/server/biometrics/common/RemovalClient.java b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
index 23d5539..27c42ab 100644
--- a/services/core/java/com/android/server/biometrics/common/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
@@ -110,7 +110,8 @@
     }
 
     @Override
-    public boolean onAuthenticated(int biometricId, int groupId) {
+    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated) {
         if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for remove!");
         return true; // Invalid for Remove.
     }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 35679a88..f8ccef5 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -400,7 +400,8 @@
                 public void onAuthenticated(final long deviceId, final int faceId, final int userId,
                         ArrayList<Byte> token) {
                     mHandler.post(() -> {
-                        FaceService.super.handleAuthenticated(deviceId, faceId, userId, token);
+                        Face face = new Face("", faceId, deviceId);
+                        FaceService.super.handleAuthenticated(face, token);
                     });
                 }
 
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
index e12498e..c438bfb 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceUserState.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
@@ -85,7 +85,7 @@
         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.getFaceId(), f.getDeviceId()));
+            result.add(new Face(f.getName(), f.getBiometricId(), f.getDeviceId()));
         }
         return result;
     }
@@ -114,7 +114,7 @@
             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.getFaceId()));
+                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);
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 7004e1b..64b248e 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -528,7 +528,8 @@
         public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
                 ArrayList<Byte> token) {
             mHandler.post(() -> {
-                FingerprintService.super.handleAuthenticated(deviceId, fingerId, groupId, token);
+                Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+                FingerprintService.super.handleAuthenticated(fp, token);
             });
         }
 
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/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/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 2918a19..ba5ee02 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -18,11 +18,12 @@
 
 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 {
 
@@ -180,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;
@@ -191,11 +232,11 @@
 
     // 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;
 
@@ -209,6 +250,7 @@
         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;
@@ -237,11 +279,11 @@
     // 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.
-     * Default is always turn on.
-     * State must be a valid {@link SystemAudioControlOnPowerOn} int.
+     *
+     * <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";
+            "persist.sys.hdmi.system_audio_control_on_power_on";
 
     /**
      * Property to record last state of system audio control before device powered off.
@@ -250,7 +292,7 @@
      * <p>State must be true or false. Default true.
      */
     static final String PROPERTY_LAST_SYSTEM_AUDIO_CONTROL =
-        "persist.sys.hdmi.last_system_audio_control";
+            "persist.sys.hdmi.last_system_audio_control";
 
     /**
      * Property to save the audio port to switch to when system audio control is on.
@@ -293,5 +335,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/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index b796abd..7aab750 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -21,6 +21,7 @@
 
 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;
@@ -43,10 +44,13 @@
     @GuardedBy("mLock")
     private boolean mSystemAudioControlFeatureEnabled;
 
-    protected Integer mSystemAudioSource;
-
     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;
@@ -232,7 +236,6 @@
             return true;
         }
 
-        mSystemAudioSource = systemAudioStatusOn ? message.getSource() : null;
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(
                         mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
@@ -259,6 +262,39 @@
         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();
 
@@ -287,7 +323,11 @@
         // Wake up device if System Audio Control is turned on but device is still on standby
         if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
             mService.wakeUp();
-            // TODO(amyjojo): Switch to the corresponding input
+        }
+        int targetPhysicalAddress = getActiveSource().physicalAddress;
+        if (newSystemAudioMode &&
+            !isPhysicalAddressMeOrBelow(targetPhysicalAddress)) {
+            switchToAudioInput();
         }
         // Mute device when feature is turned off and unmute device when feature is turned on
         boolean currentMuteStatus =
@@ -311,6 +351,32 @@
         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
+    }
+
     private void updateAudioManagerForSystemAudio(boolean on) {
         int device = mService.getAudioManager().setHdmiSystemAudioSupported(on);
         HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 71ba03a..a2eb1c1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1732,7 +1732,7 @@
                         Slog.w(TAG, "audio system is not available");
                         return;
                     }
-                    if (audioSystem().mSystemAudioSource == null) {
+                    if (!audioSystem().isSystemAudioActivated()) {
                         Slog.w(TAG, "audio system is not in system audio mode");
                         return;
                     }
@@ -1741,7 +1741,7 @@
                     sendCecCommand(HdmiCecMessageBuilder
                             .buildReportAudioStatus(
                                     device.getDeviceInfo().getLogicalAddress(),
-                                    audioSystem().mSystemAudioSource,
+                                    Constants.ADDR_TV,
                                     scaledVolume,
                                     isMute));
                 }
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/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index a49cf44..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() {
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 507d0c4..5a0578f 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;
@@ -1365,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,
@@ -1374,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);
@@ -1528,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();
@@ -5620,12 +5630,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
@@ -5633,11 +5639,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);
             }
@@ -5677,7 +5681,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)),
@@ -5694,12 +5698,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);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0154c72..f32b338 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;
@@ -96,6 +97,7 @@
     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;
@@ -182,6 +184,7 @@
         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();
@@ -1107,7 +1110,7 @@
         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)));
@@ -1116,8 +1119,6 @@
                 mGrantableUris = new ArraySet<>();
             }
             mGrantableUris.add(uri);
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } catch (SecurityException e) {
             if (!userOverriddenUri) {
                 if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b92d52c..d305032 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -947,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 666111b..ac33454 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1388,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;
@@ -2516,7 +2517,7 @@
                 }
             }
 
-            if (mFirstBoot) {
+            if (!mOnlyCore && mFirstBoot) {
                 requestCopyPreoptedFiles();
             }
 
@@ -3240,6 +3241,7 @@
                 mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
                         PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
                         SharedLibraryInfo.VERSION_UNDEFINED);
+                mRequiredPermissionControllerPackage = getRequiredPermissionsControllerLPr();
             } else {
                 mRequiredVerifierPackage = null;
                 mRequiredInstallerPackage = null;
@@ -3248,6 +3250,7 @@
                 mIntentFilterVerifier = null;
                 mServicesSystemSharedLibraryPackageName = null;
                 mSharedSystemSharedLibraryPackageName = null;
+                mRequiredPermissionControllerPackage = null;
             }
 
             mInstallerService = new PackageInstallerService(context, this);
@@ -3595,6 +3598,25 @@
         return resolveInfo.getComponentInfo().packageName;
     }
 
+    private @NonNull String getRequiredPermissionsControllerLPr() {
+        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);
 
@@ -5401,6 +5423,12 @@
     @Override
     public String getPermissionControllerPackageName() {
         synchronized (mPackages) {
+            return mRequiredPermissionControllerPackage;
+        }
+    }
+
+    String getPackageInstallerPackageName() {
+        synchronized (mPackages) {
             return mRequiredInstallerPackage;
         }
     }
@@ -14513,6 +14541,12 @@
             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 package \"" + packageName
                     + "\": protected package");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b6222bb..8ef1196 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4669,7 +4669,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);
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 45ce36b..0060328 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -525,7 +525,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.
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2175772..d6ccf3b 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -63,7 +63,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
-import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelUidCpuTimeReader;
@@ -74,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;
 
@@ -87,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;
 
@@ -895,10 +896,16 @@
     }
 
     private void pullBinderCallsStats(int tagId, List<StatsLogEventWrapper> pulledData) {
-        List<ExportedCallStat> callStats = BinderCallsStats.getInstance().getExportedCallStats();
+        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, 11 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 13 /* fields */);
             e.writeInt(callStat.uid);
             e.writeString(callStat.className);
             e.writeString(callStat.methodName);
@@ -910,6 +917,25 @@
             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);
         }
     }
@@ -1000,6 +1026,10 @@
                 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;
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..1b5ffda
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -0,0 +1,1472 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+            }
+        }
+
+        // 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 (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 -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
+                && 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/am/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java
similarity index 98%
rename from services/core/java/com/android/server/am/UriPermission.java
rename to services/core/java/com/android/server/uri/UriPermission.java
index 1e071aa..bd6348a 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/uri/UriPermission.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 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.
@@ -11,10 +11,10 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.server.am;
+package com.android.server.uri;
 
 import android.app.GrantedUriPermission;
 import android.content.Intent;
@@ -24,7 +24,6 @@
 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;
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/uri/UriPermissionOwner.java
similarity index 75%
rename from services/core/java/com/android/server/am/UriPermissionOwner.java
rename to services/core/java/com/android/server/uri/UriPermissionOwner.java
index 8eda38e..f814dc6 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/uri/UriPermissionOwner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
@@ -11,27 +11,30 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
-package com.android.server.am;
+package com.android.server.uri;
 
-import android.content.Intent;
+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;
 
-final class UriPermissionOwner {
-    final ActivityManagerService service;
-    final Object owner;
+public class UriPermissionOwner {
+    private final UriGrantsManagerInternal mService;
+    private final Object mOwner;
 
-    Binder externalToken;
+    private Binder externalToken;
 
     private ArraySet<UriPermission> mReadPerms;
     private ArraySet<UriPermission> mWritePerms;
@@ -42,12 +45,12 @@
         }
     }
 
-    UriPermissionOwner(ActivityManagerService service, Object owner) {
-        this.service = service;
-        this.owner = owner;
+    public UriPermissionOwner(UriGrantsManagerInternal service, Object owner) {
+        mService = service;
+        mOwner = owner;
     }
 
-    Binder getExternalTokenLocked() {
+    public Binder getExternalToken() {
         if (externalToken == null) {
             externalToken = new ExternalToken();
         }
@@ -61,24 +64,22 @@
         return null;
     }
 
-    void removeUriPermissionsLocked() {
-        removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    public void removeUriPermissions() {
+        removeUriPermissions(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
     }
 
-    void removeUriPermissionsLocked(int mode) {
-        removeUriPermissionLocked(null, mode);
+    void removeUriPermissions(int mode) {
+        removeUriPermission(null, mode);
     }
 
-    void removeUriPermissionLocked(ActivityManagerService.GrantUri grantUri, int mode) {
-        if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
-                && mReadPerms != null) {
+    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);
-                    service.removeUriPermissionIfNeededLocked(perm);
+                    mService.removeUriPermissionIfNeeded(perm);
                     it.remove();
                 }
             }
@@ -86,14 +87,14 @@
                 mReadPerms = null;
             }
         }
-        if ((mode & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+        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);
-                    service.removeUriPermissionIfNeededLocked(perm);
+                    mService.removeUriPermissionIfNeeded(perm);
                     it.remove();
                 }
             }
@@ -142,7 +143,7 @@
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(UriPermissionOwnerProto.OWNER, owner.toString());
+        proto.write(UriPermissionOwnerProto.OWNER, mOwner.toString());
         if (mReadPerms != null) {
             synchronized (mReadPerms) {
                 for (UriPermission p : mReadPerms) {
@@ -162,6 +163,6 @@
 
     @Override
     public String toString() {
-        return owner.toString();
+        return mOwner.toString();
     }
 }
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 d9ddf9f..4b8b6074 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -449,13 +449,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*/);
             }
         }
 
@@ -677,7 +677,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) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 31c0bdd..cea5f4c6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -401,6 +401,8 @@
 
     private MagnificationSpec mMagnificationSpec;
 
+    private InputMonitor mInputMonitor;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final AppWindowToken atoken = w.mAppToken;
@@ -798,6 +800,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() {
@@ -2362,6 +2366,8 @@
 
         pw.println();
         mDisplayFrames.dump(prefix, pw);
+        pw.println();
+        mInputMonitor.dump(pw, "  ");
     }
 
     @Override
@@ -2472,9 +2478,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 */
@@ -3057,10 +3063,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);
@@ -3387,11 +3393,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) {
@@ -4107,4 +4118,8 @@
     private boolean canUpdateImeTarget() {
         return mDeferUpdateImeTargetCount == 0;
     }
+
+    InputMonitor getInputMonitor() {
+        return mInputMonitor;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 51d5eea..20a13334 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.net.Uri;
 import android.os.Binder;
@@ -25,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;
 
@@ -72,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);
             }
@@ -86,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);
 
@@ -117,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/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index d4046e9..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.
@@ -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
index 546edaa..ad745a2 100644
--- a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
@@ -68,11 +68,13 @@
     public void finish(WindowToken token, WindowState win) {
         mTransform.reset();
         token.getPendingTransaction().setMatrix(token.mSurfaceControl, mTransform, mFloat9);
-        token.getPendingTransaction().deferTransactionUntil(token.mSurfaceControl,
-                win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
-                win.getFrameNumber());
-        win.getPendingTransaction().deferTransactionUntil(win.mSurfaceControl,
-                win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
-                win.getFrameNumber());
+        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());
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index d40db8c..6a08f4d 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -40,7 +40,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 +63,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 +127,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..016921d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -0,0 +1,278 @@
+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) {
+            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
+    }
+
+    /** 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 8ab4651..d53a21b 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,13 +31,10 @@
 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;
@@ -47,11 +42,8 @@
 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;
 
@@ -60,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;
 
@@ -85,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
@@ -113,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);
@@ -130,8 +105,9 @@
         }
     }
 
-    public InputMonitor(WindowManagerService service) {
+    public InputMonitor(WindowManagerService service, int displayId) {
         mService = service;
+        mDisplayId = displayId;
     }
 
     private void addInputConsumer(String name, InputConsumerImpl consumer) {
@@ -156,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) {
@@ -170,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;
     }
@@ -183,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;
@@ -201,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) {
@@ -325,6 +207,7 @@
         inputWindowHandle.ownerPid = child.mSession.mPid;
         inputWindowHandle.ownerUid = child.mSession.mUid;
         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
+        inputWindowHandle.displayId = child.getDisplayId();
 
         final Rect frame = child.getFrameLw();
         inputWindowHandle.frameLeft = frame.left;
@@ -414,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.
      */
@@ -555,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:");
@@ -611,40 +368,46 @@
     }
 
     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.
+            // TODO: Update Input windows and focus by display?
             mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle);
 
             clearInputWindowHandlesLw();
@@ -674,7 +437,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 6f5fea9..3a0cc28 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -53,6 +53,7 @@
 
 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;
 
@@ -201,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 {
@@ -438,8 +441,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
@@ -497,7 +502,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
@@ -505,8 +510,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 2be4001..3df4eb7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -724,7 +724,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) {
@@ -784,7 +786,9 @@
         }
 
         if (updateInputWindowsNeeded) {
-            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+            forAllDisplays(dc -> {
+                dc.getInputMonitor().updateInputWindowsLw(false /*force*/);
+            });
         }
         mService.setFocusTaskRegionLocked(null);
         if (touchExcludeRegionUpdateDisplays != null) {
@@ -1091,4 +1095,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/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index bd7e61c..8effc6b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -209,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) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 7d36650..9cdc6b7 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -38,7 +38,6 @@
 class TaskPositioningController {
     private final WindowManagerService mService;
     private final InputManagerService mInputManager;
-    private final InputMonitor mInputMonitor;
     private final IActivityTaskManager mActivityManager;
     private final Handler mHandler;
 
@@ -54,9 +53,8 @@
     }
 
     TaskPositioningController(WindowManagerService service, InputManagerService inputManager,
-            InputMonitor inputMonitor, IActivityTaskManager activityManager, Looper looper) {
+            IActivityTaskManager activityManager, Looper looper) {
         mService = service;
-        mInputMonitor = inputMonitor;
         mInputManager = inputManager;
         mActivityManager = activityManager;
         mHandler = new Handler(looper);
@@ -129,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
@@ -145,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;
         }
 
@@ -161,7 +159,6 @@
                 if (mTaskPositioner != null) {
                     mTaskPositioner.unregister();
                     mTaskPositioner = null;
-                    mInputMonitor.updateInputWindowsLw(true /*force*/);
                 }
             }
         });
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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 06a1968..9b792e3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -269,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 {
@@ -1073,7 +1075,7 @@
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
 
         mTaskPositioningController = new TaskPositioningController(
-                this, mInputManager, mInputMonitor, mActivityTaskManager, mH.getLooper());
+                this, mInputManager, mActivityTaskManager, mH.getLooper());
         mDragDropController = new DragDropController(this, mH.getLooper());
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
@@ -1099,9 +1101,8 @@
         showEmulatorDisplayOverlayIfNeeded();
     }
 
-
-    public InputMonitor getInputMonitor() {
-        return mInputMonitor;
+    public InputManagerCallback getInputManagerCallback() {
+        return mInputManagerCallback;
     }
 
     @Override
@@ -1489,7 +1490,7 @@
                 res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
             }
 
-            mInputMonitor.setUpdateInputWindowsNeededLw();
+            displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
 
             boolean focusChanged = false;
             if (win.canReceiveKeys()) {
@@ -1509,9 +1510,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));
@@ -1724,7 +1726,7 @@
             }
         }
 
-        mInputMonitor.updateInputWindowsLw(true /*force*/);
+        dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
     }
 
     void setInputMethodWindowLocked(WindowState win) {
@@ -2043,7 +2045,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() + ")",
@@ -2377,7 +2379,7 @@
                     return;
                 }
 
-                mInputMonitor.updateInputWindowsLw(true /*force*/);
+                dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2566,7 +2568,7 @@
             if (changed) {
                 AppWindowToken prev = mFocusedApp;
                 mFocusedApp = newFocus;
-                mInputMonitor.setFocusedAppLw(newFocus);
+                mFocusedApp.getDisplayContent().getInputMonitor().setFocusedAppLw(newFocus);
                 setFocusTaskRegionLocked(prev);
             }
 
@@ -3474,7 +3476,7 @@
             if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******************** ENABLING SCREEN!");
 
             // Enable input dispatch.
-            mInputMonitor.setEventDispatchingLw(mEventDispatchingEnabled);
+            mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
         }
 
         try {
@@ -4421,7 +4423,7 @@
     // Input Events and Focus Management
     // -------------------------------------------------------------
 
-    final InputMonitor mInputMonitor = new InputMonitor(this);
+    final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);
     private boolean mEventDispatchingEnabled;
 
     @Override
@@ -4433,7 +4435,7 @@
         synchronized (mWindowMap) {
             mEventDispatchingEnabled = enabled;
             if (mDisplayEnabled) {
-                mInputMonitor.setEventDispatchingLw(enabled);
+                mInputManagerCallback.setEventDispatchingLw(enabled);
             }
         }
     }
@@ -4458,7 +4460,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
@@ -5712,7 +5714,7 @@
             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();
@@ -5759,7 +5761,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
@@ -5825,7 +5827,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 ");
@@ -6065,24 +6067,38 @@
 
     @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());
+            mRoot.forAllDisplays(dc -> {
+                dc.getInputMonitor().createInputConsumer(token, name, inputChannel,
+                        Binder.getCallingPid(), Binder.getCallingUserHandle());
+            });
         }
     }
 
     @Override
     public boolean destroyInputConsumer(String name) {
         synchronized (mWindowMap) {
-            return mInputMonitor.destroyInputConsumer(name);
+            AtomicBoolean retValue = new AtomicBoolean(true);
+            mRoot.forAllDisplays(dc -> {
+                if (!dc.getInputMonitor().destroyInputConsumer(name)) {
+                    retValue.set(false);
+                }
+            });
+            return retValue.get();
         }
     }
 
@@ -6426,7 +6442,7 @@
                 pw.print(" mLastWakeLockObscuringWindow="); pw.print(mLastWakeLockObscuringWindow);
                 pw.println();
 
-        mInputMonitor.dump(pw, "  ");
+        mInputManagerCallback.dump(pw, "  ");
         mUnknownAppVisibilityController.dump(pw, "  ");
         mTaskSnapshotController.dump(pw, "  ");
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 06ffd5e..6385229 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2025,7 +2025,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;
                 }
 
@@ -2092,7 +2092,7 @@
                 UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
         mService.mWindowPlacerLocked.performSurfacePlacement();
         if (focusChanged) {
-            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+            getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ed4d4db..81ac6a4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -233,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;
@@ -6973,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());
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 86c5175..b6f3d3b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -118,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;
@@ -562,6 +563,11 @@
         mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
         traceEnd();
 
+        // Uri Grants Manager.
+        traceBeginAndSlog("UriGrantsManagerService");
+        mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);
+        traceEnd();
+
         // Activity manager runs the show.
         traceBeginAndSlog("StartActivityManager");
         // TODO: Might need to move after migration to WM.
@@ -903,7 +909,7 @@
             }
 
             traceBeginAndSlog("StartInputManager");
-            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
+            inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
             inputManager.start();
             traceEnd();
 
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index e9f1634..8fab197 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -16,10 +16,12 @@
 
 package com.android.server.backup;
 
+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.BackupTransport.TRANSPORT_ERROR;
 import static android.app.backup.ForwardingBackupAgent.forward;
 
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
@@ -45,6 +47,7 @@
 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;
@@ -133,12 +136,8 @@
 import java.util.stream.Stream;
 
 // TODO: When returning to RUNNING_QUEUE vs FINAL, RUNNING_QUEUE sets status = OK. Why? Verify?
-// TODO: Verify WakeLock work source as not being app
-// TODO: Check agent writes new state file => next agent reads it correctly
-// TODO: Check queue of 2, transport rejecting package but other package proceeds
 // TODO: Check queue in general, behavior w/ multiple packages
-// TODO: Check quota is passed from transport to agent
-// TODO: Verify agent with no data doesn't call transport
+// TODO: Test PM invocation
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
         manifest = Config.NONE,
@@ -225,8 +224,8 @@
 
     @Test
     public void testRunTask_whenQueueEmpty_updatesBookkeeping() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
-        TransportMock transportMock = setUpTransport(mTransport);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, true);
@@ -241,8 +240,8 @@
 
     @Test
     public void testRunTask_whenQueueEmpty_releasesWakeLock() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
-        TransportMock transportMock = setUpTransport(mTransport);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, true);
@@ -254,8 +253,8 @@
 
     @Test
     public void testRunTask_whenQueueEmpty_doesNotProduceData() throws Exception {
-        when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
         TransportMock transportMock = setUpTransport(mTransport);
+        when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, true);
@@ -268,8 +267,8 @@
 
     @Test
     public void testRunTask_whenQueueEmpty_doesNotCallTransport() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
-        TransportMock transportMock = setUpTransport(mTransport);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, true);
@@ -283,8 +282,8 @@
 
     @Test
     public void testRunTask_whenQueueEmpty_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
-        TransportMock transportMock = setUpTransport(mTransport);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, true);
@@ -298,11 +297,11 @@
 
     @Test
     public void testRunTask_whenQueueEmpty_doesNotChangeStateFiles() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, true);
-        createPmStateFile();
+        Files.write(getStateFile(mTransport, PM_PACKAGE), "pmState".getBytes());
         Files.write(getStateFile(mTransport, PACKAGE_1), "packageState".getBytes());
 
         runTask(task);
@@ -314,44 +313,36 @@
     }
 
     @Test
-    public void testRunTask_whenOnePackage_logEvents() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+    public void testRunTask_whenOnePackageAndTransportUnavailable() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport.unavailable());
         setUpAgentWithData(PACKAGE_1);
-        Path backupData = createTemporaryFile();
-        when(transportMock.transport.performBackup(
-                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .then(copyBackupDataTo(backupData));
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        createPmStateFile();
+
+        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
 
         runTask(task);
 
         assertEventLogged(EventLogTags.BACKUP_START, mTransport.transportDirName);
-        assertEventLogged(
-                EventLogTags.BACKUP_PACKAGE, PACKAGE_1.packageName, Files.size(backupData));
-    }
-
-    @Test
-    public void testRunTask_whenOnePackage_notifiesCorrectly() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgentWithData(PACKAGE_1);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(mBackupManagerService).logBackupComplete(PACKAGE_1.packageName);
-        verify(mObserver).onResult(PACKAGE_1.packageName, SUCCESS);
-        verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(SUCCESS);
     }
 
     @Test
     public void testRunTask_whenOnePackage_releasesWakeLock() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -364,15 +355,14 @@
 
     @Test
     public void testRunTask_whenOnePackage_updatesBookkeeping() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        // 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);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        // Write PM state to not reset current token
-        createPmStateFile();
 
         runTask(task);
 
@@ -387,7 +377,7 @@
     @Test
     public void testRunTask_whenPackageWithOldStateAndIncremental_passesOldStateToAgent()
             throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -395,7 +385,6 @@
                         mTransport.transportDirName,
                         false,
                         PACKAGE_1);
-        createPmStateFile();
         Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
 
         runTask(task);
@@ -406,7 +395,7 @@
     @Test
     public void testRunTask_whenPackageWithOldStateAndNonIncremental_passesEmptyOldStateToAgent()
             throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -414,7 +403,6 @@
                         mTransport.transportDirName,
                         true,
                         PACKAGE_1);
-        createPmStateFile();
         Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
 
         runTask(task);
@@ -424,10 +412,10 @@
 
     @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));
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient,
@@ -442,10 +430,10 @@
 
     @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));
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient,
@@ -461,10 +449,10 @@
 
     @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));
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient,
@@ -520,7 +508,8 @@
     @Test
     public void testRunTask_whenTransportReturnsErrorForInitialization() throws Exception {
         TransportMock transportMock = setUpTransport(mTransport);
-        when(transportMock.transport.initializeDevice()).thenReturn(TRANSPORT_ERROR);
+        when(transportMock.transport.initializeDevice())
+                .thenReturn(BackupTransport.TRANSPORT_ERROR);
         AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -560,12 +549,11 @@
 
     @Test
     public void testRunTask_whenPackageNotEligibleForBackup() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgentWithData(PACKAGE_1.backupNotAllowed());
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        createPmStateFile();
 
         runTask(task);
 
@@ -579,13 +567,12 @@
 
     @Test
     public void testRunTask_whenPackageDoesFullBackup() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         PackageData packageData = fullBackupPackage(1);
         AgentMock agentMock = setUpAgentWithData(packageData);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, packageData);
-        createPmStateFile();
 
         runTask(task);
 
@@ -598,12 +585,11 @@
 
     @Test
     public void testRunTask_whenPackageIsStopped() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgentWithData(PACKAGE_1.stopped());
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        createPmStateFile();
 
         runTask(task);
 
@@ -615,12 +601,11 @@
 
     @Test
     public void testRunTask_whenPackageUnknown() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         // Not calling setUpAgent()
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        createPmStateFile();
 
         runTask(task);
 
@@ -631,40 +616,177 @@
         assertBackupNotPendingFor(PACKAGE_1);
     }
 
-    // TODO(brufino): Test agent invocation (try block w/ BMS.bindToAgent.. inside invokeNextAgent)
-
     @Test
-    public void testRunTask_whenOnePackage_aboutAgentAndFiles() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+    public void testRunTask_whenCallingAgent_setsWakeLockWorkSource() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgent(PACKAGE_1);
         agentOnBackupDo(
                 agentMock,
                 (oldState, dataOutput, newState) -> {
-                    writeData(dataOutput, "key", "data".getBytes());
-                    writeState(newState, "newState".getBytes());
+                    // 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);
                 });
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        createPmStateFile();
 
         runTask(task);
 
-        verify(agentMock.agent).onBackup(any(), any(), any());
-        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();
+        // More verifications inside agent call above
+        verify(mBackupManagerService).setWorkSource(null);
     }
 
-    // TODO: Test PM agent invocation
+    /**
+     * 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());
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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());
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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 PerformBackupTask#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();
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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 = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
         when(transportMock.transport.getTransportFlags()).thenReturn(flags);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
@@ -677,7 +799,7 @@
 
     @Test
     public void testRunTask_whenTransportDoesNotProvidesFlags() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgent(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -691,12 +813,12 @@
     @Test
     public void testRunTask_whenTransportProvidesFlagsAndMultipleAgents_passesToAll()
             throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        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;
-        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
-        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient,
@@ -712,7 +834,7 @@
 
     @Test
     public void testRunTask_whenTransportChangeFlagsAfterTaskCreation() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgent(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -727,79 +849,15 @@
     }
 
     @Test
-    public void testRunTask_callsTransportPerformBackupWithAgentData() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        AgentMock agentMock = setUpAgent(PACKAGE_1);
-        agentOnBackupDo(
-                agentMock,
-                (oldState, dataOutput, newState) -> {
-                    writeData(dataOutput, "key1", "foo".getBytes());
-                    writeData(dataOutput, "key2", "bar".getBytes());
-                });
-        Path backupDataPath = createTemporaryFile();
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .then(copyBackupDataTo(backupDataPath));
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(transportBinder).performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
-
-        // Now verify data sent
-        FileInputStream inputStream = new FileInputStream(backupDataPath.toFile());
-        BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
-
-        // "key1" => "foo"
-        assertThat(backupData.readNextHeader()).isTrue();
-        assertThat(backupData.getKey()).isEqualTo("key1");
-        int size1 = backupData.getDataSize();
-        byte[] data1 = new byte[size1];
-        backupData.readEntityData(data1, 0, size1);
-        assertThat(data1).isEqualTo("foo".getBytes());
-
-        // "key2" => "bar"
-        assertThat(backupData.readNextHeader()).isTrue();
-        assertThat(backupData.getKey()).isEqualTo("key2");
-        int size2 = backupData.getDataSize();
-        byte[] data2 = new byte[size2];
-        backupData.readEntityData(data2, 0, size2);
-        assertThat(data2).isEqualTo("bar".getBytes());
-
-        // No more
-        assertThat(backupData.readNextHeader()).isFalse();
-        inputStream.close();
-    }
-
-    @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);
-
-        // First for PM, then for the package
-        verify(transportBinder, times(2)).finishBackup();
-    }
-
-    @Test
-    public void testRunTask_whenProhibitedKey_failsAgent() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+    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", "foo".getBytes());
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
                 });
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -807,16 +865,104 @@
 
         runTask(task);
 
-        verify(mListener).onFinished(any());
-        verify(mObserver).onResult(PACKAGE_1.packageName, BackupManager.ERROR_AGENT_FAILURE);
         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());
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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());
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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());
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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());
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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_whenFirstAgentKeyProhibitedButLastPermitted() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
+    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);
@@ -824,12 +970,14 @@
                 agentMock1,
                 (oldState, dataOutput, newState) -> {
                     char prohibitedChar = 0xff00;
-                    writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
                 });
         agentOnBackupDo(
                 agentMock2,
                 (oldState, dataOutput, newState) -> {
-                    writeData(dataOutput, "key", "bar".getBytes());
+                    writeData(dataOutput, "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
                 });
         PerformBackupTask task =
                 createPerformBackupTask(
@@ -841,55 +989,326 @@
         runTask(task);
 
         verify(mListener).onFinished(any());
-        verify(mObserver).onResult(PACKAGE_1.packageName, BackupManager.ERROR_AGENT_FAILURE);
+        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_whenTransportUnavailable() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport.unavailable());
-        setUpAgentWithData(PACKAGE_1);
+    public void testRunTask_whenAgentDoesNotWriteData_doesNotCallTransport() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // No-op
+                });
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
 
         runTask(task);
 
-        verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+        verify(transportMock.transport, never())
+                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
     }
 
     @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);
+    public void testRunTask_whenAgentDoesNotWriteData_logsEvents() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // No-op
+                });
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
 
         runTask(task);
 
-        verify(mObserver)
-                .onResult(PACKAGE_1.packageName, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
+        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
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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());
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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());
+                });
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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));
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
         assertEventLogged(
                 EventLogTags.BACKUP_AGENT_FAILURE, PACKAGE_1.packageName, "Transport rejected");
     }
 
     @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()))
+    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);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
+        setUpAgentWithData(PACKAGE_1);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient,
@@ -899,21 +1318,21 @@
 
         runTask(task);
 
-        verify(mObserver)
-                .onResult(PACKAGE_1.packageName, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
+        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 = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                 .thenReturn(BackupTransport.TRANSPORT_OK);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
                 .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient,
@@ -924,18 +1343,19 @@
         runTask(task);
 
         verify(mObserver).onResult(PACKAGE_1.packageName, SUCCESS);
-        verify(mObserver)
-                .onResult(PACKAGE_2.packageName, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
+        verify(mObserver).onResult(PACKAGE_2.packageName, ERROR_TRANSPORT_PACKAGE_REJECTED);
         verify(mObserver).backupFinished(SUCCESS);
     }
 
     @Test
     public void testRunTask_whenTransportReturnsQuotaExceeded() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+        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);
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
@@ -945,107 +1365,209 @@
         verify(mObserver)
                 .onResult(PACKAGE_1.packageName, BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
         verify(mObserver).backupFinished(SUCCESS);
-        verify(agentMock.agent).onQuotaExceeded(anyLong(), anyLong());
+        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 {
-        // It's going to be non-incremental because we haven't created any previous state
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgentWithData(PACKAGE_1);
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
         when(transportMock.transport.performBackup(
                         argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                 .thenReturn(BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED);
+        setUpAgentWithData(PACKAGE_1);
         PerformBackupTask task =
                 createPerformBackupTask(
                         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, BackupManager.ERROR_TRANSPORT_ABORTED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_TRANSPORT_ABORTED);
+        verify(mObserver).backupFinished(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(
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        Path incrementalData = createTemporaryFile();
+        when(transportMock.transport.performBackup(
                         argThat(packageInfo(PACKAGE_1)),
                         any(),
                         intThat(flags -> (flags & BackupTransport.FLAG_INCREMENTAL) != 0)))
-                .thenReturn(BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED);
-        when(transport.performBackup(
+                .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)))
-                .thenReturn(BackupTransport.TRANSPORT_OK);
+                .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());
+                    }
+                });
         PerformBackupTask task =
                 createPerformBackupTask(
                         transportMock.transportClient,
                         mTransport.transportDirName,
                         false,
                         PACKAGE_1);
-        createPmStateFile();
         // 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_whenIncrementalAndTransportUnavailableDuringPmBackup()
-            throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        setUpAgent(PACKAGE_1);
-        Exception exception = new DeadObjectException();
-        when(transportBinder.getBackupQuota(PM_PACKAGE.packageName, false)).thenThrow(exception);
+    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);
         PerformBackupTask task =
                 createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        false,
-                        PACKAGE_1);
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
 
         runTask(task);
 
-        verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-        assertEventLogged(
-                EventLogTags.BACKUP_AGENT_FAILURE, PM_PACKAGE.packageName, exception.toString());
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_TRANSPORT_ABORTED);
+        verify(mObserver).backupFinished(ERROR_TRANSPORT_ABORTED);
     }
 
     @Test
-    public void testRunTask_whenIncrementalAndPmAgentFails() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        RuntimeException exception = new RuntimeException();
-        PackageManagerBackupAgent pmAgent = createThrowingPmAgent(exception);
-        when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+    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);
         PerformBackupTask task =
                 createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        false,
-                        PACKAGE_1);
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
 
         runTask(task);
 
         verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(eq(BackupManager.ERROR_TRANSPORT_ABORTED));
+        verify(mObserver).backupFinished(ERROR_TRANSPORT_ABORTED);
         assertEventLogged(
-                EventLogTags.BACKUP_AGENT_FAILURE, PM_PACKAGE.packageName, exception.toString());
+                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);
+        PerformBackupTask task =
+                createPerformBackupTask(
+                        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());
     }
 
     private void runTask(PerformBackupTask task) {
@@ -1064,6 +1586,13 @@
         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);
     }
@@ -1104,9 +1633,15 @@
                     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());
-            doReturn(backupAgentBinder)
-                    .when(mBackupManagerService)
-                    .bindToAgentSynchronous(eq(packageInfo.applicationInfo), anyInt());
+            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
@@ -1224,6 +1759,13 @@
                 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;
     }
@@ -1255,7 +1797,12 @@
      * </ul>
      */
     private void createPmStateFile() throws IOException {
-        Files.write(getStateFile(mTransport, PM_PACKAGE), "pmState".getBytes());
+        createPmStateFile(mTransport);
+    }
+
+    /** @see #createPmStateFile() */
+    private void createPmStateFile(TransportData transport) throws IOException {
+        Files.write(getStateFile(transport, PM_PACKAGE), "pmState".getBytes());
     }
 
     /**
@@ -1282,6 +1829,7 @@
                                             new FileInputStream(oldState.getFileDescriptor()),
                                             outputStream);
                                     agentMock.oldState = outputStream.toByteArray();
+                                    agentMock.oldStateHistory.add(agentMock.oldState);
                                     function.onBackup(oldState, dataOutput, newState);
                                 })
                 .when(agentMock.agent)
@@ -1291,18 +1839,26 @@
     /**
      * 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}.
+     * backup data received to {@code backupDataPath} and returns {@code result}.
      */
-    private static Answer<Integer> copyBackupDataTo(Path backupDataPath) {
+    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 BackupTransport.TRANSPORT_OK;
+            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");
     }
@@ -1321,15 +1877,27 @@
     }
 
     private void assertBackupPendingFor(PackageData packageData) throws IOException {
-        assertThat(mBackupManagerService.getJournal().getPackages())
-                .contains(packageData.packageName);
-        assertThat(mBackupManagerService.getPendingBackups()).containsKey(packageData.packageName);
+        String packageName = packageData.packageName;
+        // We verify the current journal, NOT the old one passed to PerformBackupTask constructor
+        assertThat(mBackupManagerService.getJournal().getPackages()).contains(packageName);
+        assertThat(mBackupManagerService.getPendingBackups()).containsKey(packageName);
     }
 
     private void assertBackupNotPendingFor(PackageData packageData) throws IOException {
-        assertJournalDoesNotContain(mBackupManagerService.getJournal(), packageData.packageName);
-        assertThat(mBackupManagerService.getPendingBackups())
-                .doesNotContainKey(packageData.packageName);
+        String packageName = packageData.packageName;
+        // We verify the current journal, NOT the old one passed to PerformBackupTask 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);
     }
 
     /**
@@ -1365,6 +1933,7 @@
     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) {
diff --git a/services/robotests/src/com/android/server/backup/testing/PackageData.java b/services/robotests/src/com/android/server/backup/testing/PackageData.java
index 3badce1..f9177a8 100644
--- a/services/robotests/src/com/android/server/backup/testing/PackageData.java
+++ b/services/robotests/src/com/android/server/backup/testing/PackageData.java
@@ -23,6 +23,7 @@
 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();
 
@@ -42,15 +43,24 @@
     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, int uid) {
+            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;
     }
 
@@ -70,11 +80,15 @@
 
     public PackageData backupNotAllowed() {
         return new PackageData(
-                packageName, agentName, BackupStatus.BACKUP_NOT_ALLOWED, stopped, uid);
+                packageName, agentName, BackupStatus.BACKUP_NOT_ALLOWED, stopped, available, uid);
     }
 
     public PackageData stopped() {
-        return new PackageData(packageName, agentName, backupStatus, true, uid);
+        return new PackageData(packageName, agentName, backupStatus, true, false, uid);
+    }
+
+    public PackageData unavailable() {
+        return new PackageData(packageName, agentName, backupStatus, stopped, false, uid);
     }
 
     public boolean isPm() {
@@ -82,7 +96,6 @@
     }
 
     private static PackageData androidPackage(int identifier, @BackupStatus int backupStatus) {
-        // TODO: Preconditions is not available, include its target in dependencies
         // checkArgument(identifier >= 0, "identifier can't be < 0");
 
         String packageName = "com.sample.package" + identifier;
@@ -91,6 +104,7 @@
                 packageName + ".BackupAgent",
                 backupStatus,
                 false,
+                true,
                 Process.FIRST_APPLICATION_UID + identifier);
     }
 
@@ -101,6 +115,7 @@
                     "com.android.server.backup.PackageManagerBackupAgent",
                     BackupStatus.KEY_VALUE_BACKUP,
                     false,
+                    true,
                     Process.SYSTEM_UID);
         }
 
@@ -118,6 +133,11 @@
         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({
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 bb9a13e..3df1723 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowEventLog.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowEventLog.java
@@ -78,7 +78,7 @@
 
         @Override
         public String toString() {
-            return "Entry{" + "tag=" + tag + ", values=" + values + '}';
+            return "Entry{" + tag + ", " + values + '}';
         }
     }
 }
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 677fd52..7e8697d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -36,8 +36,8 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.DisplayWindowController;
 
 import org.junit.Rule;
@@ -103,6 +103,7 @@
         if (!sOneTimeSetupDone) {
             sOneTimeSetupDone = true;
             MockitoAnnotations.initMocks(this);
+            AttributeCache.init(mContext);
         }
         mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
         mHandlerThread.start();
@@ -127,7 +128,6 @@
     }
 
     ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        AttributeCache.init(mContext);
         final ActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
         setupActivityManagerService(am, atm);
         return am;
@@ -137,6 +137,8 @@
         atm.setActivityManagerService(am);
         atm.mAmInternal = am.new LocalService();
         am.mAtmInternal = atm.new LocalService();
+        am.mUgmInternal = mock(UriGrantsManagerInternal.class);
+        atm.mUgmInternal = mock(UriGrantsManagerInternal.class);
         // Makes sure the supervisor is using with the spy object.
         atm.mStackSupervisor.setService(atm);
         doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 715f8a6..5509935 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -19,6 +19,9 @@
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.MessageQueue;
 import com.android.server.hdmi.HdmiCecController.NativeWrapper;
+import java.util.ArrayList;
+import java.util.List;
+import com.android.internal.annotations.VisibleForTesting;
 
 /** Fake {@link NativeWrapper} useful for testing. */
 final class FakeNativeWrapper implements NativeWrapper {
@@ -41,7 +44,9 @@
                 SendMessageResult.NACK,
             };
 
+    private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
     private HdmiCecMessage mResultMessage;
+    private int mMyPhysicalAddress = 0;
 
     @Override
     public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
@@ -54,7 +59,7 @@
         if (body.length == 0) {
             return mPollAddressResponse[dstAddress];
         } else {
-            mResultMessage = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+            mResultMessages.add(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
         }
         return SendMessageResult.SUCCESS;
     }
@@ -69,7 +74,7 @@
 
     @Override
     public int nativeGetPhysicalAddress(long controllerPtr) {
-        return 0;
+        return mMyPhysicalAddress;
     }
 
     @Override
@@ -103,11 +108,23 @@
         return false;
     }
 
-    public HdmiCecMessage getResultMessage() {
-        return mResultMessage;
+    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/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index cde8e63..4745683 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -52,11 +52,9 @@
     private int mMusicVolume;
     private int mMusicMaxVolume;
     private boolean mMusicMute;
-    private boolean isAwake;
 
     @Before
     public void SetUp() {
-        isAwake = false;
         mHdmiControlService = new HdmiControlService(null) {
             @Override
             AudioManager getAudioManager() {
@@ -108,29 +106,24 @@
 
             @Override
             void wakeUp() {
-                isAwake = true;
             }
         };
         mMyLooper = mTestLooper.getLooper();
         mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
         mHdmiCecLocalDeviceAudioSystem.init();
         mHdmiControlService.setIoLooper(mMyLooper);
-
         mNativeWrapper = new FakeNativeWrapper();
         mHdmiCecController = HdmiCecController.createWithNativeWrapper(
-            mHdmiControlService, mNativeWrapper);
+                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();
     }
-
     @Test
     public void handleGiveAudioStatus_volume_10_mute_true() {
         mMusicVolume = 10;
@@ -138,215 +131,218 @@
         mMusicMaxVolume = 20;
         int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildReportAudioStatus(
-            ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
+                ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
         HdmiCecMessage messageGive = HdmiCecMessageBuilder.buildGiveAudioStatus(
-            ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleGiveSystemAudioModeStatus_originalOff() {
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         HdmiCecMessage messageGive = HdmiCecMessageBuilder
-            .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleRequestArcInitiate() {
         // TODO(b/80296911): Add tests when finishing handler impl.
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+                .buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
         HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleRequestArcTermination() {
         // TODO(b/80297105): Add tests when finishing handler impl.
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+                .buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
         HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
-            .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleSetSystemAudioMode_setOn_orignalOff() {
         mMusicMute = true;
         HdmiCecMessage messageSet = HdmiCecMessageBuilder
-            .buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
+                .buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
         HdmiCecMessage messageGive = HdmiCecMessageBuilder
-            .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
         // Check if originally off
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
-
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
-
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         // Check if correctly turned on
         expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
-
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         assertThat(mMusicMute).isEqualTo(false);
     }
-
     @Test
     public void handleSystemAudioModeRequest_turnOffByTv() {
         assertThat(mMusicMute).isEqualTo(false);
-
         // Check if feature correctly turned off
         HdmiCecMessage messageGive = HdmiCecMessageBuilder
-            .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+                .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
-            .buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
-
+                .buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+                .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
-
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         assertThat(mMusicMute).isEqualTo(true);
     }
-
     @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.getResultMessage()).isEqualTo(expectedMessage);
+                .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         assertThat(mMusicMute).isEqualTo(true);
     }
-
     @Test
     public void systemAudioControlOnPowerOn_alwaysOn() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
-
+                Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
     }
-
     @Test
     public void systemAudioControlOnPowerOn_neverOn() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
-
+                Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
     }
-
     @Test
     public void systemAudioControlOnPowerOn_useLastState_off() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
-
+                Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
     }
-
     @Test
     public void systemAudioControlOnPowerOn_useLastState_on() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
-
+                Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
     }
-
     @Test
     public void handleActiveSource_updateActiveSource() {
         HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildActiveSource(ADDR_TV, 0x0000);
+                .buildActiveSource(ADDR_TV, 0x0000);
         ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
-
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
         assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
                 .isEqualTo(true);
     }
-
     @Test
     public void terminateSystemAudioMode_systemAudioModeOff() {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
-
         mMusicMute = false;
         HdmiCecMessage message = HdmiCecMessageBuilder
                 .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
-
         mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
-
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
         assertThat(mMusicMute).isEqualTo(false);
-        assertThat(mNativeWrapper.getResultMessage()).isNotEqualTo(message);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message);
     }
-
     @Test
     public void terminateSystemAudioMode_systemAudioModeOn() {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(true);
-
         mMusicMute = false;
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
                 .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
-
         mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
-
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
         assertThat(mMusicMute).isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Test
+    public void isPhysicalAddressMeOrBelow_isMe() {
+        int targetPhysicalAddress = 0x1000;
+        mNativeWrapper.setPhysicalAddress(0x1000);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isEqualTo(true);
+    }
+
+    @Test
+    public void isPhysicalAddressMeOrBelow_isBelow() {
+        int targetPhysicalAddress = 0x1100;
+        mNativeWrapper.setPhysicalAddress(0x1000);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isEqualTo(true);
+    }
+
+    @Test
+    public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() {
+        int targetPhysicalAddress = 0x3000;
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isEqualTo(false);
+
+        targetPhysicalAddress = 0x2200;
+        mNativeWrapper.setPhysicalAddress(0x3300);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isEqualTo(false);
+
+        targetPhysicalAddress = 0x2213;
+        mNativeWrapper.setPhysicalAddress(0x2212);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isEqualTo(false);
+
+        targetPhysicalAddress = 0x2340;
+        mNativeWrapper.setPhysicalAddress(0x2310);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isEqualTo(false);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 50c6880..a7618bf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -34,13 +34,13 @@
     public void buildReportPhysicalAddressCommand() {
         HdmiCecMessage message =
                 HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
-                        ADDR_PLAYBACK_1, 01234, HdmiDeviceInfo.DEVICE_PLAYBACK);
+                        ADDR_PLAYBACK_1, 0x1234, HdmiDeviceInfo.DEVICE_PLAYBACK);
         assertThat(message)
                 .isEqualTo(
                         new HdmiCecMessage(
                                 ADDR_PLAYBACK_1,
                                 ADDR_BROADCAST,
                                 Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
-                                new byte[] {012, 034}));
+                                new byte[] {0x12, 0x34, 0x04}));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index 5442674..a1614f2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -130,6 +130,11 @@
             void wakeUp() {
 
             }
+
+            @Override
+            int getPhysicalAddress() {
+                return 0;
+            }
         };
         mHdmiCecLocalDeviceAudioSystem =
                 new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index eec852b..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,6 +34,7 @@
 
 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";
@@ -64,5 +71,10 @@
                             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/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e7a8b58..a0d145d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -69,6 +69,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;
@@ -112,11 +113,13 @@
 
 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;
@@ -187,6 +190,10 @@
     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 {
@@ -233,6 +240,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.
@@ -251,7 +261,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");
@@ -283,7 +293,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;
@@ -2578,13 +2588,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())
@@ -2595,24 +2606,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());
     }
 
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 bd6416d..6be633e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -62,6 +62,7 @@
 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;
@@ -646,7 +647,8 @@
     @Test
     public void testCalculateGrantableUris_PappProvided() throws RemoteException {
         IActivityManager am = mock(IActivityManager.class);
-        when(am.checkGrantUriPermission(anyInt(), eq(null), any(),
+        UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
+        when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
                 anyInt(), anyInt())).thenThrow(new SecurityException());
 
         Notification n = mock(Notification.class);
@@ -655,6 +657,7 @@
                 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();
@@ -667,8 +670,9 @@
     @Test
     public void testCalculateGrantableUris_PuserOverridden() throws RemoteException {
         IActivityManager am = mock(IActivityManager.class);
-        when(am.checkGrantUriPermission(anyInt(), eq(null), any(),
-                anyInt(), anyInt())).thenThrow(SecurityException.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);
@@ -684,8 +688,9 @@
     @Test
     public void testCalculateGrantableUris_prePappProvided() throws RemoteException {
         IActivityManager am = mock(IActivityManager.class);
-        when(am.checkGrantUriPermission(anyInt(), eq(null), any(),
-                anyInt(), anyInt())).thenThrow(SecurityException.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());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 01f5d9c..57729b5 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -30,6 +30,7 @@
 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;
@@ -62,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;
@@ -82,6 +84,7 @@
     final int mCallingUid;
     final Handler mHandler;
     final IActivityManager mAm;
+    final UriGrantsManagerInternal mUgmInternal;
     final IWindowManager mIWindowManager;
     final AppOpsManager mAppOps;
     final IBinder mPermissionOwner;
@@ -141,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, 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);
@@ -303,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);
@@ -352,12 +352,8 @@
                     } 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 {
                         ActivityTaskManager.getService().finishVoiceTask(mSession);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2a68044..1dfe5df 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1493,6 +1493,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.
@@ -2029,6 +2038,7 @@
         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.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
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/tools/hiddenapi/class2greylist/test/Android.mk b/tests/SystemMemoryTest/device/Android.mk
similarity index 67%
copy from tools/hiddenapi/class2greylist/test/Android.mk
copy to tests/SystemMemoryTest/device/Android.mk
index 23f4156..75408df 100644
--- a/tools/hiddenapi/class2greylist/test/Android.mk
+++ b/tests/SystemMemoryTest/device/Android.mk
@@ -13,20 +13,12 @@
 # limitations under the License.
 
 LOCAL_PATH := $(call my-dir)
-
 include $(CLEAR_VARS)
 
-# Only compile source java files in this apk.
+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_MODULE := class2greylisttest
-
-LOCAL_STATIC_JAVA_LIBRARIES := class2greylistlib truth-host-prebuilt mockito-host junit-host
-
-# tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := general-tests
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+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/tools/hiddenapi/class2greylist/test/Android.mk b/tests/SystemMemoryTest/host/Android.mk
similarity index 70%
rename from tools/hiddenapi/class2greylist/test/Android.mk
rename to tests/SystemMemoryTest/host/Android.mk
index 23f4156..a516e38 100644
--- a/tools/hiddenapi/class2greylist/test/Android.mk
+++ b/tests/SystemMemoryTest/host/Android.mk
@@ -15,18 +15,9 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-
-# Only compile source java files in this apk.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE := class2greylisttest
-
-LOCAL_STATIC_JAVA_LIBRARIES := class2greylistlib truth-host-prebuilt mockito-host junit-host
-
-# tag this module as a cts test artifact
+LOCAL_MODULE := system-memory-test
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := tradefed
 LOCAL_COMPATIBILITY_SUITE := general-tests
-
 include $(BUILD_HOST_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
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/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/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 dbf81d6..142c88b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -62,9 +62,7 @@
 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;
@@ -85,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;
@@ -114,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;
@@ -135,7 +131,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -196,7 +191,16 @@
     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";
@@ -1465,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;
 
@@ -1545,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) {
@@ -1601,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
@@ -1617,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);
         }
@@ -1633,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,
@@ -1645,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,
@@ -1769,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();
@@ -1986,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.
@@ -3210,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();
@@ -3230,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);
 
diff --git a/tools/hiddenapi/class2greylist/Android.bp b/tools/hiddenapi/class2greylist/Android.bp
deleted file mode 100644
index 7b1233b..0000000
--- a/tools/hiddenapi/class2greylist/Android.bp
+++ /dev/null
@@ -1,33 +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.
-//
-
-java_library_host {
-  name: "class2greylistlib",
-  srcs: ["src/**/*.java"],
-  static_libs: [
-    "commons-cli-1.2",
-    "apache-bcel",
-  ],
-}
-
-java_binary_host {
-  name: "class2greylist",
-  manifest: "src/class2greylist.mf",
-  static_libs: [
-    "class2greylistlib",
-  ],
-}
-
diff --git a/tools/hiddenapi/class2greylist/src/class2greylist.mf b/tools/hiddenapi/class2greylist/src/class2greylist.mf
deleted file mode 100644
index ea3a3d9..0000000
--- a/tools/hiddenapi/class2greylist/src/class2greylist.mf
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.class2greylist.Class2Greylist
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
deleted file mode 100644
index 6685752..0000000
--- a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
+++ /dev/null
@@ -1,118 +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.class2greylist;
-
-import org.apache.bcel.Const;
-import org.apache.bcel.classfile.AnnotationEntry;
-import org.apache.bcel.classfile.DescendingVisitor;
-import org.apache.bcel.classfile.ElementValuePair;
-import org.apache.bcel.classfile.EmptyVisitor;
-import org.apache.bcel.classfile.Field;
-import org.apache.bcel.classfile.FieldOrMethod;
-import org.apache.bcel.classfile.JavaClass;
-import org.apache.bcel.classfile.Method;
-
-import java.util.Locale;
-
-/**
- * Visits a JavaClass instance and pulls out all members annotated with a
- * specific annotation. The signatures of such members are passed to {@link
- * Status#greylistEntry(String)}. Any errors result in a call to {@link
- * Status#error(String)}.
- *
- * If the annotation has a property "expectedSignature" the generated signature
- * will be verified against the one specified there. If it differs, an error
- * will be generated.
- */
-public class AnnotationVisitor extends EmptyVisitor {
-
-    private static final String EXPECTED_SIGNATURE = "expectedSignature";
-
-    private final JavaClass mClass;
-    private final String mAnnotationType;
-    private final Status mStatus;
-    private final DescendingVisitor mDescendingVisitor;
-
-    public AnnotationVisitor(JavaClass clazz, String annotation, Status d) {
-        mClass = clazz;
-        mAnnotationType = annotation;
-        mStatus = d;
-        mDescendingVisitor = new DescendingVisitor(clazz, this);
-    }
-
-    public void visit() {
-        mStatus.debug("Visit class %s", mClass.getClassName());
-        mDescendingVisitor.visit();
-    }
-
-    private static String getClassDescriptor(JavaClass clazz) {
-        // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
-        // the original class name from the constant pool.
-        return clazz.getConstantPool().getConstantString(
-                clazz.getClassNameIndex(), Const.CONSTANT_Class);
-    }
-
-    @Override
-    public void visitMethod(Method method) {
-        visitMember(method, "L%s;->%s%s");
-    }
-
-    @Override
-    public void visitField(Field field) {
-        visitMember(field, "L%s;->%s:%s");
-    }
-
-    private void visitMember(FieldOrMethod member, String signatureFormatString) {
-        JavaClass definingClass = (JavaClass) mDescendingVisitor.predecessor();
-        mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature());
-        for (AnnotationEntry a : member.getAnnotationEntries()) {
-            if (mAnnotationType.equals(a.getAnnotationType())) {
-                mStatus.debug("Method has annotation %s", mAnnotationType);
-                String signature = String.format(Locale.US, signatureFormatString,
-                        getClassDescriptor(definingClass), member.getName(), member.getSignature());
-                for (ElementValuePair property : a.getElementValuePairs()) {
-                    switch (property.getNameString()) {
-                        case EXPECTED_SIGNATURE:
-                            String expected = property.getValue().stringifyValue();
-                            if (!signature.equals(expected)) {
-                                error(definingClass, member,
-                                        "Expected signature does not match generated:\n"
-                                                + "Expected:  %s\n"
-                                                + "Generated: %s", expected, signature);
-                            }
-                            break;
-                    }
-                }
-                mStatus.greylistEntry(signature);
-            }
-        }
-    }
-
-    private void error(JavaClass clazz, FieldOrMethod member, String message, Object... args) {
-        StringBuilder error = new StringBuilder();
-        error.append(clazz.getSourceFileName())
-                .append(": ")
-                .append(clazz.getClassName())
-                .append(".")
-                .append(member.getName())
-                .append(": ")
-                .append(String.format(Locale.US, message, args));
-
-        mStatus.error(error.toString());
-    }
-
-}
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Class2Greylist.java
deleted file mode 100644
index bcccf4a..0000000
--- a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ /dev/null
@@ -1,99 +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.class2greylist;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.GnuParser;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.OptionBuilder;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.PatternOptionBuilder;
-
-import java.io.IOException;
-
-/**
- * Build time tool for extracting a list of members from jar files that have the @UsedByApps
- * annotation, for building the greylist.
- */
-public class Class2Greylist {
-
-    private static final String ANNOTATION_TYPE = "Landroid/annotation/UsedByApps;";
-
-    public static void main(String[] args) {
-        Options options = new Options();
-        options.addOption(OptionBuilder
-                .withLongOpt("debug")
-                .hasArgs(0)
-                .withDescription("Enable debug")
-                .create("d"));
-        options.addOption(OptionBuilder
-                .withLongOpt("help")
-                .hasArgs(0)
-                .withDescription("Show this help")
-                .create("h"));
-
-        CommandLineParser parser = new GnuParser();
-        CommandLine cmd;
-
-        try {
-            cmd = parser.parse(options, args);
-        } catch (ParseException e) {
-            System.err.println(e.getMessage());
-            help(options);
-            return;
-        }
-        if (cmd.hasOption('h')) {
-            help(options);
-        }
-
-        String[] jarFiles = cmd.getArgs();
-        if (jarFiles.length == 0) {
-            System.err.println("Error: no jar files specified.");
-            help(options);
-        }
-
-        Status status = new Status(cmd.hasOption('d'));
-
-        for (String jarFile : jarFiles) {
-            status.debug("Processing jar file %s", jarFile);
-            try {
-                JarReader reader = new JarReader(status, jarFile);
-                reader.stream().forEach(clazz -> new AnnotationVisitor(
-                        clazz, ANNOTATION_TYPE, status).visit());
-                reader.close();
-            } catch (IOException e) {
-                status.error(e);
-            }
-        }
-        if (status.ok()) {
-            System.exit(0);
-        } else {
-            System.exit(1);
-        }
-
-    }
-
-    private static void help(Options options) {
-        new HelpFormatter().printHelp(
-                "class2greylist path/to/classes.jar [classes2.jar ...]",
-                "Extracts greylist entries from classes jar files given",
-                options, null, true);
-        System.exit(1);
-    }
-}
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/JarReader.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/JarReader.java
deleted file mode 100644
index f3a9d0b..0000000
--- a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/JarReader.java
+++ /dev/null
@@ -1,65 +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.class2greylist;
-
-import org.apache.bcel.classfile.ClassParser;
-import org.apache.bcel.classfile.JavaClass;
-
-import java.io.IOException;
-import java.util.Objects;
-import java.util.stream.Stream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * Reads {@link JavaClass} members from a zip/jar file, providing a stream of them for processing.
- * Any errors are reported via {@link Status#error(Throwable)}.
- */
-public class JarReader {
-
-    private final Status mStatus;
-    private final String mFileName;
-    private final ZipFile mZipFile;
-
-    public JarReader(Status s, String filename) throws IOException {
-        mStatus = s;
-        mFileName = filename;
-        mZipFile = new ZipFile(mFileName);
-    }
-
-    private JavaClass openZipEntry(ZipEntry e) {
-        try {
-            mStatus.debug("Reading %s from %s", e.getName(), mFileName);
-            return new ClassParser(mZipFile.getInputStream(e), e.getName()).parse();
-        } catch (IOException ioe) {
-            mStatus.error(ioe);
-            return null;
-        }
-    }
-
-
-    public Stream<JavaClass> stream() {
-        return mZipFile.stream()
-                .filter(zipEntry -> zipEntry.getName().endsWith(".class"))
-                .map(zipEntry -> openZipEntry(zipEntry))
-                .filter(Objects::nonNull);
-    }
-
-    public void close() throws IOException {
-        mZipFile.close();
-    }
-}
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Status.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Status.java
deleted file mode 100644
index d707898..0000000
--- a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Status.java
+++ /dev/null
@@ -1,58 +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.class2greylist;
-
-import java.util.Locale;
-
-public class Status {
-
-    // Highlight "Error:" in red.
-    private static final String ERROR = "\u001B[31mError: \u001B[0m";
-
-    private final boolean mDebug;
-    private boolean mHasErrors;
-
-    public Status(boolean debug) {
-        mDebug = debug;
-    }
-
-    public void debug(String msg, Object... args) {
-        if (mDebug) {
-            System.err.println(String.format(Locale.US, msg, args));
-        }
-    }
-
-    public void error(Throwable t) {
-        System.err.print(ERROR);
-        t.printStackTrace(System.err);
-        mHasErrors = true;
-    }
-
-    public void error(String message) {
-        System.err.print(ERROR);
-        System.err.println(message);
-        mHasErrors = true;
-    }
-
-    public void greylistEntry(String signature) {
-        System.out.println(signature);
-    }
-
-    public boolean ok() {
-        return !mHasErrors;
-    }
-}
diff --git a/tools/hiddenapi/class2greylist/test/AndroidTest.xml b/tools/hiddenapi/class2greylist/test/AndroidTest.xml
deleted file mode 100644
index 66bb6344..0000000
--- a/tools/hiddenapi/class2greylist/test/AndroidTest.xml
+++ /dev/null
@@ -1,21 +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="class2greylist tests">
-    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="jar" value="class2greylisttest.jar" />
-        <option name="runtime-hint" value="1m" />
-    </test>
-</configuration>
\ No newline at end of file
diff --git a/tools/hiddenapi/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java b/tools/hiddenapi/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java
deleted file mode 100644
index 2d97218..0000000
--- a/tools/hiddenapi/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java
+++ /dev/null
@@ -1,202 +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.javac;
-
-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.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.android.class2greylist.Status;
-import com.android.class2greylist.AnnotationVisitor;
-
-import com.google.common.base.Joiner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-
-import java.io.IOException;
-
-public class AnnotationVisitorTest {
-
-    private static final String ANNOTATION = "Lannotation/Anno;";
-
-    private Javac mJavac;
-    @Mock
-    private Status mStatus;
-
-    @Before
-    public void setup() throws IOException {
-        initMocks(this);
-        mJavac = new Javac();
-        mJavac.addSource("annotation.Anno", Joiner.on('\n').join(
-                "package annotation;",
-                 "import static java.lang.annotation.RetentionPolicy.CLASS;",
-                 "import java.lang.annotation.Retention;",
-                "import java.lang.annotation.Target;",
-                "@Retention(CLASS)",
-                "public @interface Anno {",
-                "  String expectedSignature() default \"\";",
-                "}"));
-    }
-
-    private void assertNoErrors() {
-        verify(mStatus, never()).error(any(Throwable.class));
-        verify(mStatus, never()).error(any(String.class));
-    }
-
-    @Test
-    public void testGreylistMethod() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Anno;",
-                "public class Class {",
-                "  @Anno",
-                "  public void method() {}",
-                "}"));
-        assertThat(mJavac.compile()).isTrue();
-
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
-                .visit();
-
-        assertNoErrors();
-        ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
-        verify(mStatus, times(1)).greylistEntry(greylist.capture());
-        assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
-    }
-
-    @Test
-    public void testGreylistConstructor() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Anno;",
-                "public class Class {",
-                "  @Anno",
-                "  public Class() {}",
-                "}"));
-        assertThat(mJavac.compile()).isTrue();
-
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
-                .visit();
-
-        assertNoErrors();
-        ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
-        verify(mStatus, times(1)).greylistEntry(greylist.capture());
-        assertThat(greylist.getValue()).isEqualTo("La/b/Class;-><init>()V");
-    }
-
-    @Test
-    public void testGreylistField() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Anno;",
-                "public class Class {",
-                "  @Anno",
-                "  public int i;",
-                "}"));
-        assertThat(mJavac.compile()).isTrue();
-
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
-                .visit();
-
-        assertNoErrors();
-        ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
-        verify(mStatus, times(1)).greylistEntry(greylist.capture());
-        assertThat(greylist.getValue()).isEqualTo("La/b/Class;->i:I");
-    }
-
-    @Test
-    public void testGreylistMethodExpectedSignature() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Anno;",
-                "public class Class {",
-                "  @Anno(expectedSignature=\"La/b/Class;->method()V\")",
-                "  public void method() {}",
-                "}"));
-        assertThat(mJavac.compile()).isTrue();
-
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
-                .visit();
-
-        assertNoErrors();
-        ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
-        verify(mStatus, times(1)).greylistEntry(greylist.capture());
-        assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
-    }
-
-    @Test
-    public void testGreylistMethodExpectedSignatureWrong() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Anno;",
-                "public class Class {",
-                "  @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")",
-                "  public void method() {}",
-                "}"));
-        assertThat(mJavac.compile()).isTrue();
-
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
-                .visit();
-
-        verify(mStatus, times(1)).error(any(String.class));
-    }
-
-    @Test
-    public void testGreylistInnerClassMethod() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Anno;",
-                "public class Class {",
-                "  public class Inner {",
-                "    @Anno",
-                "    public void method() {}",
-                "  }",
-                "}"));
-        assertThat(mJavac.compile()).isTrue();
-
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), ANNOTATION,
-                mStatus).visit();
-
-        assertNoErrors();
-        ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
-        verify(mStatus, times(1)).greylistEntry(greylist.capture());
-        assertThat(greylist.getValue()).isEqualTo("La/b/Class$Inner;->method()V");
-    }
-
-    @Test
-    public void testMethodNotGreylisted() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "public class Class {",
-                "  public void method() {}",
-                "}"));
-        assertThat(mJavac.compile()).isTrue();
-
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
-                .visit();
-
-        assertNoErrors();
-        verify(mStatus, never()).greylistEntry(any(String.class));
-    }
-
-}
diff --git a/tools/hiddenapi/class2greylist/test/src/com/android/javac/Javac.java b/tools/hiddenapi/class2greylist/test/src/com/android/javac/Javac.java
deleted file mode 100644
index 202f412..0000000
--- a/tools/hiddenapi/class2greylist/test/src/com/android/javac/Javac.java
+++ /dev/null
@@ -1,103 +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.javac;
-
-import com.google.common.io.Files;
-
-import org.apache.bcel.classfile.ClassParser;
-import org.apache.bcel.classfile.JavaClass;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-import javax.tools.DiagnosticCollector;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileObject;
-import javax.tools.SimpleJavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.StandardLocation;
-import javax.tools.ToolProvider;
-
-/**
- * Helper class for compiling snippets of Java source and providing access to the resulting class
- * files.
- */
-public class Javac {
-
-    private final JavaCompiler mJavac;
-    private final StandardJavaFileManager mFileMan;
-    private final List<JavaFileObject> mCompilationUnits;
-    private final File mClassOutDir;
-
-    public Javac() throws IOException {
-        mJavac = ToolProvider.getSystemJavaCompiler();
-        mFileMan = mJavac.getStandardFileManager(null, Locale.US, null);
-        mClassOutDir = Files.createTempDir();
-        mFileMan.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(mClassOutDir));
-        mFileMan.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(mClassOutDir));
-        mCompilationUnits = new ArrayList<>();
-    }
-
-    private String classToFileName(String classname) {
-        return classname.replace('.', '/');
-    }
-
-    public Javac addSource(String classname, String contents) {
-        JavaFileObject java = new SimpleJavaFileObject(URI.create(
-                String.format("string:///%s.java", classToFileName(classname))),
-                JavaFileObject.Kind.SOURCE
-                ){
-            @Override
-            public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
-                return contents;
-            }
-        };
-        mCompilationUnits.add(java);
-        return this;
-    }
-
-    public boolean compile() {
-        JavaCompiler.CompilationTask task = mJavac.getTask(
-                null,
-                mFileMan,
-                null,
-                null,
-                null,
-                mCompilationUnits);
-        return task.call();
-    }
-
-    public InputStream getClassFile(String classname) throws IOException {
-        Iterable<? extends JavaFileObject> objs = mFileMan.getJavaFileObjects(
-                new File(mClassOutDir, String.format("%s.class", classToFileName(classname))));
-        if (!objs.iterator().hasNext()) {
-            return null;
-        }
-        return objs.iterator().next().openInputStream();
-    }
-
-    public JavaClass getCompiledClass(String classname) throws IOException {
-        return new ClassParser(getClassFile(classname),
-                String.format("%s.class", classToFileName(classname))).parse();
-    }
-}
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 af44b7e..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;
@@ -180,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 922e025..43f0e50 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;
@@ -920,22 +919,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;
 
@@ -2460,7 +2443,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(() -> {
@@ -2469,7 +2452,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);
@@ -3102,9 +3085,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) {
@@ -3718,4 +3700,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 8fb7c87..045291b 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -928,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");
@@ -942,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);
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);
+    }
 }