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);
+ }
}