Merge "Introduce Font and its builder class"
diff --git a/Android.bp b/Android.bp
index 459f179..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",
@@ -479,6 +480,8 @@
"media/java/android/media/tv/ITvRemoteServiceInput.aidl",
"media/java/android/service/media/IMediaBrowserService.aidl",
"media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
+ "telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl",
+ "telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl",
"telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
"telecomm/java/com/android/internal/telecom/ICallScreeningService.aidl",
"telecomm/java/com/android/internal/telecom/IVideoCallback.aidl",
@@ -574,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",
@@ -999,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.
@@ -1006,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",
@@ -1056,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",
@@ -1088,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",
@@ -1122,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",
@@ -1147,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: [
@@ -1180,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",
@@ -1200,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 +
@@ -1249,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",
@@ -1280,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",
@@ -1298,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",
@@ -1312,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 e126fb8..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,9 @@
import android.support.test.runner.AndroidJUnit4;
import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal.CallSession;
+
+import java.util.Random;
import org.junit.After;
import org.junit.Before;
@@ -45,7 +48,7 @@
@Before
public void setUp() {
- mBinderCallsStats = new BinderCallsStats();
+ mBinderCallsStats = new BinderCallsStats(new Random());
}
@After
@@ -59,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++;
}
@@ -71,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 bdedc5f..498ab95 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -272,6 +272,7 @@
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
+ field public static final int allowForceDark = 16844171; // 0x101058b
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -2818,6 +2819,7 @@
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
field public static final int SHOW_MODE_AUTO = 0; // 0x0
field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
+ field public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2; // 0x2
}
public static abstract class AccessibilityService.GestureResultCallback {
@@ -3951,22 +3953,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 +4026,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 +6084,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);
@@ -12671,12 +12675,14 @@
method public static void appendColumns(java.lang.StringBuilder, java.lang.String[]);
method public void appendWhere(java.lang.CharSequence);
method public void appendWhereEscapeString(java.lang.String);
+ method public void appendWhereStandalone(java.lang.CharSequence);
method public java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public deprecated java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public static java.lang.String buildQueryString(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public java.lang.String buildUnionQuery(java.lang.String[], java.lang.String, java.lang.String);
method public java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public deprecated java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.lang.String);
+ method public int delete(android.database.sqlite.SQLiteDatabase, java.lang.String, java.lang.String[]);
method public java.lang.String getTables();
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
@@ -12686,6 +12692,7 @@
method public void setProjectionMap(java.util.Map<java.lang.String, java.lang.String>);
method public void setStrict(boolean);
method public void setTables(java.lang.String);
+ method public int update(android.database.sqlite.SQLiteDatabase, android.content.ContentValues, java.lang.String, java.lang.String[]);
}
public class SQLiteReadOnlyDatabaseException extends android.database.sqlite.SQLiteException {
@@ -40990,6 +40997,16 @@
field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
+ public abstract class CallRedirectionService extends android.app.Service {
+ ctor public CallRedirectionService();
+ method public final void cancelCall();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+ method public final void placeCallUnmodified();
+ method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
+ }
+
public abstract class CallScreeningService extends android.app.Service {
ctor public CallScreeningService();
method public android.os.IBinder onBind(android.content.Intent);
@@ -48838,6 +48855,7 @@
method public void dispatchOnGlobalLayout();
method public boolean dispatchOnPreDraw();
method public boolean isAlive();
+ method public void registerFrameCommitCallback(java.lang.Runnable);
method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
method public void removeOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
@@ -48847,6 +48865,7 @@
method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
+ method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
}
public static abstract interface ViewTreeObserver.OnDrawListener {
@@ -70742,7 +70761,7 @@
ctor public InflaterInputStream(java.io.InputStream);
method protected void fill() throws java.io.IOException;
field protected byte[] buf;
- field protected boolean closed;
+ field protected deprecated boolean closed;
field protected java.util.zip.Inflater inf;
field protected int len;
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 961026b..b88c760 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -81,7 +81,7 @@
package android.os {
public class Build {
- field public static final boolean PERMISSIONS_REVIEW_REQUIRED;
+ field public static final boolean PERMISSIONS_REVIEW_REQUIRED = true;
}
public final class PowerManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index b8acfdb..462d22e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -266,6 +266,7 @@
}
public abstract class PackageManager {
+ method public abstract boolean arePermissionsIndividuallyControlled();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
@@ -275,7 +276,6 @@
method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
method public abstract java.lang.String getSharedSystemSharedLibraryPackageName();
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
- method public abstract boolean isPermissionReviewModeEnabled();
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final java.lang.String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
@@ -1467,6 +1467,11 @@
method public static int getLongPressTooltipHideTimeout();
}
+ public final class ViewTreeObserver {
+ method public void registerFrameCommitCallback(java.lang.Runnable);
+ method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
+ }
+
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
diff --git a/cmds/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/Android.bp b/cmds/statsd/Android.bp
index 14af5b9..b566099 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -25,7 +25,6 @@
],
shared_libs: [
- "libmetricprotos",
"libplatformprotos",
],
@@ -38,7 +37,6 @@
},
export_shared_lib_headers: [
- "libmetricprotos",
"libplatformprotos",
]
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 49ea6d5..61c185f 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -232,7 +232,6 @@
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
libgmock \
- libmetricprotos \
libplatformprotos
LOCAL_PROTOC_OPTIMIZE_TYPE := full
@@ -253,7 +252,6 @@
LOCAL_MODULE := statsdprotolite
LOCAL_SRC_FILES := \
- src/metrics_constants/metrics_constants.proto \
src/stats_log.proto \
src/statsd_config.proto \
src/atoms.proto
@@ -317,7 +315,6 @@
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
- libmetricprotos \
libplatformprotos
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1119eb3..0c241fc 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -403,7 +403,7 @@
fprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n");
fprintf(out, "\n be removed from memory and disk!\n");
fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME [--proto]\n");
+ fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] [--proto]\n");
fprintf(out, " Dump all metric data for a configuration.\n");
fprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
@@ -567,12 +567,17 @@
int argCount = args.size();
bool good = false;
bool proto = false;
+ bool includeCurrentBucket = false;
int uid;
string name;
if (!std::strcmp("--proto", args[argCount-1].c_str())) {
proto = true;
argCount -= 1;
}
+ if (!std::strcmp("--include_current_bucket", args[argCount-1].c_str())) {
+ includeCurrentBucket = true;
+ argCount -= 1;
+ }
if (argCount == 2) {
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
@@ -600,7 +605,7 @@
if (good) {
vector<uint8_t> data;
mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
- false /* include_current_bucket*/, ADB_DUMP, &data);
+ includeCurrentBucket, ADB_DUMP, &data);
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
fprintf(out, "%c", data[i]);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 279ed02..203a8e9b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -29,7 +29,6 @@
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
-import "frameworks/base/proto/src/metrics_constants.proto";
/**
* The master atom class. This message defines all of the available
@@ -49,7 +48,7 @@
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
BleScanStateChanged ble_scan_state_changed = 2;
- // 3 is available for use
+ ProcessStateChanged process_state_changed = 3;
BleScanResultReceived ble_scan_result_received = 4;
SensorStateChanged sensor_state_changed = 5;
GpsScanStateChanged gps_scan_state_changed = 6;
@@ -60,7 +59,12 @@
LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12;
WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13;
- // 14 - 19 are available
+ ActivityManagerSleepStateChanged activity_manager_sleep_state_changed = 14;
+ MemoryFactorStateChanged memory_factor_state_changed = 15;
+ ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16;
+ CachedKillReported cached_kill_reported = 17;
+ ProcessMemoryStatReported process_memory_stat_reported = 18;
+ // 19 is available
BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
@@ -126,7 +130,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10023
+ // Next: 10024
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -151,6 +155,7 @@
FullBatteryCapacity full_battery_capacity = 10020;
Temperature temperature = 10021;
BinderCalls binder_calls = 10022;
+ BinderCallsExceptions binder_calls_exceptions = 10023;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -210,7 +215,8 @@
}
/**
- * Logs that the state of a process state, as per the activity manager, has changed.
+ * Logs that the process state of the uid, as determined by ActivityManager
+ * (i.e. the highest process state of that uid's processes) has changed.
*
* Logged from:
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -223,6 +229,112 @@
}
/**
+ * Logs process state change of a process, as per the activity manager.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
+ */
+message ProcessStateChanged {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ // TODO: remove this when validation is done
+ optional int64 version = 5;
+ // The state, from frameworks/base/core/proto/android/app/enums.proto.
+ optional android.app.ProcessStateEnum state = 4;
+}
+
+/**
+ * Logs when ActivityManagerService sleep state is changed.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+ */
+message ActivityManagerSleepStateChanged {
+ // TODO: import frameworks proto
+ enum State {
+ UNKNOWN = 0;
+ ASLEEP = 1;
+ AWAKE = 2;
+ }
+ optional State state = 1 [(stateFieldOption).option = EXCLUSIVE];
+}
+
+/**
+ * Logs when system memory state changes.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message MemoryFactorStateChanged {
+ // TODO: import frameworks proto
+ enum State {
+ MEMORY_UNKNOWN = 0;
+ NORMAL = 1; // normal.
+ MODERATE = 2; // moderate memory pressure.
+ LOW = 3; // low memory.
+ CRITICAL = 4; // critical memory.
+
+ }
+ optional State factor = 1 [(stateFieldOption).option = EXCLUSIVE];
+}
+
+/**
+ * Logs when app is using too much cpu, according to ActivityManagerService.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message ExcessiveCpuUsageReported {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ // package version. TODO: remove this when validation is done
+ optional int64 version = 4;
+}
+
+/**
+ * Logs when a cached process is killed, along with its pss.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message CachedKillReported {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ // TODO: remove this when validation is done
+ optional int64 version = 5;
+ optional int64 pss = 4;
+}
+
+/**
+ * Logs when memory stats of a process is reported.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
+ */
+message ProcessMemoryStatReported {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ //TODO: remove this when validation is done
+ optional int64 version = 9;
+ optional int64 pss = 4;
+ optional int64 uss = 5;
+ optional int64 rss = 6;
+ enum Type {
+ ADD_PSS_INTERNAL_SINGLE = 0;
+ ADD_PSS_INTERNAL_ALL_MEM = 1;
+ ADD_PSS_INTERNAL_ALL_POLL = 2;
+ ADD_PSS_EXTERNAL = 3;
+ ADD_PSS_EXTERNAL_SLOW = 4;
+ }
+ optional Type type = 7;
+ optional int64 duration = 8;
+}
+
+/**
* Logs that a process started, finished, crashed, or ANRed.
*
* Logged from:
@@ -1982,44 +2094,76 @@
*
* Binder stats are cumulative from boot unless somebody reset the data using
* > adb shell dumpsys binder_calls_stats --reset
+ *
+ * Next tag: 14
*/
message BinderCalls {
- // TODO(gaillard): figure out if binder call stats includes data from isolated uids, if a uid
- // gets recycled and we have isolated uids, we might attribute the data incorrectly.
- // TODO(gaillard): there is a high dimensions cardinality, figure out if we should drop the less
- // commonly used APIs.
- optional int32 uid = 1 [(is_uid) = true];
- // Fully qualified class name of the API call.
- optional string service_class_name = 2;
- // Method name of the API call. It can also be a transaction code if we cannot resolve it to a
- // name. See Binder#getTransactionName.
- optional string service_method_name = 3;
- // Total number of API calls.
- optional int64 call_count = 4;
- // Number of exceptions thrown by the API.
- optional int64 exception_count = 5;
- // Total latency of all API calls.
- // Average can be computed using total_latency_micros / call_count.
- optional int64 total_latency_micros = 6;
- // Maximum latency of one API call.
- optional int64 max_latency_micros = 7;
- // Total CPU usage of all API calls.
- optional int64 total_cpu_micros = 8;
- // Maximum CPU usage of one API call.
- optional int64 max_cpu_micros = 9;
- // Maximum parcel reply size of one API call.
- optional int64 max_reply_size_bytes = 10;
- // Maximum parcel request size of one API call.
- optional int64 max_request_size_bytes = 11;
+ optional int32 uid = 1 [(is_uid) = true];
+ // Fully qualified class name of the API call.
+ //
+ // This is a system server class name.
+ //
+ // TODO(gaillard): figure out if binder call stats includes data from isolated uids, if a uid
+ // gets recycled and we have isolated uids, we might attribute the data incorrectly.
+ // TODO(gaillard): there is a high dimensions cardinality, figure out if we should drop the less
+ // commonly used APIs.
+ optional string service_class_name = 2;
+ // Method name of the API call. It can also be a transaction code if we cannot
+ // resolve it to a name. See Binder#getTransactionName.
+ //
+ // This is a system server method name.
+ optional string service_method_name = 3;
+ // Total number of API calls.
+ optional int64 call_count = 4;
+ // True if the screen was interactive PowerManager#isInteractive at the end of the call.
+ optional bool screen_interactive = 13;
+ // Total number of API calls we have data recorded for. If we collected data for all the calls,
+ // call_count will be equal to recorded_call_count.
+ //
+ // If recorded_call_count is different than call_count, it means data collection has been
+ // sampled. All the fields below will be sampled in this case.
+ optional int64 recorded_call_count = 12;
+ // Number of exceptions thrown by the API.
+ optional int64 recorded_exception_count = 5;
+ // Total latency of all API calls.
+ // Average can be computed using total_latency_micros / recorded_call_count.
+ optional int64 recorded_total_latency_micros = 6;
+ // Maximum latency of one API call.
+ optional int64 recorded_max_latency_micros = 7;
+ // Total CPU usage of all API calls.
+ // Average can be computed using total_cpu_micros / recorded_call_count.
+ // Total can be computed using total_cpu_micros / recorded_call_count * call_count.
+ optional int64 recorded_total_cpu_micros = 8;
+ // Maximum CPU usage of one API call.
+ optional int64 recorded_max_cpu_micros = 9;
+ // Maximum parcel reply size of one API call.
+ optional int64 recorded_max_reply_size_bytes = 10;
+ // Maximum parcel request size of one API call.
+ optional int64 recorded_max_request_size_bytes = 11;
+}
+
+/**
+ * Pulls the statistics of exceptions during calls to Binder.
+ *
+ * Binder stats are cumulative from boot unless somebody reset the data using
+ * > adb shell dumpsys binder_calls_stats --reset
+ */
+message BinderCallsExceptions {
+ // Exception class name, e.g. java.lang.IllegalArgumentException.
+ //
+ // This is an exception class name thrown by the system server.
+ optional string exception_class_name = 1;
+ // Total number of exceptions.
+ optional int64 exception_count = 2;
}
/**
* An atom for generic metrics logging. Available from Android Q.
- * One has to add an enum to frameworks/base/proto/src/metrics_constants.proto
- * to extend another metric.
*/
message GenericAtom {
- // Type of event. Previously it only indicated visual elements but now it
- // is expanded to describe any type of event.
- optional com_android_internal_logging.MetricsEvent.View view = 1;
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // An event_id indicates the type of event.
+ optional int32 event_id = 2;
}
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index caac677..22cb2f5 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -37,6 +37,8 @@
virtual ~StatsPuller() {}
+ // Pulls the data. The returned data will have elapsedTimeNs set as timeNs
+ // and will have wallClockTimeNs set as current wall clock time.
bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
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/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 45efc4a..bbf5d9d 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -53,9 +53,12 @@
virtual ~StatsPullerManager() {
}
+ // Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
+ // and then every intervalNs thereafter.
virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
int64_t intervalNs);
+ // Stop listening on a tagId.
virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
// Verify if we know how to pull for this matcher
@@ -63,11 +66,16 @@
void OnAlarmFired(const int64_t timeNs);
+ // Use respective puller to pull the data. The returned data will have
+ // elapsedTimeNs set as timeNs and will have wallClockTimeNs set as current
+ // wall clock time.
virtual bool Pull(const int tagId, const int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data);
+ // Clear pull data cache immediately.
int ForceClearPullerCache();
+ // Clear pull data cache if it is beyond respective cool down time.
int ClearPullerCacheIfNecessary(int64_t timestampNs);
void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index a894782..bd94800 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -66,8 +66,8 @@
CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
+ const int64_t timeBaseNs, const int64_t startTimeNs)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -100,6 +100,10 @@
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
+ flushIfNeededLocked(startTimeNs);
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
+
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 520d5de..39d4ae2 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -42,7 +42,7 @@
public:
CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs);
+ const int64_t timeBaseNs, const int64_t startTimeNs);
virtual ~CountMetricProducer();
@@ -98,6 +98,7 @@
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
+ FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index a19eb0b..9d9e5be9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -68,8 +68,8 @@
const bool nesting,
const sp<ConditionWizard>& wizard,
const FieldMatcher& internalDimensions,
- const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
+ const int64_t timeBaseNs, const int64_t startTimeNs)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
@@ -128,6 +128,9 @@
mMetric2ConditionLinks.begin()->conditionFields);
}
}
+ flushIfNeededLocked(startTimeNs);
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index c496b12..12addb8 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -42,7 +42,7 @@
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
const sp<ConditionWizard>& wizard,
- const FieldMatcher& internalDimensions, const int64_t startTimeNs);
+ const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs);
virtual ~DurationMetricProducer();
@@ -141,6 +141,7 @@
FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
+ FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index a779410..fbe0b21 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -76,6 +76,7 @@
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mPullerManager(pullerManager),
mPullTagId(pullTagId),
+ mIsPulled(pullTagId != -1),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
StatsdStats::kAtomDimensionKeySizeLimitMap.end()
@@ -125,11 +126,17 @@
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
- if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
+ if (mIsPulled) {
+ pullLocked(startTimeNs);
+ }
+
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
mConditionSliced);
@@ -137,7 +144,7 @@
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
- if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mPullerManager->UnRegisterReceiver(mPullTagId, this);
}
}
@@ -323,13 +330,11 @@
if (!triggerPuller) {
return;
}
-
vector<std::shared_ptr<LogEvent>> allData;
if (!mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
- ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
+ ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
-
for (const auto& data : allData) {
onMatchedLogEventLocked(0, *data);
}
@@ -340,8 +345,7 @@
VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
flushIfNeededLocked(eventTimeNs);
mCondition = conditionMet;
-
- if (mPullTagId != -1) {
+ if (mIsPulled) {
pullLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
@@ -354,7 +358,7 @@
// If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
// pull for every dimension.
mCondition = overallCondition;
- if (mPullTagId != -1) {
+ if (mIsPulled) {
pullLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6984aa2..cc65440 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -77,7 +77,7 @@
}
flushCurrentBucketLocked(eventTimeNs);
mCurrentBucketStartTimeNs = eventTimeNs;
- if (mPullTagId != -1) {
+ if (mIsPulled) {
pullLocked(eventTimeNs);
}
};
@@ -121,6 +121,9 @@
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
+ // if this is pulled metric
+ const bool mIsPulled;
+
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
@@ -159,12 +162,13 @@
const size_t mGaugeAtomsPerDimensionLimit;
- FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestWithSlicedCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
- FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
+ FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index c6f7bb4..0768298 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -74,7 +74,7 @@
ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t timeBaseNs, const int64_t startTimestampNs,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mPullerManager(pullerManager),
@@ -127,13 +127,20 @@
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
- flushIfNeededLocked(startTimestampNs);
+ flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mIsPulled) {
- mPullerManager->RegisterReceiver(mPullTagId, this,
- mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
+ mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mBucketSizeNs);
}
+ // TODO: Only do this for partial buckets like first bucket. All other buckets should use
+ // flushIfNeeded to adjust start and end to bucket boundaries.
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
+ if (mIsPulled) {
+ pullLocked(startTimeNs);
+ }
VLOG("value metric %lld created. bucket size %lld start_time: %lld",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
@@ -280,16 +287,19 @@
flushIfNeededLocked(eventTimeNs);
if (mIsPulled) {
- vector<shared_ptr<LogEvent>> allData;
- if (mPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
- if (allData.size() == 0) {
- return;
- }
- for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data);
- }
+ pullLocked(eventTimeNs);
+ }
+}
+
+void ValueMetricProducer::pullLocked(const int64_t timestampNs) {
+ vector<std::shared_ptr<LogEvent>> allData;
+ if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+ if (allData.size() == 0) {
+ return;
}
- return;
+ for (const auto& data : allData) {
+ onMatchedLogEventLocked(0, *data);
+ }
}
}
@@ -306,12 +316,14 @@
int64_t eventTime = mTimeBaseNs +
((realEventTime - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
+ // close the end of the bucket
mCondition = false;
for (const auto& data : allData) {
data->setElapsedTimestampNs(eventTime - 1);
onMatchedLogEventLocked(0, *data);
}
+ // start a new bucket
mCondition = true;
for (const auto& data : allData) {
data->setElapsedTimestampNs(eventTime);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 188e3de..b2f0b6f 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -55,7 +55,7 @@
const int64_t version) override {
std::lock_guard<std::mutex> lock(mMutex);
- if (mPullTagId != -1 && (mCondition == true || mConditionTrackerIndex < 0) ) {
+ if (mIsPulled && (mCondition == true || mConditionTrackerIndex < 0)) {
vector<shared_ptr<LogEvent>> allData;
mPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
if (allData.size() == 0) {
@@ -159,6 +159,8 @@
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+ void pullLocked(const int64_t timestampNs);
+
static const size_t kBucketSize = sizeof(ValueBucket{});
const size_t mDimensionSoftLimit;
@@ -171,7 +173,7 @@
const Type mValueType;
- FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
@@ -190,6 +192,7 @@
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced);
+ FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index e03edb3..ff48d02 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -312,7 +312,7 @@
}
sp<MetricProducer> countProducer =
- new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
+ new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs);
allMetricProducers.push_back(countProducer);
}
@@ -382,7 +382,7 @@
sp<MetricProducer> durationMetric = new DurationMetricProducer(
key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
- trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs);
+ trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs);
allMetricProducers.push_back(durationMetric);
}
diff --git a/cmds/statsd/src/metrics_constants/metrics_constants.proto b/cmds/statsd/src/metrics_constants/metrics_constants.proto
deleted file mode 120000
index 8366db7..0000000
--- a/cmds/statsd/src/metrics_constants/metrics_constants.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../proto/src/metrics_constants.proto
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 9a8919e..67c704e 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -37,6 +37,19 @@
const ConfigKey kConfigKey(0, 12345);
+TEST(CountMetricProducerTest, TestFirstBucket) {
+ CountMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ 5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+ EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, countProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
+}
+
TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -56,8 +69,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
// 2 events in bucket 1.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -119,8 +131,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
countProducer.onConditionChanged(true, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -181,8 +192,7 @@
EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -221,8 +231,7 @@
event1.init();
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -280,8 +289,7 @@
event1.init();
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
// Bucket is flushed yet.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -337,8 +345,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 7ef8c5b..b540964 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -39,6 +39,23 @@
const ConfigKey kConfigKey(0, 12345);
+TEST(DurationMetricTrackerTest, TestFirstBucket) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+
+ EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs());
+}
+
TEST(DurationMetricTrackerTest, TestNoCondition) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
int64_t bucketStartTimeNs = 10000000000;
@@ -58,8 +75,7 @@
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -100,8 +116,7 @@
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
EXPECT_FALSE(durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -151,8 +166,7 @@
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -206,8 +220,7 @@
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -261,8 +274,7 @@
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -300,8 +312,7 @@
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -348,8 +359,7 @@
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 19c9f77..2fda858 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -47,7 +47,33 @@
const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-TEST(GaugeMetricProducerTest, TestNoCondition) {
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(GaugeMetricProducerTest, TestFirstBucket) {
+ GaugeMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_gauge_fields_filter()->set_include_all(false);
+ auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+ gaugeFieldMatcher->set_field(tagId);
+ gaugeFieldMatcher->add_child()->set_field(1);
+ gaugeFieldMatcher->add_child()->set_field(3);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ -1, 5, 600 * NS_PER_SEC + NS_PER_SEC/2, pullerManager);
+
+ EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
+}
+
+TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -62,6 +88,16 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
@@ -83,7 +119,9 @@
EXPECT_EQ(10, it->mValue.int_value);
it++;
EXPECT_EQ(11, it->mValue.int_value);
- EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms
+ .front().mFields->begin()->mValue.int_value);
allData.clear();
std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
@@ -102,7 +140,7 @@
EXPECT_EQ(25, it->mValue.int_value);
// One dimension.
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(10L, it->mValue.int_value);
@@ -114,7 +152,7 @@
EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
// One dimension.
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(24L, it->mValue.int_value);
@@ -210,6 +248,7 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -263,7 +302,7 @@
->mValue.int_value);
}
-TEST(GaugeMetricProducerTest, TestWithCondition) {
+TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -333,7 +372,7 @@
->mValue.int_value);
}
-TEST(GaugeMetricProducerTest, TestWithSlicedCondition) {
+TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
const int conditionTag = 65;
GaugeMetric metric;
metric.set_id(1111111);
@@ -409,13 +448,14 @@
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
}
-TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
sp<AlarmMonitor> alarmMonitor;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
GaugeMetric metric;
metric.set_id(metricId);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 3559a7c..57aab97 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -50,9 +50,32 @@
const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestFirstBucket) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ -1, 5, 600 * NS_PER_SEC + NS_PER_SEC/2, pullerManager);
+
+ EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
+}
+
+/*
* Tests pulled atoms with no conditions
*/
-TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
+TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
ValueMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -63,10 +86,20 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -85,7 +118,8 @@
EXPECT_EQ(true, curInterval.startUpdated);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(11, curInterval.start.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -99,9 +133,9 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
// tartUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.value.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
allData.clear();
@@ -115,9 +149,9 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
// startUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.value.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
@@ -136,10 +170,10 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -205,10 +239,10 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -275,6 +309,17 @@
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ // should not take effect
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -298,7 +343,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -349,7 +393,6 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -392,6 +435,7 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -404,7 +448,6 @@
}));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -447,6 +490,7 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -469,7 +513,6 @@
}));
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
valueProducer.onConditionChanged(false, bucket2StartTimeNs-100);
@@ -496,7 +539,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -538,7 +580,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -612,7 +653,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -687,10 +727,10 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
// pull 1
@@ -770,6 +810,7 @@
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
// condition becomes true
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
@@ -795,7 +836,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -849,6 +889,7 @@
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
// condition becomes true
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
@@ -885,7 +926,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -947,6 +987,7 @@
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
// condition becomes true
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
@@ -972,7 +1013,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -1022,7 +1062,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1065,7 +1104,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1108,7 +1146,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1154,7 +1191,6 @@
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1218,14 +1254,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
-
EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index d81e2dc..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
@@ -3210,6 +3208,7 @@
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEARCHABLE:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_DISPLAY_NUMBER:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_TITLE:Ljava/lang/String;
+Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SERIES_ID:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SHORT_DESCRIPTION:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_THUMBNAIL_URI:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_TITLE:Ljava/lang/String;
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/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 829a9444..300a530 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -31,7 +31,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -398,13 +397,49 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "SHOW_MODE_" }, value = {
SHOW_MODE_AUTO,
- SHOW_MODE_HIDDEN
+ SHOW_MODE_HIDDEN,
+ SHOW_MODE_WITH_HARD_KEYBOARD
})
public @interface SoftKeyboardShowMode {}
+ /**
+ * Allow the system to control when the soft keyboard is shown.
+ * @see SoftKeyboardController
+ */
public static final int SHOW_MODE_AUTO = 0;
+
+ /**
+ * Never show the soft keyboard.
+ * @see SoftKeyboardController
+ */
public static final int SHOW_MODE_HIDDEN = 1;
+ /**
+ * Allow the soft keyboard to be shown, even if a hard keyboard is connected
+ * @see SoftKeyboardController
+ */
+ public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2;
+
+ /**
+ * Mask used to cover the show modes supported in public API
+ * @hide
+ */
+ public static final int SHOW_MODE_MASK = 0x03;
+
+ /**
+ * Bit used to hold the old value of the hard IME setting to restore when a service is shut
+ * down.
+ * @hide
+ */
+ public static final int SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE = 0x20000000;
+
+ /**
+ * Bit for show mode setting to indicate that the user has overridden the hard keyboard
+ * behavior.
+ * @hide
+ */
+ public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000;
+
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
private AccessibilityServiceInfo mInfo;
@@ -1147,7 +1182,27 @@
}
/**
- * Used to control and query the soft keyboard show mode.
+ * Used to control, query, and listen for changes to the soft keyboard show mode.
+ * <p>
+ * Accessibility services may request to override the decisions normally made about whether or
+ * not the soft keyboard is shown.
+ * <p>
+ * If multiple services make conflicting requests, the last request is honored. A service may
+ * register a listener to find out if the mode has changed under it.
+ * <p>
+ * If the user takes action to override the behavior behavior requested by an accessibility
+ * service, the user's request takes precendence, the show mode will be reset to
+ * {@link AccessibilityService#SHOW_MODE_AUTO}, and services will no longer be able to control
+ * that aspect of the soft keyboard's behavior.
+ * <p>
+ * Note: Because soft keyboards are independent apps, the framework does not have total control
+ * over their behavior. They may choose to show themselves, or not, without regard to requests
+ * made here. So the framework will make a best effort to deliver the behavior requested, but
+ * cannot guarantee success.
+ *
+ * @see AccessibilityService#SHOW_MODE_AUTO
+ * @see AccessibilityService#SHOW_MODE_HIDDEN
+ * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
*/
public static final class SoftKeyboardController {
private final AccessibilityService mService;
@@ -1217,7 +1272,8 @@
* @param listener the listener to remove, must be non-null
* @return {@code true} if the listener was removed, {@code false} otherwise
*/
- public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
+ public boolean removeOnShowModeChangedListener(
+ @NonNull OnShowModeChangedListener listener) {
if (mListeners == null) {
return false;
}
@@ -1289,32 +1345,32 @@
}
/**
- * Returns the show mode of the soft keyboard. The default show mode is
- * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
- * focused. An AccessibilityService can also request the show mode
- * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
+ * Returns the show mode of the soft keyboard.
*
* @return the current soft keyboard show mode
+ *
+ * @see AccessibilityService#SHOW_MODE_AUTO
+ * @see AccessibilityService#SHOW_MODE_HIDDEN
+ * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
*/
@SoftKeyboardShowMode
public int getShowMode() {
- try {
- return Settings.Secure.getInt(mService.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
- } catch (Settings.SettingNotFoundException e) {
- Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
- // The settings hasn't been changed yet, so it's value is null. Return the default.
- return 0;
- }
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getSoftKeyboardShowMode();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ return SHOW_MODE_AUTO;
}
/**
- * Sets the soft keyboard show mode. The default show mode is
- * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
- * focused. An AccessibilityService can also request the show mode
- * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
- * The lastto this method will be honored, regardless of any previous calls (including those
- * made by other AccessibilityServices).
+ * Sets the soft keyboard show mode.
* <p>
* <strong>Note:</strong> If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
@@ -1322,6 +1378,10 @@
*
* @param showMode the new show mode for the soft keyboard
* @return {@code true} on success
+ *
+ * @see AccessibilityService#SHOW_MODE_AUTO
+ * @see AccessibilityService#SHOW_MODE_HIDDEN
+ * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
*/
public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
final IAccessibilityServiceConnection connection =
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 037aeb0..276131f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -87,6 +87,8 @@
boolean setSoftKeyboardShowMode(int showMode);
+ int getSoftKeyboardShowMode();
+
void setSoftKeyboardCallbackEnabled(boolean enabled);
boolean isAccessibilityButtonAvailable();
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
new file mode 100644
index 0000000..05de3e8
--- /dev/null
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a class member, that is not part of the SDK, is used by apps.
+ * Since the member is not part of the SDK, such use is not supported.
+ *
+ * This annotation acts as a heads up that changing a given method or field
+ * may affect apps, potentially breaking them when the next Android version is
+ * released. In some cases, for members that are heavily used, this annotation
+ * may imply restrictions on changes to the member.
+ *
+ * This annotation also results in access to the member being permitted by the
+ * runtime, with a warning being generated in debug builds.
+ *
+ * For more details, see go/UnsupportedAppUsage.
+ *
+ * {@hide}
+ */
+@Retention(CLASS)
+@Target({CONSTRUCTOR, METHOD, FIELD})
+public @interface UnsupportedAppUsage {
+
+ /**
+ * Associates a bug tracking the work to add a public alternative to this API. Optional.
+ *
+ * @return ID of the associated tracking bug
+ */
+ long trackingBug() default 0;
+
+ /**
+ * For debug use only. The expected dex signature to be generated for this API, used to verify
+ * parts of the build process.
+ *
+ * @return A dex API signature.
+ */
+ String expectedSignature() default "";
+}
diff --git a/core/java/android/app/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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 344610a..09a614c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -342,9 +342,15 @@
}
@Override
- public boolean isPermissionReviewModeEnabled() {
+ public boolean arePermissionsIndividuallyControlled() {
return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_permissionReviewRequired);
+ com.android.internal.R.bool.config_permissionsIndividuallyControlled);
+ }
+
+ @Override
+ public boolean isWirelessConsentModeEnabled() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wirelessConsentRequired);
}
@Override
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/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 69852f3..6f73d02 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -33,7 +33,7 @@
// Perms.
void grantSlicePermission(String callingPkg, String toPkg, in Uri uri);
void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri);
- int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+ int checkSlicePermission(in Uri uri, String callingPkg, String pkg, int pid, int uid,
in String[] autoGrantPermissions);
void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 26498e8..955093d 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -430,7 +430,8 @@
*/
public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
try {
- return mService.checkSlicePermission(uri, null, pid, uid, null);
+ return mService.checkSlicePermission(uri, mContext.getPackageName(), null, pid, uid,
+ null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -487,7 +488,8 @@
if (pkg == null) {
throw new SecurityException("No pkg specified");
}
- int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
+ int result = mService.checkSlicePermission(uri, mContext.getPackageName(), pkg, pid,
+ uid, autoGrantPermissions);
if (result == PERMISSION_DENIED) {
throw new SecurityException("User " + uid + " does not have slice permission for "
+ uri + ".");
diff --git a/core/java/android/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/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ce551ee..c0b3400 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3269,12 +3269,19 @@
@PermissionInfoFlags int flags) throws NameNotFoundException;
/**
- * Returns true if Permission Review Mode is enabled, false otherwise.
+ * Returns true if some permissions are individually controlled
*
* @hide
*/
@TestApi
- public abstract boolean isPermissionReviewModeEnabled();
+ public abstract boolean arePermissionsIndividuallyControlled();
+
+ /**
+ * Returns true if wireless consent mode is enabled
+ *
+ * @hide
+ */
+ public abstract boolean isWirelessConsentModeEnabled();
/**
* Retrieve all of the information we know about a particular group of
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 3d019f0..7a8ab60 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -16,6 +16,7 @@
package android.database;
+import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
@@ -34,6 +35,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.Collator;
@@ -216,6 +219,92 @@
}
/**
+ * Bind the given selection with the given selection arguments.
+ * <p>
+ * Internally assumes that '?' is only ever used for arguments, and doesn't
+ * appear as a literal or escaped value.
+ * <p>
+ * This method is typically useful for trusted code that needs to cook up a
+ * fully-bound selection.
+ *
+ * @hide
+ */
+ public static @Nullable String bindSelection(@Nullable String selection,
+ @Nullable Object... selectionArgs) {
+ if (selection == null) return null;
+ // If no arguments provided, so we can't bind anything
+ if (ArrayUtils.isEmpty(selectionArgs)) return selection;
+ // If no bindings requested, so we can shortcut
+ if (selection.indexOf('?') == -1) return selection;
+
+ // Track the chars immediately before and after each bind request, to
+ // decide if it needs additional whitespace added
+ char before = ' ';
+ char after = ' ';
+
+ int argIndex = 0;
+ final int len = selection.length();
+ final StringBuilder res = new StringBuilder(len);
+ for (int i = 0; i < len; ) {
+ char c = selection.charAt(i++);
+ if (c == '?') {
+ // Assume this bind request is guarded until we find a specific
+ // trailing character below
+ after = ' ';
+
+ // Sniff forward to see if the selection is requesting a
+ // specific argument index
+ int start = i;
+ for (; i < len; i++) {
+ c = selection.charAt(i);
+ if (c < '0' || c > '9') {
+ after = c;
+ break;
+ }
+ }
+ if (start != i) {
+ argIndex = Integer.parseInt(selection.substring(start, i)) - 1;
+ }
+
+ // Manually bind the argument into the selection, adding
+ // whitespace when needed for clarity
+ final Object arg = selectionArgs[argIndex++];
+ if (before != ' ' && before != '=') res.append(' ');
+ switch (DatabaseUtils.getTypeOfObject(arg)) {
+ case Cursor.FIELD_TYPE_NULL:
+ res.append("NULL");
+ break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ res.append(((Number) arg).longValue());
+ break;
+ case Cursor.FIELD_TYPE_FLOAT:
+ res.append(((Number) arg).doubleValue());
+ break;
+ case Cursor.FIELD_TYPE_BLOB:
+ throw new IllegalArgumentException("Blobs not supported");
+ case Cursor.FIELD_TYPE_STRING:
+ default:
+ if (arg instanceof Boolean) {
+ // Provide compatibility with legacy applications which may pass
+ // Boolean values in bind args.
+ res.append(((Boolean) arg).booleanValue() ? 1 : 0);
+ } else {
+ res.append('\'');
+ res.append(arg.toString());
+ res.append('\'');
+ }
+ break;
+ }
+ if (after != ' ') res.append(' ');
+ } else {
+ res.append(c);
+ before = c;
+ }
+ }
+ return res.toString();
+ }
+
+ /**
* Returns data type of the given object's value.
*<p>
* Returned values are
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index c6c676f..06560f2 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -16,17 +16,26 @@
package android.database.sqlite;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.os.Build;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.provider.BaseColumns;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@@ -34,8 +43,7 @@
* This is a convenience class that helps build SQL queries to be sent to
* {@link SQLiteDatabase} objects.
*/
-public class SQLiteQueryBuilder
-{
+public class SQLiteQueryBuilder {
private static final String TAG = "SQLiteQueryBuilder";
private static final Pattern sLimitPattern =
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
@@ -91,13 +99,10 @@
*
* @param inWhere the chunk of text to append to the WHERE clause.
*/
- public void appendWhere(CharSequence inWhere) {
+ public void appendWhere(@NonNull CharSequence inWhere) {
if (mWhereClause == null) {
mWhereClause = new StringBuilder(inWhere.length() + 16);
}
- if (mWhereClause.length() == 0) {
- mWhereClause.append('(');
- }
mWhereClause.append(inWhere);
}
@@ -111,17 +116,35 @@
* @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
* to avoid SQL injection attacks
*/
- public void appendWhereEscapeString(String inWhere) {
+ public void appendWhereEscapeString(@NonNull String inWhere) {
if (mWhereClause == null) {
mWhereClause = new StringBuilder(inWhere.length() + 16);
}
- if (mWhereClause.length() == 0) {
- mWhereClause.append('(');
- }
DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
}
/**
+ * Add a standalone chunk to the {@code WHERE} clause of this query.
+ * <p>
+ * This method differs from {@link #appendWhere(CharSequence)} in that it
+ * automatically appends {@code AND} to any existing {@code WHERE} clause
+ * already under construction before appending the given standalone
+ * expression wrapped in parentheses.
+ *
+ * @param inWhere the standalone expression to append to the {@code WHERE}
+ * clause. It will be wrapped in parentheses when it's appended.
+ */
+ public void appendWhereStandalone(@NonNull CharSequence inWhere) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ if (mWhereClause.length() > 0) {
+ mWhereClause.append(" AND ");
+ }
+ mWhereClause.append('(').append(inWhere).append(')');
+ }
+
+ /**
* Sets the projection map for the query. The projection map maps
* from column names that the caller passes into query to database
* column names. This is useful for renaming columns as well as
@@ -376,6 +399,11 @@
return null;
}
+ final String sql;
+ final String unwrappedSql = buildQuery(
+ projectionIn, selection, groupBy, having,
+ sortOrder, limit);
+
if (mStrict && selection != null && selection.length() > 0) {
// Validate the user-supplied selection to detect syntactic anomalies
// in the selection string that could indicate a SQL injection attempt.
@@ -384,25 +412,165 @@
// originally specified. An attacker cannot create an expression that
// would escape the SQL expression while maintaining balanced parentheses
// in both the wrapped and original forms.
- String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
+
+ // NOTE: The ordering of the below operations is important; we must
+ // execute the wrapped query to ensure the untrusted clause has been
+ // fully isolated.
+
+ // Validate the unwrapped query
+ db.validateSql(unwrappedSql, cancellationSignal); // will throw if query is invalid
+
+ // Execute wrapped query for extra protection
+ final String wrappedSql = buildQuery(projectionIn, wrap(selection), groupBy,
having, sortOrder, limit);
- db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid
+ sql = wrappedSql;
+ } else {
+ // Execute unwrapped query
+ sql = unwrappedSql;
}
- String sql = buildQuery(
- projectionIn, selection, groupBy, having,
- sortOrder, limit);
-
+ final String[] sqlArgs = selectionArgs;
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Performing query: " + sql);
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ } else {
+ Log.d(TAG, sql);
+ }
}
return db.rawQueryWithFactory(
- mFactory, sql, selectionArgs,
+ mFactory, sql, sqlArgs,
SQLiteDatabase.findEditTable(mTables),
cancellationSignal); // will throw if query is invalid
}
/**
+ * Perform an update by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to update on
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @return the number of rows updated
+ */
+ public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ Objects.requireNonNull(db, "No database defined");
+ Objects.requireNonNull(values, "No values defined");
+
+ final String sql;
+ final String unwrappedSql = buildUpdate(values, selection);
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+
+ // NOTE: The ordering of the below operations is important; we must
+ // execute the wrapped query to ensure the untrusted clause has been
+ // fully isolated.
+
+ // Validate the unwrapped query
+ db.validateSql(unwrappedSql, null); // will throw if query is invalid
+
+ // Execute wrapped query for extra protection
+ final String wrappedSql = buildUpdate(values, wrap(selection));
+ sql = wrappedSql;
+ } else {
+ // Execute unwrapped query
+ sql = unwrappedSql;
+ }
+
+ if (selectionArgs == null) {
+ selectionArgs = EmptyArray.STRING;
+ }
+ final ArrayMap<String, Object> rawValues = values.getValues();
+ final int valuesLength = rawValues.size();
+ final Object[] sqlArgs = new Object[valuesLength + selectionArgs.length];
+ for (int i = 0; i < sqlArgs.length; i++) {
+ if (i < valuesLength) {
+ sqlArgs[i] = rawValues.valueAt(i);
+ } else {
+ sqlArgs[i] = selectionArgs[i - valuesLength];
+ }
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ } else {
+ Log.d(TAG, sql);
+ }
+ }
+ return db.executeSql(sql, sqlArgs);
+ }
+
+ /**
+ * Perform a delete by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to delete on
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @return the number of rows deleted
+ */
+ public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ Objects.requireNonNull(db, "No database defined");
+
+ final String sql;
+ final String unwrappedSql = buildDelete(selection);
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+
+ // NOTE: The ordering of the below operations is important; we must
+ // execute the wrapped query to ensure the untrusted clause has been
+ // fully isolated.
+
+ // Validate the unwrapped query
+ db.validateSql(unwrappedSql, null); // will throw if query is invalid
+
+ // Execute wrapped query for extra protection
+ final String wrappedSql = buildDelete(wrap(selection));
+ sql = wrappedSql;
+ } else {
+ // Execute unwrapped query
+ sql = unwrappedSql;
+ }
+
+ final String[] sqlArgs = selectionArgs;
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ } else {
+ Log.d(TAG, sql);
+ }
+ }
+ return db.executeSql(sql, sqlArgs);
+ }
+
+ /**
* Construct a SELECT statement suitable for use in a group of
* SELECT statements that will be joined through UNION operators
* in buildUnionQuery.
@@ -434,28 +602,10 @@
String[] projectionIn, String selection, String groupBy,
String having, String sortOrder, String limit) {
String[] projection = computeProjection(projectionIn);
-
- StringBuilder where = new StringBuilder();
- boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
-
- if (hasBaseWhereClause) {
- where.append(mWhereClause.toString());
- where.append(')');
- }
-
- // Tack on the user's selection, if present.
- if (selection != null && selection.length() > 0) {
- if (hasBaseWhereClause) {
- where.append(" AND ");
- }
-
- where.append('(');
- where.append(selection);
- where.append(')');
- }
+ String where = computeWhere(selection);
return buildQueryString(
- mDistinct, mTables, projection, where.toString(),
+ mDistinct, mTables, projection, where,
groupBy, having, sortOrder, limit);
}
@@ -472,6 +622,42 @@
return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
}
+ /** {@hide} */
+ public String buildUpdate(ContentValues values, String selection) {
+ if (values == null || values.isEmpty()) {
+ throw new IllegalArgumentException("Empty values");
+ }
+
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("UPDATE ");
+ sql.append(mTables);
+ sql.append(" SET ");
+
+ final ArrayMap<String, Object> rawValues = values.getValues();
+ for (int i = 0; i < rawValues.size(); i++) {
+ if (i > 0) {
+ sql.append(',');
+ }
+ sql.append(rawValues.keyAt(i));
+ sql.append("=?");
+ }
+
+ final String where = computeWhere(selection);
+ appendClause(sql, " WHERE ", where);
+ return sql.toString();
+ }
+
+ /** {@hide} */
+ public String buildDelete(String selection) {
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("DELETE FROM ");
+ sql.append(mTables);
+
+ final String where = computeWhere(selection);
+ appendClause(sql, " WHERE ", where);
+ return sql.toString();
+ }
+
/**
* Construct a SELECT statement suitable for use in a group of
* SELECT statements that will be joined through UNION operators
@@ -645,4 +831,37 @@
}
return null;
}
+
+ private @Nullable String computeWhere(@Nullable String selection) {
+ final boolean hasInternal = !TextUtils.isEmpty(mWhereClause);
+ final boolean hasExternal = !TextUtils.isEmpty(selection);
+
+ if (hasInternal || hasExternal) {
+ final StringBuilder where = new StringBuilder();
+ if (hasInternal) {
+ where.append('(').append(mWhereClause).append(')');
+ }
+ if (hasInternal && hasExternal) {
+ where.append(" AND ");
+ }
+ if (hasExternal) {
+ where.append('(').append(selection).append(')');
+ }
+ return where.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Wrap given argument in parenthesis, unless it's {@code null} or
+ * {@code ()}, in which case return it verbatim.
+ */
+ private @Nullable String wrap(@Nullable String arg) {
+ if (TextUtils.isEmpty(arg)) {
+ return arg;
+ } else {
+ return "(" + arg + ")";
+ }
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteStatementBuilder.java b/core/java/android/database/sqlite/SQLiteStatementBuilder.java
deleted file mode 100644
index e2efb2f..0000000
--- a/core/java/android/database/sqlite/SQLiteStatementBuilder.java
+++ /dev/null
@@ -1,1036 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-import static android.content.ContentResolver.QUERY_ARG_SQL_GROUP_BY;
-import static android.content.ContentResolver.QUERY_ARG_SQL_HAVING;
-import static android.content.ContentResolver.QUERY_ARG_SQL_LIMIT;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SORT_ORDER;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.os.Build;
-import android.os.Bundle;
-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 dalvik.system.VMRuntime;
-
-import libcore.util.EmptyArray;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-/**
- * This is a convenience class that helps build SQL queries to be sent to
- * {@link SQLiteDatabase} objects.
- * @hide
- */
-public class SQLiteStatementBuilder {
- private static final String TAG = "SQLiteStatementBuilder";
- private static final Pattern sLimitPattern =
- Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
-
- private Map<String, String> mProjectionMap = null;
- private String mTables = "";
- private StringBuilder mWhereClause = null; // lazily created
- private String[] mWhereArgs = EmptyArray.STRING;
- private boolean mDistinct;
- private SQLiteDatabase.CursorFactory mFactory;
- private boolean mStrict;
-
- public SQLiteStatementBuilder() {
- mDistinct = false;
- mFactory = null;
- }
-
- /**
- * Mark the query as DISTINCT.
- *
- * @param distinct if true the query is DISTINCT, otherwise it isn't
- */
- public void setDistinct(boolean distinct) {
- mDistinct = distinct;
- }
-
- /**
- * Returns the list of tables being queried
- *
- * @return the list of tables being queried
- */
- public String getTables() {
- return mTables;
- }
-
- /**
- * Sets the list of tables to query. Multiple tables can be specified to perform a join.
- * For example:
- * setTables("foo, bar")
- * setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
- *
- * @param inTables the list of tables to query on
- */
- public void setTables(String inTables) {
- mTables = inTables;
- }
-
- /** {@hide} */
- public @Nullable String getWhere() {
- return (mWhereClause != null) ? mWhereClause.toString() : null;
- }
-
- /** {@hide} */
- public String[] getWhereArgs() {
- return mWhereArgs;
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like:
- *
- * <pre>
- * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- */
- public void appendWhere(@NonNull CharSequence inWhere) {
- appendWhere(inWhere, EmptyArray.STRING);
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like:
- *
- * <pre>
- * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the where clause.
- */
- public void appendWhere(@NonNull CharSequence inWhere, String... inWhereArgs) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- mWhereClause.append(inWhere);
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Append a standalone expression to the {@code WHERE} clause of this query.
- * <p>
- * This method differs from {@link #appendWhere(CharSequence)} in that it
- * automatically appends {@code AND} to any existing {@code WHERE} clause
- * already under construction before appending the given standalone
- * expression.
- *
- * @param inWhere the standalone expression to append to the {@code WHERE}
- * clause. It will be wrapped in parentheses when it's appended.
- */
- public void appendWhereExpression(@NonNull CharSequence inWhere) {
- appendWhereExpression(inWhere, EmptyArray.STRING);
- }
-
- /**
- * Append a standalone expression to the {@code WHERE} clause of this query.
- * <p>
- * This method differs from {@link #appendWhere(CharSequence)} in that it
- * automatically appends {@code AND} to any existing {@code WHERE} clause
- * already under construction before appending the given standalone
- * expression.
- *
- * @param inWhere the standalone expression to append to the {@code WHERE}
- * clause. It will be wrapped in parentheses when it's appended.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the standalone expression.
- */
- public void appendWhereExpression(@NonNull CharSequence inWhere, String... inWhereArgs) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- if (mWhereClause.length() > 0) {
- mWhereClause.append(" AND ");
- }
- mWhereClause.append('(').append(inWhere).append(')');
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like this:
- *
- * <pre>
- * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * It will be escaped to avoid SQL injection attacks.
- */
- public void appendWhereEscapeString(@NonNull String inWhere) {
- appendWhereEscapeString(inWhere, EmptyArray.STRING);
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like this:
- *
- * <pre>
- * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * It will be escaped to avoid SQL injection attacks.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the where clause.
- */
- public void appendWhereEscapeString(@NonNull String inWhere, String... inWhereArgs) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Sets the projection map for the query. The projection map maps
- * from column names that the caller passes into query to database
- * column names. This is useful for renaming columns as well as
- * disambiguating column names when doing joins. For example you
- * could map "name" to "people.name". If a projection map is set
- * it must contain all column names the user may request, even if
- * the key and value are the same.
- *
- * @param columnMap maps from the user column names to the database column names
- */
- public void setProjectionMap(Map<String, String> columnMap) {
- mProjectionMap = columnMap;
- }
-
- /**
- * Sets the cursor factory to be used for the query. You can use
- * one factory for all queries on a database but it is normally
- * easier to specify the factory when doing this query.
- *
- * @param factory the factory to use.
- */
- public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
- mFactory = factory;
- }
-
- /**
- * When set, the selection is verified against malicious arguments.
- * When using this class to create a statement using
- * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
- * non-numeric limits will raise an exception. If a projection map is specified, fields
- * not in that map will be ignored.
- * If this class is used to execute the statement directly using
- * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
- * or
- * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
- * additionally also parenthesis escaping selection are caught.
- *
- * To summarize: To get maximum protection against malicious third party apps (for example
- * content provider consumers), make sure to do the following:
- * <ul>
- * <li>Set this value to true</li>
- * <li>Use a projection map</li>
- * <li>Use one of the query overloads instead of getting the statement as a sql string</li>
- * </ul>
- * By default, this value is false.
- */
- public void setStrict(boolean strict) {
- mStrict = strict;
- }
-
- /**
- * Build an SQL query string from the given clauses.
- *
- * @param distinct true if you want each row to be unique, false otherwise.
- * @param tables The table names to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param where 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 groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return the SQL query string
- */
- public static String buildQueryString(
- boolean distinct, String tables, String[] columns, String where,
- String groupBy, String having, String orderBy, String limit) {
- if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
- throw new IllegalArgumentException(
- "HAVING clauses are only permitted when using a groupBy clause");
- }
- if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
- throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
- }
-
- StringBuilder query = new StringBuilder(120);
-
- query.append("SELECT ");
- if (distinct) {
- query.append("DISTINCT ");
- }
- if (columns != null && columns.length != 0) {
- appendColumns(query, columns);
- } else {
- query.append("* ");
- }
- query.append("FROM ");
- query.append(tables);
- appendClause(query, " WHERE ", where);
- appendClause(query, " GROUP BY ", groupBy);
- appendClause(query, " HAVING ", having);
- appendClause(query, " ORDER BY ", orderBy);
- appendClause(query, " LIMIT ", limit);
-
- return query.toString();
- }
-
- private static void appendClause(StringBuilder s, String name, String clause) {
- if (!TextUtils.isEmpty(clause)) {
- s.append(name);
- s.append(clause);
- }
- }
-
- /**
- * Add the names that are non-null in columns to s, separating
- * them with commas.
- */
- public static void appendColumns(StringBuilder s, String[] columns) {
- int n = columns.length;
-
- for (int i = 0; i < n; i++) {
- String column = columns[i];
-
- if (column != null) {
- if (i > 0) {
- s.append(", ");
- }
- s.append(column);
- }
- }
- s.append(' ');
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @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.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder) {
- return query(db, projection, selection, selectionArgs, groupBy, having, sortOrder,
- null /* limit */, null /* cancellationSignal */);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @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.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder,
- @Nullable String limit) {
- return query(db, projection, selection, selectionArgs,
- groupBy, having, sortOrder, limit, null);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @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.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String sortOrder,
- @Nullable CancellationSignal cancellationSignal) {
- return query(db, projection, selection, selectionArgs, null, null, sortOrder, null,
- cancellationSignal);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @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.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder,
- @Nullable String limit,
- @Nullable CancellationSignal cancellationSignal) {
- final Bundle queryArgs = new Bundle();
- maybePutString(queryArgs, QUERY_ARG_SQL_SELECTION, selection);
- maybePutStringArray(queryArgs, QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
- maybePutString(queryArgs, QUERY_ARG_SQL_GROUP_BY, groupBy);
- maybePutString(queryArgs, QUERY_ARG_SQL_HAVING, having);
- maybePutString(queryArgs, QUERY_ARG_SQL_SORT_ORDER, sortOrder);
- maybePutString(queryArgs, QUERY_ARG_SQL_LIMIT, limit);
- return query(db, projection, queryArgs, cancellationSignal);
- }
-
- /**
- * Perform a query by combining all current settings and the information
- * passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param queryArgs A collection of arguments for the query, defined using
- * keys such as {@link ContentResolver#QUERY_ARG_SQL_SELECTION}
- * and {@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}.
- * @param cancellationSignal A signal to cancel the operation in progress,
- * or null if none. If the operation is canceled, then
- * {@link OperationCanceledException} will be thrown when the
- * query is executed.
- * @return a cursor over the result set
- */
- public Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable Bundle queryArgs,
- @Nullable CancellationSignal cancellationSignal) {
- Objects.requireNonNull(db, "No database defined");
-
- if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
- Objects.requireNonNull(mTables, "No tables defined");
- } else if (mTables == null) {
- return null;
- }
-
- if (queryArgs == null) {
- queryArgs = Bundle.EMPTY;
- }
-
- // Final SQL that we will execute
- final String sql;
-
- final String unwrappedSql = buildQuery(projection,
- queryArgs.getString(QUERY_ARG_SQL_SELECTION),
- queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
- queryArgs.getString(QUERY_ARG_SQL_HAVING),
- queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
- queryArgs.getString(QUERY_ARG_SQL_LIMIT));
-
- 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.
-
- // TODO: decode SORT ORDER and LIMIT clauses, since they can contain
- // "expr" inside that need to be validated
-
- final String wrappedSql = buildQuery(projection,
- wrap(queryArgs.getString(QUERY_ARG_SQL_SELECTION)),
- queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
- queryArgs.getString(QUERY_ARG_SQL_HAVING),
- queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
- queryArgs.getString(QUERY_ARG_SQL_LIMIT));
-
- // Validate the unwrapped query
- db.validateSql(unwrappedSql, cancellationSignal);
-
- // Execute wrapped query for extra protection
- sql = wrappedSql;
- } else {
- // Execute unwrapped query
- sql = unwrappedSql;
- }
-
- final String[] sqlArgs = ArrayUtils.concat(String.class,
- queryArgs.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS), mWhereArgs);
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- return db.rawQueryWithFactory(
- mFactory, sql, sqlArgs,
- SQLiteDatabase.findEditTable(mTables),
- cancellationSignal); // will throw if query is invalid
- }
-
- /**
- * Perform an update by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to update on
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @return the number of rows updated
- */
- public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
- @Nullable String selection, @Nullable String[] selectionArgs) {
- Objects.requireNonNull(mTables, "No tables defined");
- Objects.requireNonNull(db, "No database defined");
- Objects.requireNonNull(values, "No values defined");
-
- 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.
- final String sql = buildUpdate(values, wrap(selection));
- db.validateSql(sql, null); // will throw if query is invalid
- }
-
- 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 sql = buildUpdate(values, selection);
- final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs,
- ArrayUtils.concat(String.class, selectionArgs, mWhereArgs));
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- return db.executeSql(sql, sqlArgs);
- }
-
- /**
- * Perform a delete by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to delete on
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @return the number of rows deleted
- */
- public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
- @Nullable String[] selectionArgs) {
- Objects.requireNonNull(mTables, "No tables defined");
- Objects.requireNonNull(db, "No database defined");
-
- 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.
- final String sql = buildDelete(wrap(selection));
- db.validateSql(sql, null); // will throw if query is invalid
- }
-
- final String sql = buildDelete(selection);
- final String[] sqlArgs = ArrayUtils.concat(String.class, selectionArgs, mWhereArgs);
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- 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.
- *
- * @param projectionIn A list of which columns to return. Passing
- * null will return all columns, which is discouraged to
- * prevent reading data from storage that isn't going to be
- * used.
- * @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 groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY itself).
- * Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return the resulting SQL SELECT statement
- */
- public String buildQuery(
- String[] projectionIn, String selection, String groupBy,
- String having, String sortOrder, String limit) {
- String[] projection = computeProjection(projectionIn);
- String where = computeWhere(selection);
-
- return buildQueryString(
- mDistinct, mTables, projection, where,
- groupBy, having, sortOrder, limit);
- }
-
- /**
- * @deprecated This method's signature is misleading since no SQL parameter
- * substitution is carried out. The selection arguments parameter does not get
- * used at all. To avoid confusion, call
- * {@link #buildQuery(String[], String, String, String, String, String)} instead.
- */
- @Deprecated
- public String buildQuery(
- String[] projectionIn, String selection, String[] selectionArgs,
- String groupBy, String having, String sortOrder, String limit) {
- 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
- * in buildUnionQuery.
- *
- * @param typeDiscriminatorColumn the name of the result column
- * whose cells will contain the name of the table from which
- * each row was drawn.
- * @param unionColumns the names of the columns to appear in the
- * result. This may include columns that do not appear in the
- * table this SELECT is querying (i.e. mTables), but that do
- * appear in one of the other tables in the UNION query that we
- * are constructing.
- * @param columnsPresentInTable a Set of the names of the columns
- * that appear in this table (i.e. in the table whose name is
- * mTables). Since columns in unionColumns include columns that
- * appear only in other tables, we use this array to distinguish
- * which ones actually are present. Other columns will have
- * NULL values for results from this subquery.
- * @param computedColumnsOffset all columns in unionColumns before
- * this index are included under the assumption that they're
- * computed and therefore won't appear in columnsPresentInTable,
- * e.g. "date * 1000 as normalized_date"
- * @param typeDiscriminatorValue the value used for the
- * type-discriminator column in this subquery
- * @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 groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY itself).
- * Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @return the resulting SQL SELECT statement
- */
- public String buildUnionSubQuery(
- String typeDiscriminatorColumn,
- String[] unionColumns,
- Set<String> columnsPresentInTable,
- int computedColumnsOffset,
- String typeDiscriminatorValue,
- String selection,
- String groupBy,
- String having) {
- int unionColumnsCount = unionColumns.length;
- String[] projectionIn = new String[unionColumnsCount];
-
- for (int i = 0; i < unionColumnsCount; i++) {
- String unionColumn = unionColumns[i];
-
- if (unionColumn.equals(typeDiscriminatorColumn)) {
- projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
- + typeDiscriminatorColumn;
- } else if (i <= computedColumnsOffset
- || columnsPresentInTable.contains(unionColumn)) {
- projectionIn[i] = unionColumn;
- } else {
- projectionIn[i] = "NULL AS " + unionColumn;
- }
- }
- return buildQuery(
- projectionIn, selection, groupBy, having,
- null /* sortOrder */,
- null /* limit */);
- }
-
- /**
- * @deprecated This method's signature is misleading since no SQL parameter
- * substitution is carried out. The selection arguments parameter does not get
- * used at all. To avoid confusion, call
- * {@link #buildUnionSubQuery}
- * instead.
- */
- @Deprecated
- public String buildUnionSubQuery(
- String typeDiscriminatorColumn,
- String[] unionColumns,
- Set<String> columnsPresentInTable,
- int computedColumnsOffset,
- String typeDiscriminatorValue,
- String selection,
- String[] selectionArgs,
- String groupBy,
- String having) {
- return buildUnionSubQuery(
- typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
- computedColumnsOffset, typeDiscriminatorValue, selection,
- groupBy, having);
- }
-
- /**
- * Given a set of subqueries, all of which are SELECT statements,
- * construct a query that returns the union of what those
- * subqueries return.
- * @param subQueries an array of SQL SELECT statements, all of
- * which must have the same columns as the same positions in
- * their results
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing
- * null will use the default sort order, which may be unordered.
- * @param limit The limit clause, which applies to the entire union result set
- *
- * @return the resulting SQL SELECT statement
- */
- public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
- StringBuilder query = new StringBuilder(128);
- int subQueryCount = subQueries.length;
- String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
-
- for (int i = 0; i < subQueryCount; i++) {
- if (i > 0) {
- query.append(unionOperator);
- }
- query.append(subQueries[i]);
- }
- appendClause(query, " ORDER BY ", sortOrder);
- appendClause(query, " LIMIT ", limit);
- return query.toString();
- }
-
- private @Nullable String[] computeProjection(@Nullable String[] projectionIn) {
- if (projectionIn != null && projectionIn.length > 0) {
- if (mProjectionMap != null) {
- String[] projection = new String[projectionIn.length];
- int length = projectionIn.length;
-
- for (int i = 0; i < length; i++) {
- String userColumn = projectionIn[i];
- String column = mProjectionMap.get(userColumn);
-
- if (column != null) {
- projection[i] = column;
- continue;
- }
-
- if (!mStrict &&
- ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
- /* A column alias already exist */
- projection[i] = userColumn;
- continue;
- }
-
- throw new IllegalArgumentException("Invalid column "
- + projectionIn[i] + " from tables " + mTables);
- }
- return projection;
- } else {
- return projectionIn;
- }
- } else if (mProjectionMap != null) {
- // Return all columns in projection map.
- Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
- String[] projection = new String[entrySet.size()];
- Iterator<Entry<String, String>> entryIter = entrySet.iterator();
- int i = 0;
-
- while (entryIter.hasNext()) {
- Entry<String, String> entry = entryIter.next();
-
- // Don't include the _count column when people ask for no projection.
- if (entry.getKey().equals(BaseColumns._COUNT)) {
- continue;
- }
- projection[i++] = entry.getValue();
- }
- return projection;
- }
- return null;
- }
-
- private @NonNull String computeWhere(@Nullable String selection) {
- final boolean hasUser = selection != null && selection.length() > 0;
- final boolean hasInternal = mWhereClause != null && mWhereClause.length() > 0;
-
- if (hasUser || hasInternal) {
- final StringBuilder where = new StringBuilder();
- if (hasUser) {
- where.append('(').append(selection).append(')');
- }
- if (hasUser && hasInternal) {
- where.append(" AND ");
- }
- if (hasInternal) {
- where.append('(').append(mWhereClause.toString()).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 (arg == null) {
- return null;
- } else if (arg.equals("")) {
- return arg;
- } else {
- return "(" + arg + ")";
- }
- }
-
- private static void maybePutString(@NonNull Bundle bundle, @NonNull String key,
- @Nullable String value) {
- if (value != null) {
- bundle.putString(key, value);
- }
- }
-
- private static void maybePutStringArray(@NonNull Bundle bundle, @NonNull String key,
- @Nullable String[] value) {
- if (value != null) {
- bundle.putStringArray(key, value);
- }
- }
-}
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/net/Uri.java b/core/java/android/net/Uri.java
index 0fb84b7..b7f5cdf 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -24,8 +24,6 @@
import android.os.StrictMode;
import android.util.Log;
-import libcore.net.UriCodec;
-
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -1959,7 +1957,8 @@
if (s == null) {
return null;
}
- return UriCodec.decode(s, false, StandardCharsets.UTF_8, false);
+ return UriCodec.decode(
+ s, false /* convertPlus */, StandardCharsets.UTF_8, false /* throwOnFailure */);
}
/**
diff --git a/core/java/android/net/UriCodec.java b/core/java/android/net/UriCodec.java
new file mode 100644
index 0000000..e1470e0
--- /dev/null
+++ b/core/java/android/net/UriCodec.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Decodes “application/x-www-form-urlencoded” content.
+ *
+ * @hide
+ */
+public final class UriCodec {
+
+ private UriCodec() {}
+
+ /**
+ * Interprets a char as hex digits, returning a number from -1 (invalid char) to 15 ('f').
+ */
+ private static int hexCharToValue(char c) {
+ if ('0' <= c && c <= '9') {
+ return c - '0';
+ }
+ if ('a' <= c && c <= 'f') {
+ return 10 + c - 'a';
+ }
+ if ('A' <= c && c <= 'F') {
+ return 10 + c - 'A';
+ }
+ return -1;
+ }
+
+ private static URISyntaxException unexpectedCharacterException(
+ String uri, String name, char unexpected, int index) {
+ String nameString = (name == null) ? "" : " in [" + name + "]";
+ return new URISyntaxException(
+ uri, "Unexpected character" + nameString + ": " + unexpected, index);
+ }
+
+ private static char getNextCharacter(String uri, int index, int end, String name)
+ throws URISyntaxException {
+ if (index >= end) {
+ String nameString = (name == null) ? "" : " in [" + name + "]";
+ throw new URISyntaxException(
+ uri, "Unexpected end of string" + nameString, index);
+ }
+ return uri.charAt(index);
+ }
+
+ /**
+ * Decode a string according to the rules of this decoder.
+ *
+ * - if {@code convertPlus == true} all ‘+’ chars in the decoded output are converted to ‘ ‘
+ * (white space)
+ * - if {@code throwOnFailure == true}, an {@link IllegalArgumentException} is thrown for
+ * invalid inputs. Else, U+FFFd is emitted to the output in place of invalid input octets.
+ */
+ public static String decode(
+ String s, boolean convertPlus, Charset charset, boolean throwOnFailure) {
+ StringBuilder builder = new StringBuilder(s.length());
+ appendDecoded(builder, s, convertPlus, charset, throwOnFailure);
+ return builder.toString();
+ }
+
+ /**
+ * Character to be output when there's an error decoding an input.
+ */
+ private static final char INVALID_INPUT_CHARACTER = '\ufffd';
+
+ private static void appendDecoded(
+ StringBuilder builder,
+ String s,
+ boolean convertPlus,
+ Charset charset,
+ boolean throwOnFailure) {
+ CharsetDecoder decoder = charset.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .replaceWith("\ufffd")
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ // Holds the bytes corresponding to the escaped chars being read (empty if the last char
+ // wasn't a escaped char).
+ ByteBuffer byteBuffer = ByteBuffer.allocate(s.length());
+ int i = 0;
+ while (i < s.length()) {
+ char c = s.charAt(i);
+ i++;
+ switch (c) {
+ case '+':
+ flushDecodingByteAccumulator(
+ builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(convertPlus ? ' ' : '+');
+ break;
+ case '%':
+ // Expect two characters representing a number in hex.
+ byte hexValue = 0;
+ for (int j = 0; j < 2; j++) {
+ try {
+ c = getNextCharacter(s, i, s.length(), null /* name */);
+ } catch (URISyntaxException e) {
+ // Unexpected end of input.
+ if (throwOnFailure) {
+ throw new IllegalArgumentException(e);
+ } else {
+ flushDecodingByteAccumulator(
+ builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(INVALID_INPUT_CHARACTER);
+ return;
+ }
+ }
+ i++;
+ int newDigit = hexCharToValue(c);
+ if (newDigit < 0) {
+ if (throwOnFailure) {
+ throw new IllegalArgumentException(
+ unexpectedCharacterException(s, null /* name */, c, i - 1));
+ } else {
+ flushDecodingByteAccumulator(
+ builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(INVALID_INPUT_CHARACTER);
+ break;
+ }
+ }
+ hexValue = (byte) (hexValue * 0x10 + newDigit);
+ }
+ byteBuffer.put(hexValue);
+ break;
+ default:
+ flushDecodingByteAccumulator(builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(c);
+ }
+ }
+ flushDecodingByteAccumulator(builder, decoder, byteBuffer, throwOnFailure);
+ }
+
+ private static void flushDecodingByteAccumulator(
+ StringBuilder builder,
+ CharsetDecoder decoder,
+ ByteBuffer byteBuffer,
+ boolean throwOnFailure) {
+ if (byteBuffer.position() == 0) {
+ return;
+ }
+ byteBuffer.flip();
+ try {
+ builder.append(decoder.decode(byteBuffer));
+ } catch (CharacterCodingException e) {
+ if (throwOnFailure) {
+ throw new IllegalArgumentException(e);
+ } else {
+ builder.append(INVALID_INPUT_CHARACTER);
+ }
+ } finally {
+ // Use the byte buffer to write again.
+ byteBuffer.flip();
+ byteBuffer.limit(byteBuffer.capacity());
+ }
+ }
+}
diff --git a/core/java/android/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/os/Build.java b/core/java/android/os/Build.java
index e71f4e9..25a5e91 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1114,8 +1114,7 @@
* @removed
*/
@SystemApi
- public static final boolean PERMISSIONS_REVIEW_REQUIRED =
- SystemProperties.getInt("ro.permission_review_required", 0) == 1;
+ public static final boolean PERMISSIONS_REVIEW_REQUIRED = true;
/**
* Returns the version string for the radio firmware. May return
diff --git a/core/java/android/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/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index b6c6bdc..5a7a83f 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -151,14 +151,14 @@
@Override
public String toString() {
return new StringBuilder(Condition.class.getSimpleName()).append('[')
- .append("id=").append(id)
- .append(",summary=").append(summary)
- .append(",line1=").append(line1)
- .append(",line2=").append(line2)
- .append(",icon=").append(icon)
- .append(",state=").append(stateToString(state))
- .append(",flags=").append(flags)
- .append(']').toString();
+ .append("state=").append(stateToString(state))
+ .append(",id=").append(id)
+ .append(",summary=").append(summary)
+ .append(",line1=").append(line1)
+ .append(",line2=").append(line2)
+ .append(",icon=").append(icon)
+ .append(",flags=").append(flags)
+ .append(']').toString();
}
/** @hide */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f52234d..5ac36afc 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -240,11 +240,29 @@
.append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
.append(",suppressedVisualEffects=").append(suppressedVisualEffects)
.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
- .append(",automaticRules=").append(automaticRules)
- .append(",manualRule=").append(manualRule)
+ .append(",\nautomaticRules=").append(rulesToString())
+ .append(",\nmanualRule=").append(manualRule)
.append(']').toString();
}
+ private String rulesToString() {
+ if (automaticRules.isEmpty()) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(automaticRules.size() * 28);
+ buffer.append('{');
+ for (int i = 0; i < automaticRules.size(); i++) {
+ if (i > 0) {
+ buffer.append(",\n");
+ }
+ Object value = automaticRules.valueAt(i);
+ buffer.append(value);
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+
private Diff diff(ZenModeConfig to) {
final Diff d = new Diff();
if (to == null) {
@@ -1027,10 +1045,10 @@
public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
final boolean isSchedule = conditionId != null
- && conditionId.getScheme().equals(Condition.SCHEME)
- && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+ && Condition.SCHEME.equals(conditionId.getScheme())
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
&& conditionId.getPathSegments().size() == 1
- && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
+ && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0));
if (!isSchedule) return null;
final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
@@ -1128,10 +1146,10 @@
public static EventInfo tryParseEventConditionId(Uri conditionId) {
final boolean isEvent = conditionId != null
- && conditionId.getScheme().equals(Condition.SCHEME)
- && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+ && Condition.SCHEME.equals(conditionId.getScheme())
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
&& conditionId.getPathSegments().size() == 1
- && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
+ && EVENT_PATH.equals(conditionId.getPathSegments().get(0));
if (!isEvent) return null;
final EventInfo rt = new EventInfo();
rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
@@ -1340,14 +1358,14 @@
@Override
public String toString() {
return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
- .append("enabled=").append(enabled)
+ .append("id=").append(id)
+ .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
.append(",snoozing=").append(snoozing)
.append(",name=").append(name)
.append(",zenMode=").append(Global.zenModeToString(zenMode))
.append(",conditionId=").append(conditionId)
.append(",condition=").append(condition)
.append(",component=").append(component)
- .append(",id=").append(id)
.append(",creationTime=").append(creationTime)
.append(",enabler=").append(enabler)
.append(']').toString();
@@ -1479,7 +1497,7 @@
final int N = lines.size();
for (int i = 0; i < N; i++) {
if (i > 0) {
- sb.append(',');
+ sb.append(",\n");
}
sb.append(lines.get(i));
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c2c3182..9bf8cd2 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -30,10 +30,6 @@
import android.text.style.ReplacementSpan;
import android.util.Pools.SynchronizedPool;
-import dalvik.annotation.optimization.CriticalNative;
-
-import libcore.util.NativeAllocationRegistry;
-
import java.util.Arrays;
/**
@@ -62,9 +58,6 @@
public class MeasuredParagraph {
private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
- private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
private MeasuredParagraph() {} // Use build static functions instead.
private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
@@ -128,24 +121,7 @@
private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
// The native MeasuredParagraph.
- // See getNativePtr comments.
- // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
- private /* Maybe Zero */ long mNativePtr = 0;
- private @Nullable Runnable mNativeObjectCleaner;
-
- // Associate the native object to this Java object.
- private void bindNativeObject(/* Non Zero*/ long nativePtr) {
- mNativePtr = nativePtr;
- mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
- }
-
- // Decouple the native object from this Java object and release the native object.
- private void unbindNativeObject() {
- if (mNativePtr != 0) {
- mNativeObjectCleaner.run();
- mNativePtr = 0;
- }
- }
+ private @Nullable NativeMeasuredParagraph mNativeMeasuredParagraph;
// Following two objects are for avoiding object allocation.
private @NonNull TextPaint mCachedPaint = new TextPaint();
@@ -173,7 +149,7 @@
mWidths.clear();
mFontMetrics.clear();
mSpanEndCache.clear();
- unbindNativeObject();
+ mNativeMeasuredParagraph = null;
}
/**
@@ -267,10 +243,10 @@
* Returns the native ptr of the MeasuredParagraph.
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
- * Returns 0 in other cases.
+ * Returns null in other cases.
*/
- public /* Maybe Zero */ long getNativePtr() {
- return mNativePtr;
+ public NativeMeasuredParagraph getNativeMeasuredParagraph() {
+ return mNativeMeasuredParagraph;
}
/**
@@ -283,7 +259,7 @@
* @param end the exclusive end offset of the target region in the text
*/
public float getWidth(int start, int end) {
- if (mNativePtr == 0) {
+ if (mNativeMeasuredParagraph == null) {
// We have result in Java.
final float[] widths = mWidths.getRawArray();
float r = 0.0f;
@@ -293,7 +269,7 @@
return r;
} else {
// We have result in native.
- return nGetWidth(mNativePtr, start, end);
+ return mNativeMeasuredParagraph.getWidth(start, end);
}
}
@@ -305,7 +281,16 @@
*/
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect bounds) {
- nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
+ mNativeMeasuredParagraph.getBounds(mCopiedBuffer, start, end, bounds);
+ }
+
+ /**
+ * Returns a width of the character at the offset.
+ *
+ * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ */
+ public float getCharWidthAt(@IntRange(from = 0) int offset) {
+ return mNativeMeasuredParagraph.getCharWidthAt(offset);
}
/**
@@ -364,7 +349,7 @@
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
mt.applyMetricsAffectingSpan(
- paint, null /* spans */, start, end, 0 /* native static layout ptr */);
+ paint, null /* spans */, start, end, null /* native builder ptr */);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
int spanEnd;
@@ -374,7 +359,7 @@
MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
mt.applyMetricsAffectingSpan(
- paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+ paint, spans, spanStart, spanEnd, null /* native builder ptr */);
}
}
return mt;
@@ -406,25 +391,16 @@
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
+ final NativeMeasuredParagraph.Builder builder = new NativeMeasuredParagraph.Builder();
if (mt.mTextLength == 0) {
// Need to build empty native measured text for StaticLayout.
// TODO: Stop creating empty measured text for empty lines.
- long nativeBuilderPtr = nInitBuilder();
- try {
- mt.bindNativeObject(
- nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
- computeHyphenation, computeLayout));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
- }
- return mt;
- }
-
- long nativeBuilderPtr = nInitBuilder();
- try {
+ mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+ computeLayout);
+ } else {
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
+ mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, builder);
mt.mSpanEndCache.append(end);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply
@@ -437,15 +413,12 @@
MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
MetricAffectingSpan.class);
- mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
- nativeBuilderPtr);
+ mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, builder);
mt.mSpanEndCache.append(spanEnd);
}
}
- mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
- computeHyphenation, computeLayout));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
+ mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+ computeLayout);
}
return mt;
@@ -517,13 +490,13 @@
private void applyReplacementRun(@NonNull ReplacementSpan replacement,
@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
// Use original text. Shouldn't matter.
// TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
// backward compatibility? or Should we initialize them for getFontMetricsInt?
final float width = replacement.getSize(
mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
// Assigns all width to the first character. This is the same behavior as minikin.
mWidths.set(start, width);
if (end > start + 1) {
@@ -531,24 +504,22 @@
}
mWholeWidth += width;
} else {
- nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- width);
+ builder.addReplacementRun(mCachedPaint, start, end, width);
}
}
private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
if (mLtrWithoutBidi) {
// If the whole text is LTR direction, just apply whole region.
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
mWholeWidth += mCachedPaint.getTextRunAdvances(
mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
mWidths.getRawArray(), start);
} else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- false /* isRtl */);
+ builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
}
} else {
// If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -558,14 +529,13 @@
for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point
final boolean isRtl = (level & 0x1) != 0;
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
final int levelLength = levelEnd - levelStart;
mWholeWidth += mCachedPaint.getTextRunAdvances(
mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
isRtl, mWidths.getRawArray(), levelStart);
} else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
- levelEnd, isRtl);
+ builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
}
if (levelEnd == end) {
break;
@@ -582,12 +552,12 @@
@Nullable MetricAffectingSpan[] spans,
@IntRange(from = 0) int start, // inclusive, in original text buffer
@IntRange(from = 0) int end, // exclusive, in original text buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
mCachedPaint.set(paint);
// XXX paint should not have a baseline shift, but...
mCachedPaint.baselineShift = 0;
- final boolean needFontMetrics = nativeBuilderPtr != 0;
+ final boolean needFontMetrics = builder != null;
if (needFontMetrics && mCachedFm == null) {
mCachedFm = new Paint.FontMetricsInt();
@@ -610,15 +580,14 @@
final int startInCopiedBuffer = start - mTextStart;
final int endInCopiedBuffer = end - mTextStart;
- if (nativeBuilderPtr != 0) {
+ if (builder != null) {
mCachedPaint.getFontMetricsInt(mCachedFm);
}
if (replacement != null) {
- applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
- nativeBuilderPtr);
+ applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, builder);
} else {
- applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+ applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, builder);
}
if (needFontMetrics) {
@@ -689,59 +658,6 @@
* This only works if the MeasuredParagraph is computed with buildForStaticLayout.
*/
public @IntRange(from = 0) int getMemoryUsage() {
- return nGetMemoryUsage(mNativePtr);
+ return mNativeMeasuredParagraph.getMemoryUsage();
}
-
- private static native /* Non Zero */ long nInitBuilder();
-
- /**
- * Apply style to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param isRtl True if the text is RTL.
- */
- private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- boolean isRtl);
-
- /**
- * Apply ReplacementRun to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param width The width of the replacement.
- */
- private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @FloatRange(from = 0) float width);
-
- private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
- @NonNull char[] text,
- boolean computeHyphenation,
- boolean computeLayout);
-
- private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
-
- @CriticalNative
- private static native float nGetWidth(/* Non Zero */ long nativePtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end);
-
- @CriticalNative
- private static native /* Non Zero */ long nGetReleaseFunc();
-
- @CriticalNative
- private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
-
- private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
- Rect rect);
}
diff --git a/core/java/android/text/NativeLineBreaker.java b/core/java/android/text/NativeLineBreaker.java
new file mode 100644
index 0000000..a31b336
--- /dev/null
+++ b/core/java/android/text/NativeLineBreaker.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * A native implementation of the line breaker.
+ * TODO: Consider to make this class public.
+ * @hide
+ */
+public class NativeLineBreaker {
+
+ /**
+ * A result object of a line breaking
+ */
+ public static class LineBreaks {
+ public int breakCount;
+ private static final int INITIAL_SIZE = 16;
+ public int[] breaks = new int[INITIAL_SIZE];
+ public float[] widths = new float[INITIAL_SIZE];
+ public float[] ascents = new float[INITIAL_SIZE];
+ public float[] descents = new float[INITIAL_SIZE];
+ public int[] flags = new int[INITIAL_SIZE];
+ // breaks, widths, and flags should all have the same length
+ }
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ NativeLineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);
+
+ private final long mNativePtr;
+
+ /**
+ * A constructor of NativeLineBreaker
+ */
+ public NativeLineBreaker(@Layout.BreakStrategy int breakStrategy,
+ @Layout.HyphenationFrequency int hyphenationFrequency,
+ boolean justify, @Nullable int[] indents) {
+ mNativePtr = nInit(breakStrategy, hyphenationFrequency, justify, indents);
+ sRegistry.registerNativeAllocation(this, mNativePtr);
+ }
+
+ /**
+ * Break text into lines
+ *
+ * @param chars an array of characters
+ * @param measuredPara a result of the text measurement
+ * @param length a length of the target text from the begining
+ * @param firstWidth a width of the first width of the line in this paragraph
+ * @param firstWidthLineCount a number of lines that has the length of the firstWidth
+ * @param restWidth a width of the rest of the lines.
+ * @param variableTabStops an array of tab stop widths
+ * @param defaultTabStop a width of the tab stop
+ * @param indentsOffset an offset of the indents to be used.
+ * @param out output buffer
+ * @return a number of the lines
+ */
+ @NonNull public int computeLineBreaks(
+ @NonNull char[] chars,
+ @NonNull NativeMeasuredParagraph measuredPara,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ @Nullable int[] variableTabStops,
+ int defaultTabStop,
+ @IntRange(from = 0) int indentsOffset,
+ @NonNull LineBreaks out) {
+ return nComputeLineBreaks(
+ mNativePtr,
+
+ // Inputs
+ chars,
+ measuredPara.getNativePtr(),
+ length,
+ firstWidth,
+ firstWidthLineCount,
+ restWidth,
+ variableTabStops,
+ defaultTabStop,
+ indentsOffset,
+
+ // Outputs
+ out,
+ out.breaks.length,
+ out.breaks,
+ out.widths,
+ out.ascents,
+ out.descents,
+ out.flags);
+
+ }
+
+ @FastNative
+ private static native long nInit(
+ @Layout.BreakStrategy int breakStrategy,
+ @Layout.HyphenationFrequency int hyphenationFrequency,
+ boolean isJustified,
+ @Nullable int[] indents);
+
+ @CriticalNative
+ private static native long nGetReleaseFunc();
+
+ // populates LineBreaks and returns the number of breaks found
+ //
+ // the arrays inside the LineBreaks objects are passed in as well
+ // to reduce the number of JNI calls in the common case where the
+ // arrays do not have to be resized
+ // The individual character widths will be returned in charWidths. The length of
+ // charWidths must be at least the length of the text.
+ private static native int nComputeLineBreaks(
+ /* non zero */ long nativePtr,
+
+ // Inputs
+ @NonNull char[] text,
+ /* Non Zero */ long measuredTextPtr,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ @Nullable int[] variableTabStops,
+ int defaultTabStop,
+ @IntRange(from = 0) int indentsOffset,
+
+ // Outputs
+ @NonNull LineBreaks recycle,
+ @IntRange(from = 0) int recycleLength,
+ @NonNull int[] recycleBreaks,
+ @NonNull float[] recycleWidths,
+ @NonNull float[] recycleAscents,
+ @NonNull float[] recycleDescents,
+ @NonNull int[] recycleFlags);
+}
diff --git a/core/java/android/text/NativeMeasuredParagraph.java b/core/java/android/text/NativeMeasuredParagraph.java
new file mode 100644
index 0000000..d03674f
--- /dev/null
+++ b/core/java/android/text/NativeMeasuredParagraph.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * A native implementation of measured paragraph.
+ * TODO: Consider to make this class public.
+ * @hide
+ */
+public class NativeMeasuredParagraph {
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ NativeMeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
+ private long mNativePtr;
+
+ // Use builder instead.
+ private NativeMeasuredParagraph(long ptr) {
+ mNativePtr = ptr;
+ }
+
+ /**
+ * Returns a width of the given region
+ */
+ public float getWidth(int start, int end) {
+ return nGetWidth(mNativePtr, start, end);
+ }
+
+ /**
+ * Returns a memory usage of the native object.
+ */
+ public int getMemoryUsage() {
+ return nGetMemoryUsage(mNativePtr);
+ }
+
+ /**
+ * Fills the boundary box of the given region
+ */
+ public void getBounds(char[] buf, int start, int end, Rect rect) {
+ nGetBounds(mNativePtr, buf, start, end, rect);
+ }
+
+ /**
+ * Returns the width of the character at the given offset
+ */
+ public float getCharWidthAt(int offset) {
+ return nGetCharWidthAt(mNativePtr, offset);
+ }
+
+ /**
+ * Returns a native pointer of the underlying native object.
+ */
+ public long getNativePtr() {
+ return mNativePtr;
+ }
+
+ @CriticalNative
+ private static native float nGetWidth(/* Non Zero */ long nativePtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end);
+
+ @CriticalNative
+ private static native /* Non Zero */ long nGetReleaseFunc();
+
+ @CriticalNative
+ private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+ private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
+ Rect rect);
+
+ @CriticalNative
+ private static native float nGetCharWidthAt(long nativePtr, int offset);
+
+ /**
+ * A builder for the NativeMeasuredParagraph
+ */
+ public static class Builder {
+ private final long mNativePtr;
+
+ public Builder() {
+ mNativePtr = nInitBuilder();
+ }
+
+ /**
+ * Apply styles to given range
+ */
+ public void addStyleRun(@NonNull Paint paint, int start, int end, boolean isRtl) {
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
+ }
+
+ /**
+ * Tells native that the given range is replaced with the object of given width.
+ */
+ public void addReplacementRun(@NonNull Paint paint, int start, int end, float width) {
+ nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
+ }
+
+ /**
+ * Build the NativeMeasuredParagraph
+ */
+ public NativeMeasuredParagraph build(char[] text, boolean computeHyphenation,
+ boolean computeLayout) {
+ try {
+ long ptr = nBuildNativeMeasuredParagraph(mNativePtr, text, computeHyphenation,
+ computeLayout);
+ NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr);
+ sRegistry.registerNativeAllocation(res, ptr);
+ return res;
+ } finally {
+ nFreeBuilder(mNativePtr);
+ }
+ }
+
+ private static native /* Non Zero */ long nInitBuilder();
+
+ /**
+ * Apply style to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param isRtl True if the text is RTL.
+ */
+ private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ boolean isRtl);
+ /**
+ * Apply ReplacementRun to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param width The width of the replacement.
+ */
+ private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @FloatRange(from = 0) float width);
+
+ private static native long nBuildNativeMeasuredParagraph(
+ /* Non Zero */ long nativeBuilderPtr,
+ @NonNull char[] text,
+ boolean computeHyphenation,
+ boolean computeLayout);
+
+ private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+ }
+}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 027ead3..b7ea012 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -518,6 +518,21 @@
}
/**
+ * Returns a width of a character at offset
+ *
+ * @param offset an offset of the text.
+ * @return a width of the character.
+ * @hide
+ */
+ public float getCharWidthAt(@IntRange(from = 0) int offset) {
+ Preconditions.checkArgument(0 <= offset && offset < mText.length(), "invalid offset");
+ final int paraIndex = findParaIndex(offset);
+ final int paraStart = getParagraphStart(paraIndex);
+ final int paraEnd = getParagraphEnd(paraIndex);
+ return getMeasuredParagraph(paraIndex).getCharWidthAt(offset - paraStart);
+ }
+
+ /**
* Returns the size of native PrecomputedText memory usage.
*
* Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 4b78aa2..6dad238 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
-import android.text.AutoGrowArray.FloatArray;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
@@ -32,9 +31,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
import java.util.Arrays;
/**
@@ -57,7 +53,7 @@
*
* - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
* native.
- * - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
+ * - Run NativeLineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
*/
@@ -586,8 +582,7 @@
float ellipsizedWidth = b.mEllipsizedWidth;
TextUtils.TruncateAt ellipsize = b.mEllipsize;
final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
- LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs
- FloatArray widths = new FloatArray();
+ NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks();
mLineCount = 0;
mEllipsized = false;
@@ -615,7 +610,7 @@
indents = null;
}
- final long nativePtr = nInit(
+ final NativeLineBreaker lineBreaker = new NativeLineBreaker(
b.mBreakStrategy, b.mHyphenationFrequency,
// TODO: Support more justification mode, e.g. letter spacing, stretching.
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
@@ -639,243 +634,219 @@
bufEnd, false /* computeLayout */);
}
- try {
- for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
- final int paraStart = paraIndex == 0
- ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
- final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
+ for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
+ final int paraStart = paraIndex == 0
+ ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
+ final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
- int firstWidthLineCount = 1;
- int firstWidth = outerWidth;
- int restWidth = outerWidth;
+ int firstWidthLineCount = 1;
+ int firstWidth = outerWidth;
+ int restWidth = outerWidth;
- LineHeightSpan[] chooseHt = null;
+ LineHeightSpan[] chooseHt = null;
+ if (spanned != null) {
+ LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
+ LeadingMarginSpan.class);
+ for (int i = 0; i < sp.length; i++) {
+ LeadingMarginSpan lms = sp[i];
+ firstWidth -= sp[i].getLeadingMargin(true);
+ restWidth -= sp[i].getLeadingMargin(false);
- if (spanned != null) {
- LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
- LeadingMarginSpan.class);
- for (int i = 0; i < sp.length; i++) {
- LeadingMarginSpan lms = sp[i];
- firstWidth -= sp[i].getLeadingMargin(true);
- restWidth -= sp[i].getLeadingMargin(false);
+ // LeadingMarginSpan2 is odd. The count affects all
+ // leading margin spans, not just this particular one
+ if (lms instanceof LeadingMarginSpan2) {
+ LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
+ firstWidthLineCount = Math.max(firstWidthLineCount,
+ lms2.getLeadingMarginLineCount());
+ }
+ }
- // LeadingMarginSpan2 is odd. The count affects all
- // leading margin spans, not just this particular one
- if (lms instanceof LeadingMarginSpan2) {
- LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
- firstWidthLineCount = Math.max(firstWidthLineCount,
- lms2.getLeadingMarginLineCount());
- }
+ chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
+
+ if (chooseHt.length == 0) {
+ chooseHt = null; // So that out() would not assume it has any contents
+ } else {
+ if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
+ chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
}
- chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
+ for (int i = 0; i < chooseHt.length; i++) {
+ int o = spanned.getSpanStart(chooseHt[i]);
- if (chooseHt.length == 0) {
- chooseHt = null; // So that out() would not assume it has any contents
+ if (o < paraStart) {
+ // starts in this layout, before the
+ // current paragraph
+
+ chooseHtv[i] = getLineTop(getLineForOffset(o));
+ } else {
+ // starts in this paragraph
+
+ chooseHtv[i] = v;
+ }
+ }
+ }
+ }
+ // tab stop locations
+ int[] variableTabStops = null;
+ if (spanned != null) {
+ TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+ paraEnd, TabStopSpan.class);
+ if (spans.length > 0) {
+ int[] stops = new int[spans.length];
+ for (int i = 0; i < spans.length; i++) {
+ stops[i] = spans[i].getTabStop();
+ }
+ Arrays.sort(stops, 0, stops.length);
+ variableTabStops = stops;
+ }
+ }
+
+ final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
+ final char[] chs = measuredPara.getChars();
+ final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
+ final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
+ int breakCount = lineBreaker.computeLineBreaks(
+ measuredPara.getChars(),
+ measuredPara.getNativeMeasuredParagraph(),
+ paraEnd - paraStart,
+ firstWidth,
+ firstWidthLineCount,
+ restWidth,
+ variableTabStops,
+ TAB_INCREMENT,
+ mLineCount,
+ lineBreaks);
+
+ final int[] breaks = lineBreaks.breaks;
+ final float[] lineWidths = lineBreaks.widths;
+ final float[] ascents = lineBreaks.ascents;
+ final float[] descents = lineBreaks.descents;
+ final int[] flags = lineBreaks.flags;
+
+ final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
+ final boolean ellipsisMayBeApplied = ellipsize != null
+ && (ellipsize == TextUtils.TruncateAt.END
+ || (mMaximumVisibleLineCount == 1
+ && ellipsize != TextUtils.TruncateAt.MARQUEE));
+ if (0 < remainingLineCount && remainingLineCount < breakCount
+ && ellipsisMayBeApplied) {
+ // Calculate width
+ float width = 0;
+ int flag = 0; // XXX May need to also have starting hyphen edit
+ for (int i = remainingLineCount - 1; i < breakCount; i++) {
+ if (i == breakCount - 1) {
+ width += lineWidths[i];
} else {
- if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
- chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
- }
-
- for (int i = 0; i < chooseHt.length; i++) {
- int o = spanned.getSpanStart(chooseHt[i]);
-
- if (o < paraStart) {
- // starts in this layout, before the
- // current paragraph
-
- chooseHtv[i] = getLineTop(getLineForOffset(o));
- } else {
- // starts in this paragraph
-
- chooseHtv[i] = v;
- }
+ for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
+ width += measuredPara.getCharWidthAt(j - paraStart);
}
}
+ flag |= flags[i] & TAB_MASK;
+ }
+ // Treat the last line and overflowed lines as a single line.
+ breaks[remainingLineCount - 1] = breaks[breakCount - 1];
+ lineWidths[remainingLineCount - 1] = width;
+ flags[remainingLineCount - 1] = flag;
+
+ breakCount = remainingLineCount;
+ }
+
+ // here is the offset of the starting character of the line we are currently
+ // measuring
+ int here = paraStart;
+
+ int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
+ int fmCacheIndex = 0;
+ int spanEndCacheIndex = 0;
+ int breakIndex = 0;
+ for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
+ // retrieve end of span
+ spanEnd = spanEndCache[spanEndCacheIndex++];
+
+ // retrieve cached metrics, order matches above
+ fm.top = fmCache[fmCacheIndex * 4 + 0];
+ fm.bottom = fmCache[fmCacheIndex * 4 + 1];
+ fm.ascent = fmCache[fmCacheIndex * 4 + 2];
+ fm.descent = fmCache[fmCacheIndex * 4 + 3];
+ fmCacheIndex++;
+
+ if (fm.top < fmTop) {
+ fmTop = fm.top;
+ }
+ if (fm.ascent < fmAscent) {
+ fmAscent = fm.ascent;
+ }
+ if (fm.descent > fmDescent) {
+ fmDescent = fm.descent;
+ }
+ if (fm.bottom > fmBottom) {
+ fmBottom = fm.bottom;
}
- // tab stop locations
- int[] variableTabStops = null;
- if (spanned != null) {
- TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
- paraEnd, TabStopSpan.class);
- if (spans.length > 0) {
- int[] stops = new int[spans.length];
- for (int i = 0; i < spans.length; i++) {
- stops[i] = spans[i].getTabStop();
- }
- Arrays.sort(stops, 0, stops.length);
- variableTabStops = stops;
- }
+ // skip breaks ending before current span range
+ while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
+ breakIndex++;
}
- final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
- final char[] chs = measuredPara.getChars();
- final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
- final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
- // TODO: Stop keeping duplicated width copy in native and Java.
- widths.resize(chs.length);
+ while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
+ int endPos = paraStart + breaks[breakIndex];
- // measurement has to be done before performing line breaking
- // but we don't want to recompute fontmetrics or span ranges the
- // second time, so we cache those and then use those stored values
+ boolean moreChars = (endPos < bufEnd);
- int breakCount = nComputeLineBreaks(
- nativePtr,
+ final int ascent = fallbackLineSpacing
+ ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
+ : fmAscent;
+ final int descent = fallbackLineSpacing
+ ? Math.max(fmDescent, Math.round(descents[breakIndex]))
+ : fmDescent;
- // Inputs
- chs,
- measuredPara.getNativePtr(),
- paraEnd - paraStart,
- firstWidth,
- firstWidthLineCount,
- restWidth,
- variableTabStops,
- TAB_INCREMENT,
- mLineCount,
+ v = out(source, here, endPos,
+ ascent, descent, fmTop, fmBottom,
+ v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
+ flags[breakIndex], needMultiply, measuredPara, bufEnd,
+ includepad, trackpad, addLastLineSpacing, chs,
+ paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
+ paint, moreChars);
- // Outputs
- lineBreaks,
- lineBreaks.breaks.length,
- lineBreaks.breaks,
- lineBreaks.widths,
- lineBreaks.ascents,
- lineBreaks.descents,
- lineBreaks.flags,
- widths.getRawArray());
-
- final int[] breaks = lineBreaks.breaks;
- final float[] lineWidths = lineBreaks.widths;
- final float[] ascents = lineBreaks.ascents;
- final float[] descents = lineBreaks.descents;
- final int[] flags = lineBreaks.flags;
-
- final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
- final boolean ellipsisMayBeApplied = ellipsize != null
- && (ellipsize == TextUtils.TruncateAt.END
- || (mMaximumVisibleLineCount == 1
- && ellipsize != TextUtils.TruncateAt.MARQUEE));
- if (0 < remainingLineCount && remainingLineCount < breakCount
- && ellipsisMayBeApplied) {
- // Calculate width and flag.
- float width = 0;
- int flag = 0; // XXX May need to also have starting hyphen edit
- for (int i = remainingLineCount - 1; i < breakCount; i++) {
- if (i == breakCount - 1) {
- width += lineWidths[i];
- } else {
- for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
- width += widths.get(j);
- }
- }
- flag |= flags[i] & TAB_MASK;
- }
- // Treat the last line and overflowed lines as a single line.
- breaks[remainingLineCount - 1] = breaks[breakCount - 1];
- lineWidths[remainingLineCount - 1] = width;
- flags[remainingLineCount - 1] = flag;
-
- breakCount = remainingLineCount;
- }
-
- // here is the offset of the starting character of the line we are currently
- // measuring
- int here = paraStart;
-
- int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
- int fmCacheIndex = 0;
- int spanEndCacheIndex = 0;
- int breakIndex = 0;
- for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
- // retrieve end of span
- spanEnd = spanEndCache[spanEndCacheIndex++];
-
- // retrieve cached metrics, order matches above
- fm.top = fmCache[fmCacheIndex * 4 + 0];
- fm.bottom = fmCache[fmCacheIndex * 4 + 1];
- fm.ascent = fmCache[fmCacheIndex * 4 + 2];
- fm.descent = fmCache[fmCacheIndex * 4 + 3];
- fmCacheIndex++;
-
- if (fm.top < fmTop) {
+ if (endPos < spanEnd) {
+ // preserve metrics for current span
fmTop = fm.top;
- }
- if (fm.ascent < fmAscent) {
- fmAscent = fm.ascent;
- }
- if (fm.descent > fmDescent) {
- fmDescent = fm.descent;
- }
- if (fm.bottom > fmBottom) {
fmBottom = fm.bottom;
+ fmAscent = fm.ascent;
+ fmDescent = fm.descent;
+ } else {
+ fmTop = fmBottom = fmAscent = fmDescent = 0;
}
- // skip breaks ending before current span range
- while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
- breakIndex++;
+ here = endPos;
+ breakIndex++;
+
+ if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
+ return;
}
-
- while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
- int endPos = paraStart + breaks[breakIndex];
-
- boolean moreChars = (endPos < bufEnd);
-
- final int ascent = fallbackLineSpacing
- ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
- : fmAscent;
- final int descent = fallbackLineSpacing
- ? Math.max(fmDescent, Math.round(descents[breakIndex]))
- : fmDescent;
- v = out(source, here, endPos,
- ascent, descent, fmTop, fmBottom,
- v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
- flags[breakIndex], needMultiply, measuredPara, bufEnd,
- includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
- paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
- paint, moreChars);
-
- if (endPos < spanEnd) {
- // preserve metrics for current span
- fmTop = fm.top;
- fmBottom = fm.bottom;
- fmAscent = fm.ascent;
- fmDescent = fm.descent;
- } else {
- fmTop = fmBottom = fmAscent = fmDescent = 0;
- }
-
- here = endPos;
- breakIndex++;
-
- if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
- return;
- }
- }
- }
-
- if (paraEnd == bufEnd) {
- break;
}
}
- if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
- && mLineCount < mMaximumVisibleLineCount) {
- final MeasuredParagraph measuredPara =
- MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
- paint.getFontMetricsInt(fm);
- v = out(source,
- bufEnd, bufEnd, fm.ascent, fm.descent,
- fm.top, fm.bottom,
- v,
- spacingmult, spacingadd, null,
- null, fm, 0,
- needMultiply, measuredPara, bufEnd,
- includepad, trackpad, addLastLineSpacing, null,
- null, bufStart, ellipsize,
- ellipsizedWidth, 0, paint, false);
+ if (paraEnd == bufEnd) {
+ break;
}
- } finally {
- nFinish(nativePtr);
+ }
+
+ if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
+ && mLineCount < mMaximumVisibleLineCount) {
+ final MeasuredParagraph measuredPara =
+ MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
+ paint.getFontMetricsInt(fm);
+ v = out(source,
+ bufEnd, bufEnd, fm.ascent, fm.descent,
+ fm.top, fm.bottom,
+ v,
+ spacingmult, spacingadd, null,
+ null, fm, 0,
+ needMultiply, measuredPara, bufEnd,
+ includepad, trackpad, addLastLineSpacing, null,
+ bufStart, ellipsize,
+ ellipsizedWidth, 0, paint, false);
}
}
@@ -884,7 +855,7 @@
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured,
final int bufEnd, final boolean includePad, final boolean trackPad,
- final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
+ final boolean addLastLineLineSpacing, final char[] chs,
final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
final float textWidth, final TextPaint paint, final boolean moreChars) {
final int j = mLineCount;
@@ -942,7 +913,7 @@
(!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
ellipsize == TextUtils.TruncateAt.END);
if (doEllipsis) {
- calculateEllipsis(start, end, widths, widthStart,
+ calculateEllipsis(start, end, measured, widthStart,
ellipsisWidth, ellipsize, j,
textWidth, paint, forceEllipsis);
}
@@ -1026,7 +997,7 @@
}
private void calculateEllipsis(int lineStart, int lineEnd,
- float[] widths, int widthStart,
+ MeasuredParagraph measured, int widthStart,
float avail, TextUtils.TruncateAt where,
int line, float textWidth, TextPaint paint,
boolean forceEllipsis) {
@@ -1050,9 +1021,10 @@
int i;
for (i = len; i > 0; i--) {
- float w = widths[i - 1 + lineStart - widthStart];
+ float w = measured.getCharWidthAt(i - 1 + lineStart - widthStart);
if (w + sum + ellipsisWidth > avail) {
- while (i < len && widths[i + lineStart - widthStart] == 0.0f) {
+ while (i < len
+ && measured.getCharWidthAt(i + lineStart - widthStart) == 0.0f) {
i++;
}
break;
@@ -1074,7 +1046,7 @@
int i;
for (i = 0; i < len; i++) {
- float w = widths[i + lineStart - widthStart];
+ float w = measured.getCharWidthAt(i + lineStart - widthStart);
if (w + sum + ellipsisWidth > avail) {
break;
@@ -1097,10 +1069,12 @@
float ravail = (avail - ellipsisWidth) / 2;
for (right = len; right > 0; right--) {
- float w = widths[right - 1 + lineStart - widthStart];
+ float w = measured.getCharWidthAt(right - 1 + lineStart - widthStart);
if (w + rsum > ravail) {
- while (right < len && widths[right + lineStart - widthStart] == 0.0f) {
+ while (right < len
+ && measured.getCharWidthAt(right + lineStart - widthStart)
+ == 0.0f) {
right++;
}
break;
@@ -1110,7 +1084,7 @@
float lavail = avail - ellipsisWidth - rsum;
for (left = 0; left < right; left++) {
- float w = widths[left + lineStart - widthStart];
+ float w = measured.getCharWidthAt(left + lineStart - widthStart);
if (w + lsum > lavail) {
break;
@@ -1306,47 +1280,6 @@
? mMaxLineHeight : super.getHeight();
}
- @FastNative
- private static native long nInit(
- @BreakStrategy int breakStrategy,
- @HyphenationFrequency int hyphenationFrequency,
- boolean isJustified,
- @Nullable int[] indents);
-
- @CriticalNative
- private static native void nFinish(long nativePtr);
-
- // populates LineBreaks and returns the number of breaks found
- //
- // the arrays inside the LineBreaks objects are passed in as well
- // to reduce the number of JNI calls in the common case where the
- // arrays do not have to be resized
- // The individual character widths will be returned in charWidths. The length of charWidths must
- // be at least the length of the text.
- private static native int nComputeLineBreaks(
- /* non zero */ long nativePtr,
-
- // Inputs
- @NonNull char[] text,
- /* Non Zero */ long measuredTextPtr,
- @IntRange(from = 0) int length,
- @FloatRange(from = 0.0f) float firstWidth,
- @IntRange(from = 0) int firstWidthLineCount,
- @FloatRange(from = 0.0f) float restWidth,
- @Nullable int[] variableTabStops,
- int defaultTabStop,
- @IntRange(from = 0) int indentsOffset,
-
- // Outputs
- @NonNull LineBreaks recycle,
- @IntRange(from = 0) int recycleLength,
- @NonNull int[] recycleBreaks,
- @NonNull float[] recycleWidths,
- @NonNull float[] recycleAscents,
- @NonNull float[] recycleDescents,
- @NonNull int[] recycleFlags,
- @NonNull float[] charWidths);
-
private int mLineCount;
private int mTopPadding, mBottomPadding;
private int mColumns;
@@ -1396,8 +1329,7 @@
private static final int DEFAULT_MAX_LINE_HEIGHT = -1;
- // This is used to return three arrays from a single JNI call when
- // performing line breaking
+ // Unused, here because of gray list private API accesses.
/*package*/ static class LineBreaks {
private static final int INITIAL_SIZE = 16;
public int[] breaks = new int[INITIAL_SIZE];
diff --git a/core/java/android/util/Base64OutputStream.java b/core/java/android/util/Base64OutputStream.java
index 4535d1c..8378705 100644
--- a/core/java/android/util/Base64OutputStream.java
+++ b/core/java/android/util/Base64OutputStream.java
@@ -117,8 +117,10 @@
out.flush();
}
} catch (IOException e) {
- if (thrown != null) {
+ if (thrown == null) {
thrown = e;
+ } else {
+ thrown.addSuppressed(e);
}
}
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index bc4a19d..11aee2f 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -390,6 +390,7 @@
public static class ZipInflaterInputStream extends InflaterInputStream {
private final ZipEntry entry;
private long bytesRead = 0;
+ private boolean closed;
public ZipInflaterInputStream(InputStream is, Inflater inf, int bsize, ZipEntry entry) {
super(is, inf, bsize);
@@ -424,6 +425,12 @@
}
return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
}
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ closed = true;
+ }
}
/**
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index 18cc10f..74fa9e8 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -515,7 +515,7 @@
contextStart - paraStart,
contextEnd - contextStart,
x, y, isRtl, paint.getNativeInstance(),
- mp.getNativePtr());
+ mp.getNativeMeasuredParagraph().getNativePtr());
return;
}
}
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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 240f3c0..97a2d79 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3097,13 +3097,27 @@
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
boolean usingAsyncReport = false;
- if (mReportNextDraw && mAttachInfo.mThreadedRenderer != null
- && mAttachInfo.mThreadedRenderer.isEnabled()) {
- usingAsyncReport = true;
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
- // TODO: Use the frame number
- pendingDrawFinished();
- });
+ if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
+ ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
+ .captureFrameCommitCallbacks();
+ if (mReportNextDraw) {
+ usingAsyncReport = true;
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
+ // TODO: Use the frame number
+ pendingDrawFinished();
+ if (commitCallbacks != null) {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ }
+ });
+ } else if (commitCallbacks != null && commitCallbacks.size() > 0) {
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ });
+ }
}
try {
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 0973d0a..efc1807 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -16,6 +16,9 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
@@ -56,6 +59,9 @@
private ArrayList<OnDrawListener> mOnDrawListeners;
private static boolean sIllegalOnDrawModificationIsFatal;
+ // These listeners are one-shot
+ private ArrayList<Runnable> mOnFrameCommitListeners;
+
/** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
* that the listener will be immediately called. */
private boolean mWindowShown;
@@ -393,6 +399,14 @@
}
}
+ if (observer.mOnFrameCommitListeners != null) {
+ if (mOnFrameCommitListeners != null) {
+ mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks());
+ } else {
+ mOnFrameCommitListeners = observer.captureFrameCommitCallbacks();
+ }
+ }
+
if (observer.mOnTouchModeChangeListeners != null) {
if (mOnTouchModeChangeListeners != null) {
mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
@@ -713,6 +727,49 @@
}
/**
+ * Adds a frame commit callback. This callback will be invoked when the current rendering
+ * content has been rendered into a frame and submitted to the swap chain. The frame may
+ * not currently be visible on the display when this is invoked, but it has been submitted.
+ * This callback is useful in combination with {@link PixelCopy} to capture the current
+ * rendered content of the UI reliably.
+ *
+ * Note: Only works with hardware rendering. Does nothing otherwise.
+ *
+ * @param callback The callback to invoke when the frame is committed.
+ */
+ @TestApi
+ public void registerFrameCommitCallback(@NonNull Runnable callback) {
+ checkIsAlive();
+ if (mOnFrameCommitListeners == null) {
+ mOnFrameCommitListeners = new ArrayList<>();
+ }
+ mOnFrameCommitListeners.add(callback);
+ }
+
+ @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() {
+ ArrayList<Runnable> ret = mOnFrameCommitListeners;
+ mOnFrameCommitListeners = null;
+ return ret;
+ }
+
+ /**
+ * Attempts to remove the given callback from the list of pending frame complete callbacks.
+ *
+ * @param callback The callback to remove
+ * @return Whether or not the callback was removed. If this returns true the callback will
+ * not be invoked. If false is returned then the callback was either never added
+ * or may already be pending execution and was unable to be removed
+ */
+ @TestApi
+ public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
+ checkIsAlive();
+ if (mOnFrameCommitListeners == null) {
+ return false;
+ }
+ return mOnFrameCommitListeners.remove(callback);
+ }
+
+ /**
* Register a callback to be invoked when a view has been scrolled.
*
* @param listener The callback to add
diff --git a/core/java/android/view/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/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index e5d6556..f63c43f 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -36,7 +36,6 @@
private final ProcessStats.PackageState mPackageState;
private final String mProcessName;
private final String mName;
- private final DurationsTable mDurations;
public final class SourceState {
final SourceKey mKey;
@@ -49,8 +48,10 @@
long mDuration;
long mTrackingUptime;
int mActiveCount;
+ int mActiveProcState = ProcessStats.STATE_NOTHING;
long mActiveStartUptime;
long mActiveDuration;
+ DurationsTable mDurations;
SourceState(SourceKey key) {
mKey = key;
@@ -77,13 +78,15 @@
mProcState = procState;
}
if (procState < ProcessStats.STATE_HOME) {
+ // If the proc state has become better than cached, then we want to
+ // start tracking it to count when it is actually active. If it drops
+ // down to cached, we will clean it up when we later evaluate all currently
+ // tracked associations in ProcessStats.updateTrackingAssociationsLocked().
if (!mInTrackingList) {
mInTrackingList = true;
mTrackingUptime = now;
mProcessStats.mTrackingAssociations.add(this);
}
- } else {
- stopTracking(now);
}
}
@@ -102,6 +105,22 @@
mActiveStartUptime = now;
mActiveCount++;
}
+ if (mActiveProcState != mProcState) {
+ if (mActiveProcState != ProcessStats.STATE_NOTHING) {
+ // Currently active proc state changed, need to store the duration
+ // so far and switch tracking to the new proc state.
+ final long duration = mActiveDuration + now - mActiveStartUptime;
+ if (duration != 0) {
+ if (mDurations == null) {
+ makeDurations();
+ }
+ mDurations.addDuration(mActiveProcState, duration);
+ mActiveDuration = 0;
+ }
+ mActiveStartUptime = now;
+ }
+ mActiveProcState = mProcState;
+ }
} else {
Slog.wtf(TAG, "startActive while not tracking: " + this);
}
@@ -112,15 +131,25 @@
if (!mInTrackingList) {
Slog.wtf(TAG, "stopActive while not tracking: " + this);
}
- mActiveDuration += now - mActiveStartUptime;
+ final long duration = mActiveDuration + now - mActiveStartUptime;
+ if (mDurations != null) {
+ mDurations.addDuration(mActiveProcState, duration);
+ } else {
+ mActiveDuration = duration;
+ }
mActiveStartUptime = 0;
}
}
+ void makeDurations() {
+ mDurations = new DurationsTable(mProcessStats.mTableData);
+ }
+
void stopTracking(long now) {
stopActive(now);
if (mInTrackingList) {
mInTrackingList = false;
+ mProcState = ProcessStats.STATE_NOTHING;
// Do a manual search for where to remove, since these objects will typically
// be towards the end of the array.
final ArrayList<SourceState> list = mProcessStats.mTrackingAssociations;
@@ -207,7 +236,6 @@
mPackageState = packageState;
mName = name;
mProcessName = processName;
- mDurations = new DurationsTable(processStats.mTableData);
mProc = proc;
}
@@ -254,7 +282,6 @@
}
public void add(AssociationState other) {
- mDurations.addDurations(other.mDurations);
for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
final SourceKey key = other.mSources.keyAt(isrc);
final SourceState otherSrc = other.mSources.valueAt(isrc);
@@ -266,7 +293,48 @@
mySrc.mCount += otherSrc.mCount;
mySrc.mDuration += otherSrc.mDuration;
mySrc.mActiveCount += otherSrc.mActiveCount;
- mySrc.mActiveDuration += otherSrc.mActiveDuration;
+ if (otherSrc.mActiveDuration != 0 || otherSrc.mDurations != null) {
+ // Only need to do anything if the other one has some duration data.
+ if (mySrc.mDurations != null) {
+ // If the target already has multiple durations, just add in whatever
+ // we have in the other.
+ if (otherSrc.mDurations != null) {
+ mySrc.mDurations.addDurations(otherSrc.mDurations);
+ } else {
+ mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ otherSrc.mActiveDuration);
+ }
+ } else if (otherSrc.mDurations != null) {
+ // The other one has multiple durations, but we don't. Expand to
+ // multiple durations and copy over.
+ mySrc.makeDurations();
+ mySrc.mDurations.addDurations(otherSrc.mDurations);
+ if (mySrc.mActiveDuration != 0) {
+ mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+ mySrc.mActiveDuration = 0;
+ mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
+ }
+ } else if (mySrc.mActiveDuration != 0) {
+ // Both have a single inline duration... we can either add them together,
+ // or need to expand to multiple durations.
+ if (mySrc.mActiveProcState == otherSrc.mActiveProcState) {
+ mySrc.mDuration += otherSrc.mDuration;
+ } else {
+ // The two have durations with different proc states, need to turn
+ // in to multiple durations.
+ mySrc.makeDurations();
+ mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+ mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ otherSrc.mActiveDuration);
+ mySrc.mActiveDuration = 0;
+ mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
+ }
+ } else {
+ // The other one has a duration, and we know the target doesn't. Copy over.
+ mySrc.mActiveProcState = otherSrc.mActiveProcState;
+ mySrc.mActiveDuration = otherSrc.mActiveDuration;
+ }
+ }
}
}
@@ -275,7 +343,6 @@
}
public void resetSafely(long now) {
- mDurations.resetTable();
if (!isInUse()) {
mSources.clear();
} else {
@@ -293,6 +360,7 @@
src.mActiveCount = 0;
}
src.mActiveDuration = 0;
+ src.mDurations = null;
} else {
mSources.removeAt(isrc);
}
@@ -301,7 +369,6 @@
}
public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
- mDurations.writeToParcel(out);
final int NSRC = mSources.size();
out.writeInt(NSRC);
for (int isrc = 0; isrc < NSRC; isrc++) {
@@ -312,7 +379,14 @@
out.writeInt(src.mCount);
out.writeLong(src.mDuration);
out.writeInt(src.mActiveCount);
- out.writeLong(src.mActiveDuration);
+ if (src.mDurations != null) {
+ out.writeInt(1);
+ src.mDurations.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ out.writeInt(src.mActiveProcState);
+ out.writeLong(src.mActiveDuration);
+ }
}
}
@@ -321,9 +395,6 @@
* caused it to fail.
*/
public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
- if (!mDurations.readFromParcel(in)) {
- return "Duration table corrupt";
- }
final int NSRC = in.readInt();
if (NSRC < 0 || NSRC > 100000) {
return "Association with bad src count: " + NSRC;
@@ -336,7 +407,15 @@
src.mCount = in.readInt();
src.mDuration = in.readLong();
src.mActiveCount = in.readInt();
- src.mActiveDuration = in.readLong();
+ if (in.readInt() != 0) {
+ src.makeDurations();
+ if (!src.mDurations.readFromParcel(in)) {
+ return "Duration table corrupt: " + key + " <- " + src;
+ }
+ } else {
+ src.mActiveProcState = in.readInt();
+ src.mActiveDuration = in.readLong();
+ }
mSources.put(key, src);
}
return null;
@@ -351,7 +430,12 @@
src.mStartUptime = nowUptime;
}
if (src.mActiveStartUptime > 0) {
- src.mActiveDuration += nowUptime - src.mActiveStartUptime;
+ final long duration = src.mActiveDuration + nowUptime - src.mActiveStartUptime;
+ if (src.mDurations != null) {
+ src.mDurations.addDuration(src.mActiveProcState, duration);
+ } else {
+ src.mActiveDuration = duration;
+ }
src.mActiveStartUptime = nowUptime;
}
}
@@ -359,7 +443,7 @@
}
public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
- long now, long totalTime, boolean dumpSummary, boolean dumpAll) {
+ long now, long totalTime, boolean dumpDetails, boolean dumpAll) {
if (dumpAll) {
pw.print(prefix);
pw.print("mNumActive=");
@@ -376,18 +460,18 @@
UserHandle.formatUid(pw, key.mUid);
pw.println(":");
pw.print(prefixInner);
- pw.print(" Count ");
+ pw.print(" Total count ");
pw.print(src.mCount);
long duration = src.mDuration;
if (src.mNesting > 0) {
duration += now - src.mStartUptime;
}
if (dumpAll) {
- pw.print(" / Duration ");
+ pw.print(": Duration ");
TimeUtils.formatDuration(duration, pw);
pw.print(" / ");
} else {
- pw.print(" / time ");
+ pw.print(": time ");
}
DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
if (src.mNesting > 0) {
@@ -401,30 +485,120 @@
pw.print(")");
}
pw.println();
- if (src.mActiveCount > 0) {
+ if (src.mActiveCount > 0 || src.mDurations != null || src.mActiveDuration != 0
+ || src.mActiveStartUptime != 0) {
pw.print(prefixInner);
pw.print(" Active count ");
pw.print(src.mActiveCount);
- duration = src.mActiveDuration;
- if (src.mActiveStartUptime > 0) {
- duration += now - src.mActiveStartUptime;
- }
- if (dumpAll) {
- pw.print(" / Duration ");
- TimeUtils.formatDuration(duration, pw);
- pw.print(" / ");
+ if (dumpDetails) {
+ if (dumpAll) {
+ pw.print(src.mDurations != null ? " (multi-field)" : " (inline)");
+ }
+ pw.println(":");
+ dumpTime(pw, prefixInner, src, totalTime, now, dumpDetails, dumpAll);
} else {
- pw.print(" / time ");
+ pw.print(": ");
+ dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
+ pw.println();
}
- DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
- if (src.mActiveStartUptime > 0) {
- pw.print(" (running)");
+ }
+ if (dumpAll) {
+ if (src.mInTrackingList) {
+ pw.print(prefixInner);
+ pw.print(" mInTrackingList=");
+ pw.println(src.mInTrackingList);
}
- pw.println();
+ if (src.mProcState != ProcessStats.STATE_NOTHING) {
+ pw.print(prefixInner);
+ pw.print(" mProcState=");
+ pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
+ pw.print(" mProcStateSeq=");
+ pw.println(src.mProcStateSeq);
+ }
}
}
}
+ void dumpActiveDurationSummary(PrintWriter pw, final SourceState src, long totalTime,
+ long now, boolean dumpAll) {
+ long duration = dumpTime(null, null, src, totalTime, now, false, false);
+ final boolean isRunning = duration < 0;
+ if (isRunning) {
+ duration = -duration;
+ }
+ if (dumpAll) {
+ pw.print("Duration ");
+ TimeUtils.formatDuration(duration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print("time ");
+ }
+ DumpUtils.printPercent(pw, (double) duration / (double) totalTime);
+ if (src.mActiveStartUptime > 0) {
+ pw.print(" (running)");
+ }
+ pw.println();
+ }
+
+ long dumpTime(PrintWriter pw, String prefix, final SourceState src, long overallTime, long now,
+ boolean dumpDetails, boolean dumpAll) {
+ long totalTime = 0;
+ boolean isRunning = false;
+ for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) {
+ long time;
+ if (src.mDurations != null) {
+ time = src.mDurations.getValueForId((byte)iprocstate);
+ } else {
+ time = src.mActiveProcState == iprocstate ? src.mDuration : 0;
+ }
+ final String running;
+ if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
+ running = " (running)";
+ isRunning = true;
+ time += now - src.mActiveStartUptime;
+ } else {
+ running = null;
+ }
+ if (time != 0) {
+ if (pw != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(DumpUtils.STATE_LABELS[iprocstate]);
+ pw.print(": ");
+ if (dumpAll) {
+ pw.print("Duration ");
+ TimeUtils.formatDuration(time, pw);
+ pw.print(" / ");
+ } else {
+ pw.print("time ");
+ }
+ DumpUtils.printPercent(pw, (double) time / (double) overallTime);
+ if (running != null) {
+ pw.print(running);
+ }
+ pw.println();
+ }
+ totalTime += time;
+ }
+ }
+ if (totalTime != 0 && pw != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(DumpUtils.STATE_LABEL_TOTAL);
+ pw.print(": ");
+ if (dumpAll) {
+ pw.print("Duration ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.print(" / ");
+ } else {
+ pw.print("time ");
+ }
+ DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime);
+ pw.println();
+ }
+ return isRunning ? -totalTime : totalTime;
+ }
+
public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
String associationName, long now) {
final int NSRC = mSources.size();
@@ -454,12 +628,30 @@
pw.print(duration);
pw.print(",");
pw.print(src.mActiveCount);
- duration = src.mActiveDuration;
- if (src.mActiveStartUptime > 0) {
- duration += now - src.mActiveStartUptime;
+ final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
+ if (src.mDurations != null) {
+ final int N = src.mDurations.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int dkey = src.mDurations.getKeyAt(i);
+ duration = src.mDurations.getValue(dkey);
+ if (dkey == src.mActiveProcState) {
+ duration += timeNow;
+ }
+ final int procState = SparseMappingTable.getIdFromKey(dkey);
+ pw.print(",");
+ DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS, procState, 1);
+ pw.print(':');
+ pw.print(duration);
+ }
+ } else {
+ duration = src.mActiveDuration + timeNow;
+ if (duration != 0) {
+ pw.print(",");
+ DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS, src.mActiveProcState, 1);
+ pw.print(':');
+ pw.print(duration);
+ }
}
- pw.print(",");
- pw.print(duration);
pw.println();
}
}
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index 06b6552..e6073e5 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -48,6 +48,9 @@
*/
public final class DumpUtils {
public static final String[] STATE_NAMES;
+ public static final String[] STATE_LABELS;
+ public static final String STATE_LABEL_TOTAL;
+ public static final String STATE_LABEL_CACHED;
public static final String[] STATE_NAMES_CSV;
static final String[] STATE_TAGS;
static final int[] STATE_PROTO_ENUMS;
@@ -55,52 +58,70 @@
// Make the mapping easy to update.
static {
STATE_NAMES = new String[STATE_COUNT];
- STATE_NAMES[STATE_PERSISTENT] = "Persist";
- STATE_NAMES[STATE_TOP] = "Top";
- STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg";
- STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg";
- STATE_NAMES[STATE_BACKUP] = "Backup";
- STATE_NAMES[STATE_SERVICE] = "Service";
- STATE_NAMES[STATE_SERVICE_RESTARTING] = "ServRst";
- STATE_NAMES[STATE_RECEIVER] = "Receivr";
- STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt";
- STATE_NAMES[STATE_HOME] = "Home";
- STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct";
- STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct";
- STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct";
- STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty";
+ STATE_NAMES[STATE_PERSISTENT] = "Persist";
+ STATE_NAMES[STATE_TOP] = "Top";
+ STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg";
+ STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg";
+ STATE_NAMES[STATE_BACKUP] = "Backup";
+ STATE_NAMES[STATE_SERVICE] = "Service";
+ STATE_NAMES[STATE_SERVICE_RESTARTING] = "ServRst";
+ STATE_NAMES[STATE_RECEIVER] = "Receivr";
+ STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt";
+ STATE_NAMES[STATE_HOME] = "Home";
+ STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct";
+ STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct";
+ STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct";
+ STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty";
+
+ STATE_LABELS = new String[STATE_COUNT];
+ STATE_LABELS[STATE_PERSISTENT] = "Persistent";
+ STATE_LABELS[STATE_TOP] = " Top";
+ STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg";
+ STATE_LABELS[STATE_IMPORTANT_BACKGROUND] = " Imp Bg";
+ STATE_LABELS[STATE_BACKUP] = " Backup";
+ STATE_LABELS[STATE_SERVICE] = " Service";
+ STATE_LABELS[STATE_SERVICE_RESTARTING] = "Service Rs";
+ STATE_LABELS[STATE_RECEIVER] = " Receiver";
+ STATE_LABELS[STATE_HEAVY_WEIGHT] = " Heavy Wgt";
+ STATE_LABELS[STATE_HOME] = " (Home)";
+ STATE_LABELS[STATE_LAST_ACTIVITY] = "(Last Act)";
+ STATE_LABELS[STATE_CACHED_ACTIVITY] = " (Cch Act)";
+ STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT] = "(Cch CAct)";
+ STATE_LABELS[STATE_CACHED_EMPTY] = "(Cch Emty)";
+ STATE_LABEL_CACHED = " (Cached)";
+ STATE_LABEL_TOTAL = " TOTAL";
STATE_NAMES_CSV = new String[STATE_COUNT];
- STATE_NAMES_CSV[STATE_PERSISTENT] = "pers";
- STATE_NAMES_CSV[STATE_TOP] = "top";
- STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg";
- STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg";
- STATE_NAMES_CSV[STATE_BACKUP] = "backup";
- STATE_NAMES_CSV[STATE_SERVICE] = "service";
- STATE_NAMES_CSV[STATE_SERVICE_RESTARTING] = "service-rs";
- STATE_NAMES_CSV[STATE_RECEIVER] = "receiver";
- STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy";
- STATE_NAMES_CSV[STATE_HOME] = "home";
- STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact";
- STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity";
- STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient";
- STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty";
+ STATE_NAMES_CSV[STATE_PERSISTENT] = "pers";
+ STATE_NAMES_CSV[STATE_TOP] = "top";
+ STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg";
+ STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg";
+ STATE_NAMES_CSV[STATE_BACKUP] = "backup";
+ STATE_NAMES_CSV[STATE_SERVICE] = "service";
+ STATE_NAMES_CSV[STATE_SERVICE_RESTARTING] = "service-rs";
+ STATE_NAMES_CSV[STATE_RECEIVER] = "receiver";
+ STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy";
+ STATE_NAMES_CSV[STATE_HOME] = "home";
+ STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact";
+ STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity";
+ STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient";
+ STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty";
STATE_TAGS = new String[STATE_COUNT];
- STATE_TAGS[STATE_PERSISTENT] = "p";
- STATE_TAGS[STATE_TOP] = "t";
- STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f";
- STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b";
- STATE_TAGS[STATE_BACKUP] = "u";
- STATE_TAGS[STATE_SERVICE] = "s";
- STATE_TAGS[STATE_SERVICE_RESTARTING] = "x";
- STATE_TAGS[STATE_RECEIVER] = "r";
- STATE_TAGS[STATE_HEAVY_WEIGHT] = "w";
- STATE_TAGS[STATE_HOME] = "h";
- STATE_TAGS[STATE_LAST_ACTIVITY] = "l";
- STATE_TAGS[STATE_CACHED_ACTIVITY] = "a";
- STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c";
- STATE_TAGS[STATE_CACHED_EMPTY] = "e";
+ STATE_TAGS[STATE_PERSISTENT] = "p";
+ STATE_TAGS[STATE_TOP] = "t";
+ STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f";
+ STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b";
+ STATE_TAGS[STATE_BACKUP] = "u";
+ STATE_TAGS[STATE_SERVICE] = "s";
+ STATE_TAGS[STATE_SERVICE_RESTARTING] = "x";
+ STATE_TAGS[STATE_RECEIVER] = "r";
+ STATE_TAGS[STATE_HEAVY_WEIGHT] = "w";
+ STATE_TAGS[STATE_HOME] = "h";
+ STATE_TAGS[STATE_LAST_ACTIVITY] = "l";
+ STATE_TAGS[STATE_CACHED_ACTIVITY] = "a";
+ STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c";
+ STATE_TAGS[STATE_CACHED_EMPTY] = "e";
STATE_PROTO_ENUMS = new int[STATE_COUNT];
STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsProto.State.PERSISTENT;
@@ -166,7 +187,7 @@
pw.print("SOff/");
break;
case ADJ_SCREEN_ON:
- pw.print("SOn /");
+ pw.print(" SOn/");
break;
default:
pw.print("????/");
@@ -201,11 +222,11 @@
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_MODERATE:
- pw.print("Mod ");
+ pw.print(" Mod");
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_LOW:
- pw.print("Low ");
+ pw.print(" Low");
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_CRITICAL:
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index ad42288..9685f75 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -787,35 +787,36 @@
pw.print(" / v");
pw.print(mVersion);
pw.println(":");
- dumpProcessSummaryDetails(pw, prefix, " TOTAL: ", screenStates, memStates,
- procStates, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Persistent: ", screenStates, memStates,
- new int[] { STATE_PERSISTENT }, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Top: ", screenStates, memStates,
- new int[] {STATE_TOP}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Imp Fg: ", screenStates, memStates,
- new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Imp Bg: ", screenStates, memStates,
- new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Backup: ", screenStates, memStates,
- new int[] {STATE_BACKUP}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Heavy Wgt: ", screenStates, memStates,
- new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Service: ", screenStates, memStates,
- new int[] {STATE_SERVICE}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Service Rs: ", screenStates, memStates,
- new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Receiver: ", screenStates, memStates,
- new int[] {STATE_RECEIVER}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Heavy: ", screenStates, memStates,
- new int[] {STATE_HOME}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " (Home): ", screenStates, memStates,
- new int[] {STATE_HOME}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " (Last Act): ", screenStates, memStates,
- new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " (Cached): ", screenStates, memStates,
- new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
- STATE_CACHED_EMPTY}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_TOTAL,
+ screenStates, memStates, procStates, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_PERSISTENT],
+ screenStates, memStates, new int[] { STATE_PERSISTENT }, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_TOP],
+ screenStates, memStates, new int[] {STATE_TOP}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_IMPORTANT_FOREGROUND],
+ screenStates, memStates, new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime,
+ true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_IMPORTANT_BACKGROUND],
+ screenStates, memStates, new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime,
+ true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BACKUP],
+ screenStates, memStates, new int[] {STATE_BACKUP}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_SERVICE],
+ screenStates, memStates, new int[] {STATE_SERVICE}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_SERVICE_RESTARTING],
+ screenStates, memStates, new int[] {STATE_SERVICE_RESTARTING}, now, totalTime,
+ true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_RECEIVER],
+ screenStates, memStates, new int[] {STATE_RECEIVER}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_HEAVY_WEIGHT],
+ screenStates, memStates, new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_HOME],
+ screenStates, memStates, new int[] {STATE_HOME}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_LAST_ACTIVITY],
+ screenStates, memStates, new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_CACHED,
+ screenStates, memStates, new int[] {STATE_CACHED_ACTIVITY,
+ STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY}, now, totalTime, true);
}
public void dumpProcessState(PrintWriter pw, String prefix,
@@ -846,7 +847,7 @@
printedMem != imem ? imem : STATE_NOTHING, '/');
printedMem = imem;
}
- pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
+ pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
TimeUtils.formatDuration(time, pw); pw.println(running);
totalTime += time;
}
@@ -861,7 +862,8 @@
if (memStates.length > 1) {
DumpUtils.printMemLabel(pw, STATE_NOTHING, '/');
}
- pw.print("TOTAL : ");
+ pw.print(DumpUtils.STATE_LABEL_TOTAL);
+ pw.print(": ");
TimeUtils.formatDuration(totalTime, pw);
pw.println();
}
@@ -899,7 +901,7 @@
printedMem != imem ? imem : STATE_NOTHING, '/');
printedMem = imem;
}
- pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
+ pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
pw.print(count);
pw.print(" samples ");
DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024);
@@ -950,7 +952,9 @@
pw.print(prefix);
}
if (label != null) {
+ pw.print(" ");
pw.print(label);
+ pw.print(": ");
}
totals.print(pw, totalTime, full);
if (prefix != null) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 15f140e..12b16d0 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -158,7 +158,7 @@
};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 32;
+ private static final int PARCEL_VERSION = 33;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -1380,9 +1380,13 @@
final int NUM = mTrackingAssociations.size();
for (int i = NUM - 1; i >= 0; i--) {
final AssociationState.SourceState act = mTrackingAssociations.get(i);
- if (act.mProcStateSeq != curSeq) {
+ if (act.mProcStateSeq != curSeq || act.mProcState >= ProcessStats.STATE_HOME) {
+ // If this association did not get touched the last time we computed
+ // process states, or its state ended up down in cached, then we no
+ // longer have a reason to track it at all.
+ act.stopActive(now);
act.mInTrackingList = false;
- act.mProcState = STATE_NOTHING;
+ act.mProcState = ProcessStats.STATE_NOTHING;
mTrackingAssociations.remove(i);
} else {
final ProcessState proc = act.getAssociationState().getProcess();
@@ -1407,7 +1411,7 @@
}
public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
- boolean dumpAll, boolean activeOnly) {
+ boolean dumpDetails, boolean dumpAll, boolean activeOnly) {
long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
boolean sepNeeded = false;
@@ -1538,7 +1542,7 @@
pw.println(":");
pw.print(" Process: "); pw.println(asc.getProcessName());
asc.dumpStats(pw, " ", " ", " ",
- now, totalTime, dumpSummary, dumpAll);
+ now, totalTime, dumpDetails, dumpAll);
}
}
}
@@ -1632,21 +1636,8 @@
if (src.mActiveCount > 0) {
pw.print(" Active count ");
pw.print(src.mActiveCount);
- long duration = src.mActiveDuration;
- if (src.mActiveStartUptime > 0) {
- duration += now - src.mActiveStartUptime;
- }
- if (dumpAll) {
- pw.print(" / Duration ");
- TimeUtils.formatDuration(duration, pw);
- pw.print(" / ");
- } else {
- pw.print(" / time ");
- }
- DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
- if (src.mActiveStartUptime > 0) {
- pw.print(" (running)");
- }
+ pw.print(": ");
+ asc.dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
pw.println();
}
}
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 f87c081..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;
@@ -38,6 +39,7 @@
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.ToDoubleFunction;
@@ -46,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;
@@ -56,66 +58,55 @@
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();
- 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")
private final ArrayMap<String, Integer> mExceptionCounts = new ArrayMap<>();
private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
private final Object mLock = new Object();
+ private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
- @GuardedBy("mLock")
- private UidEntry mSampledEntries = new UidEntry(-1);
- @VisibleForTesting // Use getInstance() instead.
- public BinderCallsStats() {
+ 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;
synchronized (mLock) {
- if (mDetailedTracking) {
- s.cpuTimeStarted = getThreadTimeMicro();
- s.timeStarted = getElapsedRealtimeMicro();
- } else {
- s.sampledCallStat = mSampledEntries.getOrCreate(s.callStat);
- if (s.sampledCallStat.callCount % mPeriodicSamplingInterval == 0) {
- s.cpuTimeStarted = getThreadTimeMicro();
- s.timeStarted = getElapsedRealtimeMicro();
- }
+ if (!mDetailedTracking && !shouldTrackCall()) {
+ return s;
}
+
+ s.cpuTimeStarted = getThreadTimeMicro();
+ s.timeStarted = getElapsedRealtimeMicro();
}
return s;
}
- public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
- Preconditions.checkNotNull(s);
- if (s == NOT_ENABLED) {
- return;
+ @Override
+ public void callEnded(@Nullable CallSession s, int parcelRequestSize, int parcelReplySize) {
+ if (s == null) {
+ return;
}
processCallEnded(s, parcelRequestSize, parcelReplySize);
@@ -127,96 +118,67 @@
private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
synchronized (mLock) {
- if (!mEnabled) {
- return;
- }
-
- long duration;
- long latencyDuration;
- if (mDetailedTracking) {
- duration = getThreadTimeMicro() - s.cpuTimeStarted;
- latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
- } else {
- CallStat cs = s.sampledCallStat;
- // Non-negative time signals beginning of the new sampling interval
- if (s.cpuTimeStarted >= 0) {
- duration = getThreadTimeMicro() - s.cpuTimeStarted;
- latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
- } else {
- // callCount is always incremented, but time only once per sampling interval
- long samplesCount = cs.callCount / mPeriodicSamplingInterval + 1;
- duration = cs.cpuTimeMicros / samplesCount;
- latencyDuration = cs.latencyMicros / samplesCount;
- }
- }
-
- int callingUid = getCallingUid();
-
+ final int callingUid = getCallingUid();
UidEntry uidEntry = mUidEntries.get(callingUid);
if (uidEntry == null) {
uidEntry = new UidEntry(callingUid);
mUidEntries.put(callingUid, uidEntry);
}
-
- CallStat callStat;
- if (mDetailedTracking) {
- // Find CallStat entry and update its total time
- callStat = uidEntry.getOrCreate(s.callStat);
- callStat.exceptionCount += s.exceptionThrown ? 1 : 0;
- callStat.maxRequestSizeBytes =
- Math.max(callStat.maxRequestSizeBytes, parcelRequestSize);
- callStat.maxReplySizeBytes =
- Math.max(callStat.maxReplySizeBytes, parcelReplySize);
- } else {
- // update sampled timings in the beginning of each interval
- callStat = s.sampledCallStat;
- }
+ uidEntry.callCount++;
+ CallStat callStat = uidEntry.getOrCreate(s.className, s.transactionCode);
callStat.callCount++;
- callStat.methodName = s.callStat.methodName;
- if (s.cpuTimeStarted >= 0) {
+
+ // Non-negative time signals we need to record data for this call.
+ final boolean recordCall = s.cpuTimeStarted >= 0;
+ if (recordCall) {
+ final long duration = getThreadTimeMicro() - s.cpuTimeStarted;
+ final long latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
+ uidEntry.cpuTimeMicros += duration;
+ uidEntry.recordedCallCount++;
+
+ callStat.recordedCallCount++;
+ callStat.methodName = s.methodName;
callStat.cpuTimeMicros += duration;
callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
callStat.latencyMicros += latencyDuration;
- callStat.maxLatencyMicros = Math.max(callStat.maxLatencyMicros, latencyDuration);
+ callStat.maxLatencyMicros =
+ Math.max(callStat.maxLatencyMicros, latencyDuration);
+ if (mDetailedTracking) {
+ callStat.exceptionCount += s.exceptionThrown ? 1 : 0;
+ callStat.maxRequestSizeBytes =
+ Math.max(callStat.maxRequestSizeBytes, parcelRequestSize);
+ callStat.maxReplySizeBytes =
+ Math.max(callStat.maxReplySizeBytes, parcelReplySize);
+ }
}
-
- uidEntry.cpuTimeMicros += duration;
- uidEntry.callCount++;
}
}
- /**
- * 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) {
- return;
+ @Override
+ public void callThrewException(@Nullable CallSession s, Exception exception) {
+ if (s == null) {
+ return;
}
s.exceptionThrown = true;
try {
String className = exception.getClass().getName();
synchronized (mLock) {
if (mExceptionCounts.size() >= MAX_EXCEPTION_COUNT_SIZE) {
- className = EXCEPTION_COUNT_OVERFLOW_NAME;
+ className = EXCEPTION_COUNT_OVERFLOW_NAME;
}
Integer count = mExceptionCounts.get(className);
mExceptionCounts.put(className, count == null ? 1 : count + 1);
}
} catch (RuntimeException e) {
- // Do not propagate the exception. We do not want to swallow original exception.
- Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e);
+ // Do not propagate the exception. We do not want to swallow original exception.
+ Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e);
}
}
public ArrayList<ExportedCallStat> getExportedCallStats() {
// We do not collect all the data if detailed tracking is off.
if (!mDetailedTracking) {
- return new ArrayList<ExportedCallStat>();
+ return new ArrayList<ExportedCallStat>();
}
ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
@@ -229,11 +191,12 @@
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;
exported.maxLatencyMicros = stat.maxLatencyMicros;
+ exported.recordedCallCount = stat.recordedCallCount;
exported.callCount = stat.callCount;
exported.maxRequestSizeBytes = stat.maxRequestSizeBytes;
exported.maxReplySizeBytes = stat.maxReplySizeBytes;
@@ -246,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);
@@ -253,15 +223,12 @@
}
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;
pw.print("Start time: ");
pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+ pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
List<UidEntry> entries = new ArrayList<>();
int uidEntriesSize = mUidEntries.size();
@@ -269,70 +236,53 @@
UidEntry e = mUidEntries.valueAt(i);
entries.add(e);
totalCpuTime += e.cpuTimeMicros;
+ totalRecordedCallsCount += e.recordedCallCount;
totalCallsCount += e.callCount;
}
entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
StringBuilder sb = new StringBuilder();
- if (mDetailedTracking) {
- pw.println("Per-UID raw data " + datasetSizeDesc
- + "(package/uid, call_desc, cpu_time_micros, max_cpu_time_micros, "
- + "latency_time_micros, max_latency_time_micros, exception_count, "
- + "max_request_size_bytes, max_reply_size_bytes, call_count):");
- List<UidEntry> topEntries = verbose ? entries
- : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
- for (UidEntry uidEntry : topEntries) {
- for (CallStat e : uidEntry.getCallStatsList()) {
- sb.setLength(0);
- sb.append(" ")
- .append(uidToString(uidEntry.uid, appIdToPkgNameMap))
- .append(",").append(e)
- .append(',').append(e.cpuTimeMicros)
- .append(',').append(e.maxCpuTimeMicros)
- .append(',').append(e.latencyMicros)
- .append(',').append(e.maxLatencyMicros)
- .append(',').append(e.exceptionCount)
- .append(',').append(e.maxRequestSizeBytes)
- .append(',').append(e.maxReplySizeBytes)
- .append(',').append(e.callCount);
- pw.println(sb);
- }
- }
- pw.println();
- } else {
- pw.println("Sampled stats " + datasetSizeDesc
- + "(call_desc, cpu_time, call_count, exception_count):");
- List<CallStat> sampledStatsList = mSampledEntries.getCallStatsList();
- // Show all if verbose, otherwise 90th percentile
- if (!verbose) {
- sampledStatsList = getHighestValues(sampledStatsList,
- value -> value.cpuTimeMicros, 0.9);
- }
- for (CallStat e : sampledStatsList) {
+ List<UidEntry> topEntries = verbose ? entries
+ : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
+ pw.println("Per-UID raw data " + datasetSizeDesc
+ + "(package/uid, call_desc, cpu_time_micros, max_cpu_time_micros, "
+ + "latency_time_micros, max_latency_time_micros, exception_count, "
+ + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
+ + "call_count):");
+ for (UidEntry uidEntry : topEntries) {
+ for (CallStat e : uidEntry.getCallStatsList()) {
sb.setLength(0);
- sb.append(" ").append(e)
- .append(',').append(e.cpuTimeMicros * mPeriodicSamplingInterval)
- .append(',').append(e.callCount)
- .append(',').append(e.exceptionCount);
+ sb.append(" ")
+ .append(uidToString(uidEntry.uid, appIdToPkgNameMap))
+ .append(',').append(e)
+ .append(',').append(e.cpuTimeMicros)
+ .append(',').append(e.maxCpuTimeMicros)
+ .append(',').append(e.latencyMicros)
+ .append(',').append(e.maxLatencyMicros)
+ .append(',').append(mDetailedTracking ? e.exceptionCount : '_')
+ .append(',').append(mDetailedTracking ? e.maxRequestSizeBytes : '_')
+ .append(',').append(mDetailedTracking ? e.maxReplySizeBytes : '_')
+ .append(',').append(e.recordedCallCount)
+ .append(',').append(e.callCount);
pw.println(sb);
}
- pw.println();
}
+ pw.println();
pw.println("Per-UID Summary " + datasetSizeDesc
- + "(cpu_time, % of total cpu_time, call_count, exception_count, package/uid):");
+ + "(cpu_time, % of total cpu_time, recorded_call_count, call_count, package/uid):");
List<UidEntry> summaryEntries = verbose ? entries
: getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
for (UidEntry entry : summaryEntries) {
String uidStr = uidToString(entry.uid, appIdToPkgNameMap);
- pw.println(String.format(" %10d %3.0f%% %8d %3d %s",
- entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime, entry.callCount,
- entry.exceptionCount, uidStr));
+ pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
+ entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
+ entry.recordedCallCount, entry.callCount, uidStr));
}
pw.println();
pw.println(String.format(" Summary: total_cpu_time=%d, "
- + "calls_count=%d, avg_call_cpu_time=%.0f",
- totalCpuTime, totalCallsCount, (double)totalCpuTime / totalCallsCount));
+ + "calls_count=%d, avg_call_cpu_time=%.0f",
+ totalCpuTime, totalCallsCount, (double)totalCpuTime / totalRecordedCallsCount));
pw.println();
pw.println("Exceptions thrown (exception_count, class_name):");
@@ -340,10 +290,15 @@
// We cannot use new ArrayList(Collection) constructor because MapCollections does not
// implement toArray method.
mExceptionCounts.entrySet().iterator().forEachRemaining(
- (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
+ (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second));
for (Pair<String, Integer> entry : exceptionEntries) {
- pw.println(String.format(" %6d %s", entry.second, entry.first));
+ pw.println(String.format(" %6d %s", entry.second, entry.first));
+ }
+
+ if (!mDetailedTracking && mPeriodicSamplingInterval != 1) {
+ pw.println("");
+ pw.println("/!\\ Displayed data is sampled. See sampling interval at the top.");
}
}
@@ -366,23 +321,14 @@
return SystemClock.elapsedRealtimeNanos() / 1000;
}
- public static BinderCallsStats getInstance() {
- return sInstance;
+ private boolean shouldTrackCall() {
+ return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
}
public void setDetailedTracking(boolean enabled) {
synchronized (mLock) {
- if (enabled != mDetailedTracking) {
- mDetailedTracking = enabled;
- reset();
- }
- }
- }
-
- public void setEnabled(boolean enabled) {
- synchronized (mLock) {
- if (enabled != mEnabled) {
- mEnabled = enabled;
+ if (enabled != mDetailedTracking) {
+ mDetailedTracking = enabled;
reset();
}
}
@@ -401,7 +347,6 @@
synchronized (mLock) {
mUidEntries.clear();
mExceptionCounts.clear();
- mSampledEntries.mCallStats.clear();
mStartTime = System.currentTimeMillis();
}
}
@@ -413,11 +358,13 @@
public int uid;
public String className;
public String methodName;
+ public boolean screenInteractive;
public long cpuTimeMicros;
public long maxCpuTimeMicros;
public long latencyMicros;
public long maxLatencyMicros;
public long callCount;
+ public long recordedCallCount;
public long maxRequestSizeBytes;
public long maxReplySizeBytes;
public long exceptionCount;
@@ -426,15 +373,25 @@
@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;
+ // Number of calls for which we collected data for. We do not record data for all the calls
+ // when sampling is on.
+ public long recordedCallCount;
+ // Real number of total calls.
+ public long callCount;
+ // Total CPU of all for all the recorded calls.
+ // Approximate total CPU usage can be computed by
+ // cpuTimeMicros * callCount / recordedCallCount
public long cpuTimeMicros;
public long maxCpuTimeMicros;
+ // Total latency of all for all the recorded calls.
+ // Approximate average latency can be computed by
+ // latencyMicros * callCount / recordedCallCount
public long latencyMicros;
public long maxLatencyMicros;
- public long callCount;
// The following fields are only computed if mDetailedTracking is set.
public long maxRequestSizeBytes;
public long maxReplySizeBytes;
@@ -443,63 +400,75 @@
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();
- CallStat sampledCallStat;
- }
@VisibleForTesting
public static class UidEntry {
int uid;
- public long cpuTimeMicros;
+ // Number of calls for which we collected data for. We do not record data for all the calls
+ // when sampling is on.
+ public long recordedCallCount;
+ // Real number of total calls.
public long callCount;
- public int exceptionCount;
+ // Total CPU of all for all the recorded calls.
+ // Approximate total CPU usage can be computed by
+ // cpuTimeMicros * callCount / recordedCallCount
+ public long cpuTimeMicros;
UidEntry(int uid) {
this.uid = uid;
}
// Aggregate time spent per each call name: call_desc -> cpu_time_micros
- Map<CallStat, CallStat> mCallStats = new ArrayMap<>();
+ private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
+ private CallStatKey mTempKey = new CallStatKey();
- CallStat getOrCreate(CallStat callStat) {
- CallStat mapCallStat = mCallStats.get(callStat);
+ 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;
}
@@ -508,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;
@@ -551,11 +520,6 @@
}
@VisibleForTesting
- public UidEntry getSampledEntries() {
- return mSampledEntries;
- }
-
- @VisibleForTesting
public ArrayMap<String, Integer> getExceptionCounts() {
return mExceptionCounts;
}
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/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index a9cd5c8..e37e650 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -30,14 +30,13 @@
import android.util.Slog;
import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
+import dalvik.system.RuntimeHooks;
import dalvik.system.VMRuntime;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
-import java.util.TimeZone;
import java.util.logging.LogManager;
-import org.apache.harmony.luni.internal.util.TimezoneGetter;
/**
* Main entry point for runtime initialization. Not for
@@ -195,19 +194,13 @@
* the default handler, but not the pre handler.
*/
LoggingHandler loggingHandler = new LoggingHandler();
- Thread.setUncaughtExceptionPreHandler(loggingHandler);
+ RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
/*
- * Install a TimezoneGetter subclass for ZoneInfo.db
+ * Install a time zone supplier that uses the Android persistent time zone system property.
*/
- TimezoneGetter.setInstance(new TimezoneGetter() {
- @Override
- public String getId() {
- return SystemProperties.get("persist.sys.timezone");
- }
- });
- TimeZone.setDefault(null);
+ RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));
/*
* Sets handler for java.util.logging to use Android log facilities.
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index da19560..8f87f91 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -21,9 +21,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.icu.impl.CacheValue;
-import android.icu.text.DecimalFormatSymbols;
-import android.icu.util.ULocale;
import android.opengl.EGL14;
import android.os.Build;
import android.os.Environment;
@@ -122,9 +119,9 @@
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
- bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
- beginIcuCachePinning();
- bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
+ bootTimingsTraceLog.traceBegin("BeginPreload");
+ beginPreload();
+ bootTimingsTraceLog.traceEnd(); // BeginPreload
bootTimingsTraceLog.traceBegin("PreloadClasses");
preloadClasses();
bootTimingsTraceLog.traceEnd(); // PreloadClasses
@@ -142,7 +139,7 @@
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
- endIcuCachePinning();
+ endPreload();
warmUpJcaProviders();
Log.d(TAG, "end preload");
@@ -156,27 +153,16 @@
preload(new TimingsTraceLog("ZygoteInitTiming_lazy", Trace.TRACE_TAG_DALVIK));
}
- private static void beginIcuCachePinning() {
- // Pin ICU data in memory from this point that would normally be held by soft references.
- // Without this, any references created immediately below or during class preloading
- // would be collected when the Zygote GC runs in gcAndFinalize().
- Log.i(TAG, "Installing ICU cache reference pinning...");
+ private static void beginPreload() {
+ Log.i(TAG, "Calling ZygoteHooks.beginPreload()");
- CacheValue.setStrength(CacheValue.Strength.STRONG);
-
- Log.i(TAG, "Preloading ICU data...");
- // Explicitly exercise code to cache data apps are likely to need.
- ULocale[] localesToPin = { ULocale.ROOT, ULocale.US, ULocale.getDefault() };
- for (ULocale uLocale : localesToPin) {
- new DecimalFormatSymbols(uLocale);
- }
+ ZygoteHooks.onBeginPreload();
}
- private static void endIcuCachePinning() {
- // All cache references created by ICU from this point will be soft.
- CacheValue.setStrength(CacheValue.Strength.SOFT);
+ private static void endPreload() {
+ ZygoteHooks.onEndPreload();
- Log.i(TAG, "Uninstalled ICU cache reference pinning...");
+ Log.i(TAG, "Called ZygoteHooks.endPreload()");
}
private static void preloadSharedLibraries() {
@@ -436,15 +422,8 @@
* softly- and final-reachable objects, along with any other garbage.
* This is only useful just before a fork().
*/
- /*package*/ static void gcAndFinalize() {
- final VMRuntime runtime = VMRuntime.getRuntime();
-
- /* runFinalizationSync() lets finalizers be called in Zygote,
- * which doesn't have a HeapWorker thread.
- */
- System.gc();
- runtime.runFinalizationSync();
- System.gc();
+ private static void gcAndFinalize() {
+ ZygoteHooks.gcAndFinalize();
}
/**
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index c3d33ca8..4b66267 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -309,7 +309,7 @@
}
@SuppressWarnings("unchecked")
- public static @NonNull <T> T[] concat(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
+ public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
final int an = (a != null) ? a.length : 0;
final int bn = (b != null) ? b.length : 0;
if (an == 0 && bn == 0) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2fc5df4..6856e29 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -84,8 +84,8 @@
"android_view_VelocityTracker.cpp",
"android_text_AndroidCharacter.cpp",
"android_text_Hyphenator.cpp",
+ "android_text_LineBreaker.cpp",
"android_text_MeasuredParagraph.cpp",
- "android_text_StaticLayout.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
"android_os_HidlSupport.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0ebfc16..1820888 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -184,7 +184,7 @@
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
extern int register_android_text_MeasuredParagraph(JNIEnv* env);
-extern int register_android_text_StaticLayout(JNIEnv *env);
+extern int register_android_text_LineBreaker(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
@@ -1335,7 +1335,7 @@
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_text_MeasuredParagraph),
- REG_JNI(register_android_text_StaticLayout),
+ REG_JNI(register_android_text_LineBreaker),
REG_JNI(register_android_view_InputDevice),
REG_JNI(register_android_view_KeyCharacterMap),
REG_JNI(register_android_os_Process),
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index ca5e1e4..f8f841c 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -189,8 +189,7 @@
void HwBinderDeathRecipientList::remove(const sp<HwBinderDeathRecipient>& recipient) {
AutoMutex _l(mLock);
- List< sp<HwBinderDeathRecipient> >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
if (*iter == recipient) {
mList.erase(iter);
return;
@@ -201,12 +200,13 @@
sp<HwBinderDeathRecipient> HwBinderDeathRecipientList::find(jobject recipient) {
AutoMutex _l(mLock);
- for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
- if (deathRecipient->matches(recipient)) {
- return deathRecipient;
+ for(auto iter = mList.rbegin(); iter != mList.rend(); iter++) {
+ if ((*iter)->matches(recipient)) {
+ return (*iter);
}
}
- return NULL;
+
+ return nullptr;
}
Mutex& HwBinderDeathRecipientList::lock() {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 63aad0a..4b5a4c8 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -20,9 +20,9 @@
#include <android-base/macros.h>
#include <hwbinder/Binder.h>
#include <jni.h>
-#include <utils/List.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
+#include <vector>
namespace android {
@@ -33,7 +33,7 @@
class HwBinderDeathRecipient;
class HwBinderDeathRecipientList : public RefBase {
- List< sp<HwBinderDeathRecipient> > mList;
+ std::vector<sp<HwBinderDeathRecipient>> mList;
Mutex mLock;
public:
@@ -42,6 +42,8 @@
void add(const sp<HwBinderDeathRecipient>& recipient);
void remove(const sp<HwBinderDeathRecipient>& recipient);
+
+ // finds the most recently added matching death recipient
sp<HwBinderDeathRecipient> find(jobject recipient);
Mutex& lock(); // Use with care; specifically for mutual exclusion during binder death
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_LineBreaker.cpp
similarity index 92%
rename from core/jni/android_text_StaticLayout.cpp
rename to core/jni/android_text_LineBreaker.cpp
index fec5b69..dac108e 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_LineBreaker.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "StaticLayout"
+#define LOG_TAG "LineBreaker"
#include "unicode/locid.h"
#include "unicode/brkiter.h"
@@ -76,11 +76,15 @@
jintArrayToFloatVector(env, indents)));
}
-// CriticalNative
static void nFinish(jlong nativePtr) {
delete toNative(nativePtr);
}
+// CriticalNative
+static jlong nGetReleaseFunc() {
+ return reinterpret_cast<jlong>(nFinish);
+}
+
static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
jfloatArray recycleWidths, jfloatArray recycleAscents,
jfloatArray recycleDescents, jintArray recycleFlags,
@@ -144,9 +148,6 @@
recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
recycleFlags, recycleLength, result);
- env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(),
- measuredText->widths.data());
-
return static_cast<jint>(result.breakPoints.size());
}
@@ -160,7 +161,7 @@
")J", (void*) nInit},
// Critical Natives
- {"nFinish", "(J)V", (void*) nFinish},
+ {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},
// Regular JNI
{"nComputeLineBreaks", "("
@@ -178,21 +179,20 @@
"I" // indentsOffset
// Outputs
- "Landroid/text/StaticLayout$LineBreaks;" // recycle
+ "Landroid/text/NativeLineBreaker$LineBreaks;" // recycle
"I" // recycleLength
"[I" // recycleBreaks
"[F" // recycleWidths
"[F" // recycleAscents
"[F" // recycleDescents
"[I" // recycleFlags
- "[F" // charWidths
")I", (void*) nComputeLineBreaks}
};
-int register_android_text_StaticLayout(JNIEnv* env)
+int register_android_text_LineBreaker(JNIEnv* env)
{
gLineBreaks_class = MakeGlobalRefOrDie(env,
- FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
+ FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks"));
gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
@@ -200,7 +200,8 @@
gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
- return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
+ return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker",
+ gMethods, NELEM(gMethods));
}
}
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index 9eb6f8d..18f509c 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -109,6 +109,10 @@
return r;
}
+static jfloat nGetCharWidthAt(jlong ptr, jint offset) {
+ return toMeasuredParagraph(ptr)->widths[offset];
+}
+
// Regular JNI
static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end,
jobject bounds) {
@@ -138,23 +142,29 @@
return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage());
}
-static const JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMTBuilderMethods[] = {
// MeasuredParagraphBuilder native functions.
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
{"nBuildNativeMeasuredParagraph", "(J[CZZ)J", (void*) nBuildNativeMeasuredParagraph},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
+};
+static const JNINativeMethod gMTMethods[] = {
// MeasuredParagraph native functions.
{"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives
{"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
{"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native
+ {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt}, // Critical Native
};
int register_android_text_MeasuredParagraph(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/text/MeasuredParagraph", gMethods, NELEM(gMethods));
+ return RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph",
+ gMTMethods, NELEM(gMTMethods))
+ + RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph$Builder",
+ gMTBuilderMethods, NELEM(gMTBuilderMethods));
}
}
diff --git a/core/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 472df1a..c2ff9c9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1489,9 +1489,9 @@
android:protectionLevel="signature|privileged" />
<!-- @hide Allows internal management of Wi-Fi connectivity state when on
- permission review mode.
+ wireless consent mode.
<p>Not for use by third-party applications. -->
- <permission android:name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED"
+ <permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED"
android:protectionLevel="signature" />
<!-- #SystemApi @hide Allows an app to bypass Private DNS.
@@ -1590,9 +1590,9 @@
<permission android:name="android.permission.NFC_HANDOVER_STATUS"
android:protectionLevel="signature|privileged" />
- <!-- @hide Allows internal management of Bluetooth state when on permission review mode.
+ <!-- @hide Allows internal management of Bluetooth state when on wireless consent mode.
<p>Not for use by third-party applications. -->
- <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED"
+ <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
android:protectionLevel="signature" />
<!-- ================================== -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 44eea30c..5b79c59 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3113,6 +3113,9 @@
the alpha channel of the outlineAmbientShadowColor (typically opaque), and the
{@link android.R.attr#ambientShadowAlpha} theme attribute. -->
<attr name="outlineAmbientShadowColor" format="color" />
+
+ <!-- Whether to allow the rendering system to force this View to render as light-on-dark. -->
+ <attr name="allowForceDark" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 14c9215..3053fa1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3224,12 +3224,11 @@
<string-array translatable="false" name="config_defaultFirstUserRestrictions">
</string-array>
- <!-- Specifies whether the permissions needed by a legacy app should be
- reviewed before any of its components can run. A legacy app is one
- with targetSdkVersion < 23, i.e apps using the old permission model.
- If review is not required, permissions are reviewed before the app
- is installed. -->
- <bool name="config_permissionReviewRequired">false</bool>
+ <!-- Specifies whether certain permissions should be individually controlled. -->
+ <bool name="config_permissionsIndividuallyControlled">false</bool>
+
+ <!-- Specifies whether the user has to give consent to manage wireless (wifi + bluetooth). -->
+ <bool name="config_wirelessConsentRequired">false</bool>
<!-- Default value for android:focusableInTouchMode for some framework scrolling containers.
ListView/GridView are notably absent since this is their default anyway.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6189971..5aaabc7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2907,6 +2907,7 @@
<public name="opticalInsetTop" />
<public name="opticalInsetRight" />
<public name="opticalInsetBottom" />
+ <public name="allowForceDark" />
</public-group>
<public-group type="style" first-id="0x010302e2">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 03f965b..1c66b2b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3006,7 +3006,8 @@
<!-- Default first user restrictions -->
<java-symbol type="array" name="config_defaultFirstUserRestrictions" />
- <java-symbol type="bool" name="config_permissionReviewRequired" />
+ <java-symbol type="bool" name="config_permissionsIndividuallyControlled" />
+ <java-symbol type="bool" name="config_wirelessConsentRequired" />
<!-- Global actions icons -->
<java-symbol type="drawable" name="ic_restart" />
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/database/DatabaseUtilsTest.java b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
new file mode 100644
index 0000000..7c206d7
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database;
+
+import static android.database.DatabaseUtils.bindSelection;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DatabaseUtilsTest {
+ private static final Object[] ARGS = { "baz", 4, null };
+
+ @Test
+ public void testBindSelection_none() throws Exception {
+ assertEquals(null,
+ bindSelection(null, ARGS));
+ assertEquals("",
+ bindSelection("", ARGS));
+ assertEquals("foo=bar",
+ bindSelection("foo=bar", ARGS));
+ }
+
+ @Test
+ public void testBindSelection_normal() throws Exception {
+ assertEquals("foo='baz'",
+ bindSelection("foo=?", ARGS));
+ assertEquals("foo='baz' AND bar=4",
+ bindSelection("foo=? AND bar=?", ARGS));
+ assertEquals("foo='baz' AND bar=4 AND meow=NULL",
+ bindSelection("foo=? AND bar=? AND meow=?", ARGS));
+ }
+
+ @Test
+ public void testBindSelection_whitespace() throws Exception {
+ assertEquals("BETWEEN 5 AND 10",
+ bindSelection("BETWEEN? AND ?", 5, 10));
+ assertEquals("IN 'foo'",
+ bindSelection("IN?", "foo"));
+ }
+
+ @Test
+ public void testBindSelection_indexed() throws Exception {
+ assertEquals("foo=10 AND bar=11 AND meow=1",
+ bindSelection("foo=?10 AND bar=? AND meow=?1",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
+ }
+}
diff --git a/core/tests/coretests/src/android/net/UriCodecTest.java b/core/tests/coretests/src/android/net/UriCodecTest.java
new file mode 100644
index 0000000..7fe9402
--- /dev/null
+++ b/core/tests/coretests/src/android/net/UriCodecTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import junit.framework.TestCase;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for {@link UriCodec}
+ */
+public class UriCodecTest extends TestCase {
+
+ public void testDecode_emptyString_returnsEmptyString() {
+ assertEquals("", UriCodec.decode("",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */));
+ }
+
+ public void testDecode_wrongHexDigit_fails() {
+ try {
+ // %p in the end.
+ UriCodec.decode("ab%2f$%C4%82%25%e0%a1%80%p",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */);
+ fail("Expected URISyntaxException");
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ }
+
+ public void testDecode_secondHexDigitWrong_fails() {
+ try {
+ // %1p in the end.
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%1p",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */);
+ fail("Expected URISyntaxException");
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ }
+
+ public void testDecode_endsWithPercent_fails() {
+ try {
+ // % in the end.
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */);
+ fail("Expected URISyntaxException");
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ }
+
+ public void testDecode_dontThrowException_appendsUnknownCharacter() {
+ assertEquals("ab/$\u0102%\u0840\ufffd",
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ false /* throwOnFailure */));
+ }
+
+ public void testDecode_convertPlus() {
+ assertEquals("ab/$\u0102% \u0840",
+ UriCodec.decode("ab%2f$%c4%82%25+%e0%a1%80",
+ true /* convertPlus */,
+ StandardCharsets.UTF_8,
+ false /* throwOnFailure */));
+ }
+
+ // Last character needs decoding (make sure we are flushing the buffer with chars to decode).
+ public void testDecode_lastCharacter() {
+ assertEquals("ab/$\u0102%\u0840",
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */));
+ }
+
+ // Check that a second row of encoded characters is decoded properly (internal buffers are
+ // reset properly).
+ public void testDecode_secondRowOfEncoded() {
+ assertEquals("ab/$\u0102%\u0840aa\u0840",
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80aa%e0%a1%80",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */));
+ }
+}
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
index 952a64d..425ab89 100644
--- a/core/tests/coretests/src/android/os/WorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -378,6 +378,17 @@
assertEquals(75, ws1.getWorkChains().get(0).getAttributionUid());
}
+ public void testRemove_fromSameWorkSource() {
+ WorkSource ws1 = new WorkSource(50, "foo");
+ WorkSource ws2 = ws1;
+ ws2.add(ws1);
+ assertTrue(ws2.remove(ws1));
+
+ assertEquals(0, ws1.size());
+ assertEquals(50, ws1.get(0));
+ assertEquals("foo", ws1.getName(0));
+ }
+
public void testTransferWorkChains() {
WorkSource ws1 = new WorkSource();
WorkChain wc1 = ws1.createWorkChain().addNode(100, "tag");
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 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/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 6f1d47d..f3d6013 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import android.content.Context;
import android.graphics.Typeface;
@@ -72,7 +73,7 @@
assertEquals(0, mt.getWidths().size());
assertEquals(0, mt.getSpanEndCache().size());
assertEquals(0, mt.getFontMetrics().size());
- assertEquals(0, mt.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
// Recycle it
MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt);
@@ -84,7 +85,7 @@
assertEquals(0, mt2.getWidths().size());
assertEquals(0, mt2.getSpanEndCache().size());
assertEquals(0, mt2.getFontMetrics().size());
- assertEquals(0, mt2.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
mt2.recycle();
}
@@ -106,7 +107,7 @@
assertEquals(10, mt.getWidths().get(2), 0);
assertEquals(0, mt.getSpanEndCache().size());
assertEquals(0, mt.getFontMetrics().size());
- assertEquals(0, mt.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
// Recycle it
MeasuredParagraph mt2 =
@@ -123,7 +124,7 @@
assertEquals(5, mt2.getWidths().get(2), 0);
assertEquals(0, mt2.getSpanEndCache().size());
assertEquals(0, mt2.getFontMetrics().size());
- assertEquals(0, mt2.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
mt2.recycle();
}
@@ -143,7 +144,7 @@
assertEquals(1, mt.getSpanEndCache().size());
assertEquals(3, mt.getSpanEndCache().get(0));
assertNotEquals(0, mt.getFontMetrics().size());
- assertNotEquals(0, mt.getNativePtr());
+ assertNotNull(mt.getNativeMeasuredParagraph());
// Recycle it
MeasuredParagraph mt2 =
@@ -158,7 +159,7 @@
assertEquals(1, mt2.getSpanEndCache().size());
assertEquals(4, mt2.getSpanEndCache().get(0));
assertNotEquals(0, mt2.getFontMetrics().size());
- assertNotEquals(0, mt2.getNativePtr());
+ assertNotNull(mt.getNativeMeasuredParagraph());
mt2.recycle();
}
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index 3aee583..af608c3 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -18,13 +18,18 @@
import android.support.test.filters.LargeTest;
+import junit.framework.TestCase;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Random;
-import junit.framework.TestCase;
+import java.util.stream.Collectors;
@LargeTest
public class Base64Test extends TestCase {
@@ -530,4 +535,74 @@
assertEquals(plain, actual);
}
}
+
+ public void testOutputStream_ioExceptionDuringClose() {
+ OutputStream out = new OutputStream() {
+ @Override public void write(int b) throws IOException { }
+ @Override public void close() throws IOException {
+ throw new IOException("close()");
+ }
+ };
+ OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+ try {
+ out2.close();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testOutputStream_ioExceptionDuringCloseAndWrite() {
+ OutputStream out = new OutputStream() {
+ @Override public void write(int b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void close() throws IOException {
+ throw new IOException("close()");
+ }
+ };
+ OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+ try {
+ out2.close();
+ fail();
+ } catch (IOException expected) {
+ // Base64OutputStream write()s pending (possibly empty) data
+ // before close(), so the IOE from write() should be thrown and
+ // any later exception suppressed.
+ assertEquals("write()", expected.getMessage());
+ Throwable[] suppressed = expected.getSuppressed();
+ List<String> suppressedMessages = Arrays.asList(suppressed).stream()
+ .map((e) -> e.getMessage())
+ .collect(Collectors.toList());
+ assertEquals(Collections.singletonList("close()"), suppressedMessages);
+ }
+ }
+
+ public void testOutputStream_ioExceptionDuringWrite() {
+ OutputStream out = new OutputStream() {
+ @Override public void write(int b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ throw new IOException("write()");
+ }
+ };
+ // Base64OutputStream write()s pending (possibly empty) data
+ // before close(), so the IOE from write() should be thrown.
+ OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+ try {
+ out2.close();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 44b1f08..0dd7685 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -119,6 +119,10 @@
return false;
}
+ public int getSoftKeyboardShowMode() {
+ return 0;
+ }
+
public void setSoftKeyboardCallbackEnabled(boolean enabled) {}
public boolean isAccessibilityButtonAvailable() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index d46c154..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;
@@ -33,6 +35,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import static org.junit.Assert.assertEquals;
@@ -48,9 +51,10 @@
public void testDetailedOff() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(false);
+ bcs.setSamplingInterval(5);
Binder binder = new Binder();
- BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ CallSession callSession = bcs.callStarted(binder, 1);
bcs.time += 10;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -58,44 +62,31 @@
assertEquals(1, uidEntries.size());
BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
Assert.assertNotNull(uidEntry);
+ List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
assertEquals(1, uidEntry.callCount);
+ assertEquals(1, uidEntry.recordedCallCount);
assertEquals(10, uidEntry.cpuTimeMicros);
- assertEquals("Detailed tracking off - no entries should be returned",
- 0, uidEntry.getCallStatsList().size());
+ assertEquals(binder.getClass().getName(), callStatsList.get(0).className);
+ assertEquals(1, callStatsList.get(0).transactionCode);
- BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
- List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(1, sampledCallStatsList.size());
-
-
- assertEquals(1, sampledCallStatsList.get(0).callCount);
- assertEquals(10, sampledCallStatsList.get(0).cpuTimeMicros);
- assertEquals(binder.getClass().getName(), sampledCallStatsList.get(0).className);
- assertEquals(1, sampledCallStatsList.get(0).msg);
-
+ // CPU usage is sampled, should not be tracked here.
callSession = bcs.callStarted(binder, 1);
bcs.time += 20;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
- uidEntry = bcs.getUidEntries().get(TEST_UID);
assertEquals(2, uidEntry.callCount);
- // When sampling is enabled, cpu time is only measured for the first transaction in the
- // sampling interval, for others an average duration of previous transactions is used as
- // approximation
- assertEquals(20, uidEntry.cpuTimeMicros);
- sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(1, sampledCallStatsList.size());
+ assertEquals(1, uidEntry.recordedCallCount);
+ assertEquals(10, uidEntry.cpuTimeMicros);
+ assertEquals(1, callStatsList.size());
callSession = bcs.callStarted(binder, 2);
bcs.time += 50;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
uidEntry = bcs.getUidEntries().get(TEST_UID);
assertEquals(3, uidEntry.callCount);
-
- // This is the first transaction of a new type, so the real CPU time will be measured
- assertEquals(70, uidEntry.cpuTimeMicros);
- sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(2, sampledCallStatsList.size());
+ assertEquals(1, uidEntry.recordedCallCount);
+ // Still sampled even for another API.
+ callStatsList = uidEntry.getCallStatsList();
+ assertEquals(2, callStatsList.size());
}
@Test
@@ -104,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);
@@ -116,15 +107,11 @@
assertEquals(10, uidEntry.cpuTimeMicros);
assertEquals(1, uidEntry.getCallStatsList().size());
- BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
- assertEquals("Sampling is not used when detailed tracking on",
- 0, bcs.getSampledEntries().getCallStatsList().size());
-
List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
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;
@@ -149,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());
@@ -199,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);
@@ -216,13 +180,57 @@
BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
Assert.assertNotNull(uidEntry);
assertEquals(3, uidEntry.callCount);
- assertEquals(70, uidEntry.cpuTimeMicros);
- assertEquals("Detailed tracking off - no entries should be returned",
- 0, uidEntry.getCallStatsList().size());
+ assertEquals(60 /* 10 + 50 */, uidEntry.cpuTimeMicros);
- BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
- List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(1, sampledCallStatsList.size());
+ List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ assertEquals(1, callStatsList.size());
+ BinderCallsStats.CallStat callStats = callStatsList.get(0);
+ assertEquals(3, callStats.callCount);
+ assertEquals(2, callStats.recordedCallCount);
+ assertEquals(60, callStats.cpuTimeMicros);
+ assertEquals(50, callStats.maxCpuTimeMicros);
+ assertEquals(0, callStats.maxRequestSizeBytes);
+ assertEquals(0, callStats.maxReplySizeBytes);
+ }
+
+ @Test
+ public void testSamplingWithDifferentApis() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(false);
+ bcs.setSamplingInterval(2);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 2 /* another method */);
+ bcs.time += 1000; // shoud be ignored.
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(1, uidEntries.size());
+ BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+ assertEquals(2, uidEntry.callCount);
+ assertEquals(1, uidEntry.recordedCallCount);
+ assertEquals(10, uidEntry.cpuTimeMicros);
+
+ List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ assertEquals(2, callStatsList.size());
+
+ BinderCallsStats.CallStat callStats = callStatsList.get(0);
+ assertEquals(1, callStats.callCount);
+ assertEquals(1, callStats.recordedCallCount);
+ assertEquals(10, callStats.cpuTimeMicros);
+ assertEquals(10, callStats.maxCpuTimeMicros);
+
+ // Only call count should is tracked, rest is sampled.
+ callStats = callStatsList.get(1);
+ assertEquals(1, callStats.callCount);
+ assertEquals(0, callStats.recordedCallCount);
+ assertEquals(0, callStats.cpuTimeMicros);
+ assertEquals(0, callStats.maxCpuTimeMicros);
}
@Test
@@ -232,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);
}
@@ -250,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);
@@ -266,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);
@@ -285,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);
@@ -312,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);
@@ -335,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);
@@ -348,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());
@@ -359,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);
@@ -374,6 +382,7 @@
assertEquals(20, stat.latencyMicros);
assertEquals(20, stat.maxLatencyMicros);
assertEquals(1, stat.callCount);
+ assertEquals(1, stat.recordedCallCount);
assertEquals(REQUEST_SIZE, stat.maxRequestSizeBytes);
assertEquals(REPLY_SIZE, stat.maxReplySizeBytes);
assertEquals(0, stat.exceptionCount);
@@ -385,6 +394,14 @@
long elapsedTime = 0;
TestBinderCallsStats() {
+ // Make random generator not random.
+ super(new Random() {
+ int mCallCount = 0;
+
+ 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/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 6464ad3..39bb84a 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -16,6 +16,8 @@
package com.android.internal.util;
+import static com.android.internal.util.ArrayUtils.concatElements;
+
import static org.junit.Assert.assertArrayEquals;
import junit.framework.TestCase;
@@ -156,23 +158,23 @@
public void testConcatEmpty() throws Exception {
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, null, null));
+ concatElements(Long.class, null, null));
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, new Long[] {}, null));
+ concatElements(Long.class, new Long[] {}, null));
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, null, new Long[] {}));
+ concatElements(Long.class, null, new Long[] {}));
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, new Long[] {}, new Long[] {}));
+ concatElements(Long.class, new Long[] {}, new Long[] {}));
}
- public void testConcat() throws Exception {
+ public void testconcatElements() throws Exception {
assertArrayEquals(new Long[] { 1L },
- ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] {}));
+ concatElements(Long.class, new Long[] { 1L }, new Long[] {}));
assertArrayEquals(new Long[] { 1L },
- ArrayUtils.concat(Long.class, new Long[] {}, new Long[] { 1L }));
+ concatElements(Long.class, new Long[] {}, new Long[] { 1L }));
assertArrayEquals(new Long[] { 1L, 2L },
- ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] { 2L }));
+ concatElements(Long.class, new Long[] { 1L }, new Long[] { 2L }));
assertArrayEquals(new Long[] { 1L, 2L, 3L, 4L },
- ArrayUtils.concat(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
+ concatElements(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
}
}
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 97130f1..ffe6abc1 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -501,7 +501,7 @@
contextStart - paraStart,
contextEnd - contextStart,
x, y, isRtl, paint.getNativeInstance(),
- mp.getNativePtr());
+ mp.getNativeMeasuredParagraph().getNativePtr());
return;
}
}
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/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index bac7a4d..1b15dbd 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -15,32 +15,38 @@
*/
#include "CanvasTransform.h"
+#include "utils/Color.h"
#include "Properties.h"
+#include <ui/ColorSpace.h>
#include <SkColorFilter.h>
#include <SkPaint.h>
-#include <log/log.h>
+
+#include <algorithm>
+#include <cmath>
namespace android::uirenderer {
static SkColor makeLight(SkColor color) {
- SkScalar hsv[3];
- SkColorToHSV(color, hsv);
- if (hsv[1] > .2f) return color;
- // hsv[1] *= .85f;
- // hsv[2] = std::min(1.0f, std::max(hsv[2], 1 - hsv[2]) * 1.3f);
- hsv[2] = std::max(hsv[2], 1.1f - hsv[2]);
- return SkHSVToColor(SkColorGetA(color), hsv);
+ Lab lab = sRGBToLab(color);
+ float invertedL = std::min(110 - lab.L, 100.0f);
+ if (invertedL > lab.L) {
+ lab.L = invertedL;
+ return LabToSRGB(lab, SkColorGetA(color));
+ } else {
+ return color;
+ }
}
static SkColor makeDark(SkColor color) {
- SkScalar hsv[3];
- SkColorToHSV(color, hsv);
- if (hsv[1] > .2f) return color;
- // hsv[1] *= .85f;
- // hsv[2] = std::max(0.0f, std::min(hsv[2], 1 - hsv[2]) * .7f);
- hsv[2] = std::min(hsv[2], 1.1f - hsv[2]);
- return SkHSVToColor(SkColorGetA(color), hsv);
+ Lab lab = sRGBToLab(color);
+ float invertedL = std::min(110 - lab.L, 100.0f);
+ if (invertedL < lab.L) {
+ lab.L = invertedL;
+ return LabToSRGB(lab, SkColorGetA(color));
+ } else {
+ return color;
+ }
}
static SkColor transformColor(ColorTransform transform, SkColor color) {
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 6408ce6..ab80d3d 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -286,7 +286,7 @@
eglDestroySyncKHR(display, fence);
}
- return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+ return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
}
}; // namespace android::uirenderer
diff --git a/libs/hwui/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/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index a401b3f..7a8d026 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -17,6 +17,7 @@
#include "Caches.h"
#include "HardwareBitmapUploader.h"
+#include "Properties.h"
#include "renderthread/RenderProxy.h"
#include "utils/Color.h"
@@ -34,6 +35,7 @@
#include <SkToSRGBColorFilter.h>
#include <limits>
+#include <SkHighContrastFilter.h>
namespace android {
@@ -195,11 +197,13 @@
mPixelStorage.ashmem.size = mappedSize;
}
-Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
+Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette)
: SkPixelRef(info.width(), info.height(), nullptr,
bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride())
, mInfo(validateAlpha(info))
- , mPixelStorageType(PixelStorageType::Hardware) {
+ , mPixelStorageType(PixelStorageType::Hardware)
+ , mPalette(palette)
+ , mPaletteGenerationId(getGenerationID()) {
mPixelStorage.hardware.buffer = buffer;
buffer->incStrong(buffer);
setImmutable(); // HW bitmaps are always immutable
@@ -326,7 +330,106 @@
if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
*outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
}
+
+ // TODO: Move this to the canvas (or other?) layer where we have the target lightness
+ // mode and can selectively do the right thing.
+ if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) {
+ SkHighContrastConfig config;
+ config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+ *outputColorFilter = SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter);
+ }
return image;
}
+class MinMaxAverage {
+public:
+
+ void add(float sample) {
+ if (mCount == 0) {
+ mMin = sample;
+ mMax = sample;
+ } else {
+ mMin = std::min(mMin, sample);
+ mMax = std::max(mMax, sample);
+ }
+ mTotal += sample;
+ mCount++;
+ }
+
+ float average() {
+ return mTotal / mCount;
+ }
+
+ float min() {
+ return mMin;
+ }
+
+ float max() {
+ return mMax;
+ }
+
+ float delta() {
+ return mMax - mMin;
+ }
+
+private:
+ float mMin = 0.0f;
+ float mMax = 0.0f;
+ float mTotal = 0.0f;
+ int mCount = 0;
+};
+
+BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
+ ATRACE_CALL();
+
+ SkPixmap pixmap{info, addr, rowBytes};
+
+ // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
+ // Experiment with something simpler since we just want to figure out if it's "color-ful"
+ // and then the average perceptual lightness.
+
+ MinMaxAverage hue, saturation, value;
+ int sampledCount = 0;
+
+ // Sample a grid of 100 pixels to get an overall estimation of the colors in play
+ const int x_step = std::max(1, pixmap.width() / 10);
+ const int y_step = std::max(1, pixmap.height() / 10);
+ for (int x = 0; x < pixmap.width(); x += x_step) {
+ for (int y = 0; y < pixmap.height(); y += y_step) {
+ SkColor color = pixmap.getColor(x, y);
+ if (!info.isOpaque() && SkColorGetA(color) < 75) {
+ continue;
+ }
+
+ sampledCount++;
+ float hsv[3];
+ SkColorToHSV(color, hsv);
+ hue.add(hsv[0]);
+ saturation.add(hsv[1]);
+ value.add(hsv[2]);
+ }
+ }
+
+ // TODO: Tune the coverage threshold
+ if (sampledCount < 5) {
+ ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
+ sampledCount, info.width(), info.height(), (int) info.colorType(), (int) info.alphaType());
+ return BitmapPalette::Unknown;
+ }
+
+ ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = %f]",
+ sampledCount,
+ hue.min(), hue.max(), hue.average(),
+ saturation.min(), saturation.max(), saturation.average());
+
+ if (hue.delta() <= 20 && saturation.delta() <= .1f) {
+ if (value.average() >= .5f) {
+ return BitmapPalette::Light;
+ } else {
+ return BitmapPalette::Dark;
+ }
+ }
+ return BitmapPalette::Unknown;
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index dbd4456..d268042 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -34,6 +34,12 @@
Hardware,
};
+enum class BitmapPalette {
+ Unknown,
+ Light,
+ Dark,
+};
+
namespace uirenderer {
namespace renderthread {
class RenderThread;
@@ -63,7 +69,7 @@
Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
size_t rowBytes);
Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
- Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
+ Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette = BitmapPalette::Unknown);
int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
@@ -103,6 +109,20 @@
*/
sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
+ static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
+
+ static BitmapPalette computePalette(const SkBitmap& bitmap) {
+ return computePalette(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes());
+ }
+
+ BitmapPalette palette() {
+ if (!isHardware() && mPaletteGenerationId != getGenerationID()) {
+ mPalette = computePalette(info(), pixels(), rowBytes());
+ mPaletteGenerationId = getGenerationID();
+ }
+ return mPalette;
+ }
+
private:
virtual ~Bitmap();
void* getStorage() const;
@@ -111,6 +131,9 @@
const PixelStorageType mPixelStorageType;
+ BitmapPalette mPalette = BitmapPalette::Unknown;
+ uint32_t mPaletteGenerationId = -1;
+
bool mHasHardwareMipMap = false;
union {
diff --git a/libs/hwui/hwui/Canvas.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/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 75740e8..a3e7859 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,8 +16,10 @@
#include "Color.h"
-
#include <utils/Log.h>
+#include <ui/ColorSpace.h>
+
+#include <algorithm>
#include <cmath>
namespace android {
@@ -107,5 +109,97 @@
}
}
+template<typename T>
+static constexpr T clamp(T x, T min, T max) {
+ return x < min ? min : x > max ? max : x;
+}
+
+//static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+ float3{ 0.8951f, -0.7502f, 0.0389f},
+ float3{ 0.2664f, 1.7135f, -0.0685f},
+ float3{-0.1614f, 0.0367f, 1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+ float3 srcLMS = matrix * srcWhitePoint;
+ float3 dstLMS = matrix * dstWhitePoint;
+ return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+namespace LabColorSpace {
+
+static constexpr float A = 216.0f / 24389.0f;
+static constexpr float B = 841.0f / 108.0f;
+static constexpr float C = 4.0f / 29.0f;
+static constexpr float D = 6.0f / 29.0f;
+
+float3 toXyz(const Lab& lab) {
+ float3 v { lab.L, lab.a, lab.b };
+ v[0] = clamp(v[0], 0.0f, 100.0f);
+ v[1] = clamp(v[1], -128.0f, 128.0f);
+ v[2] = clamp(v[2], -128.0f, 128.0f);
+
+ float fy = (v[0] + 16.0f) / 116.0f;
+ float fx = fy + (v[1] * 0.002f);
+ float fz = fy - (v[2] * 0.005f);
+ float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
+ float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
+ float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
+
+ v[0] = X * ILLUMINANT_D50_XYZ[0];
+ v[1] = Y * ILLUMINANT_D50_XYZ[1];
+ v[2] = Z * ILLUMINANT_D50_XYZ[2];
+
+ return v;
+}
+
+Lab fromXyz(const float3& v) {
+ float X = v[0] / ILLUMINANT_D50_XYZ[0];
+ float Y = v[1] / ILLUMINANT_D50_XYZ[1];
+ float Z = v[2] / ILLUMINANT_D50_XYZ[2];
+
+ float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
+ float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
+ float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
+
+ float L = 116.0f * fy - 16.0f;
+ float a = 500.0f * (fx - fy);
+ float b = 200.0f * (fy - fz);
+
+ return Lab {
+ clamp(L, 0.0f, 100.0f),
+ clamp(a, -128.0f, 128.0f),
+ clamp(b, -128.0f, 128.0f)
+ };
+}
+
+};
+
+Lab sRGBToLab(SkColor color) {
+ auto colorSpace = ColorSpace::sRGB();
+ float3 rgb;
+ rgb.r = SkColorGetR(color) / 255.0f;
+ rgb.g = SkColorGetG(color) / 255.0f;
+ rgb.b = SkColorGetB(color) / 255.0f;
+ float3 xyz = colorSpace.rgbToXYZ(rgb);
+ float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+ xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
+ return LabColorSpace::fromXyz(xyz);
+}
+
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
+ auto colorSpace = ColorSpace::sRGB();
+ float3 xyz = LabColorSpace::toXyz(lab);
+ float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+ xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
+ float3 rgb = colorSpace.xyzToRGB(xyz);
+ return SkColorSetARGB(alpha,
+ static_cast<uint8_t>(rgb.r * 255),
+ static_cast<uint8_t>(rgb.g * 255),
+ static_cast<uint8_t>(rgb.b * 255));
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 2bec1f5..3c13a54 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -114,6 +114,16 @@
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+
+struct Lab {
+ float L;
+ float a;
+ float b;
+};
+
+Lab sRGBToLab(SkColor color);
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha);
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ee9e732..30a07ef 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -734,6 +734,11 @@
* calling Activity. If a Looper is specified with a {@link LocationListener}
* then callbacks are made on the supplied Looper thread.
*
+ * <p> When location callbacks are invoked, the system will hold a wakelock
+ * on your application's behalf for some period of time, but not
+ * indefinitely. If your application requires a long running wakelock
+ * within the location callback, you should acquire it yourself.
+ *
* <p class="note"> Prior to Jellybean, the minTime parameter was
* only a hint, and some location provider implementations ignored it.
* From Jellybean and onwards it is mandatory for Android compatible
diff --git a/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/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
deleted file mode 100644
index 95edc5e..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.drawer;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.settingslib.applications.InterestingConfigChanges;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-public class CategoryManager {
-
- public static final String SETTING_PKG = "com.android.settings";
-
- private static final String TAG = "CategoryManager";
-
- private static CategoryManager sInstance;
- private final InterestingConfigChanges mInterestingConfigChanges;
-
- // Tile cache (key: <packageName, activityName>, value: tile)
- private final Map<Pair<String, String>, Tile> mTileByComponentCache;
-
- // Tile cache (key: category key, value: category)
- private final Map<String, DashboardCategory> mCategoryByKeyMap;
-
- private List<DashboardCategory> mCategories;
- private String mExtraAction;
-
- public static CategoryManager get(Context context) {
- return get(context, null);
- }
-
- public static CategoryManager get(Context context, String action) {
- if (sInstance == null) {
- sInstance = new CategoryManager(context, action);
- }
- return sInstance;
- }
-
- CategoryManager(Context context, String action) {
- mTileByComponentCache = new ArrayMap<>();
- mCategoryByKeyMap = new ArrayMap<>();
- mInterestingConfigChanges = new InterestingConfigChanges();
- mInterestingConfigChanges.applyNewConfig(context.getResources());
- mExtraAction = action;
- }
-
- public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
- tryInitCategories(context, SETTING_PKG);
-
- return mCategoryByKeyMap.get(categoryKey);
- }
-
- public synchronized List<DashboardCategory> getCategories(Context context) {
- tryInitCategories(context, SETTING_PKG);
- return mCategories;
- }
-
- public synchronized void reloadAllCategories(Context context, String settingPkg) {
- final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
- context.getResources());
- mCategories = null;
- tryInitCategories(context, forceClearCache, settingPkg);
- }
-
- public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
- if (mCategories == null) {
- Log.w(TAG, "Category is null, skipping blacklist update");
- }
- for (int i = 0; i < mCategories.size(); i++) {
- DashboardCategory category = mCategories.get(i);
- for (int j = 0; j < category.getTilesCount(); j++) {
- Tile tile = category.getTile(j);
- if (tileBlacklist.contains(tile.intent.getComponent())) {
- category.removeTile(j--);
- }
- }
- }
- }
-
- private synchronized void tryInitCategories(Context context, String settingPkg) {
- // Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange
- // happens.
- tryInitCategories(context, false /* forceClearCache */, settingPkg);
- }
-
- private synchronized void tryInitCategories(Context context, boolean forceClearCache,
- String settingPkg) {
- if (mCategories == null) {
- if (forceClearCache) {
- mTileByComponentCache.clear();
- }
- mCategoryByKeyMap.clear();
- mCategories = TileUtils.getCategories(context, mTileByComponentCache,
- mExtraAction, settingPkg);
- for (DashboardCategory category : mCategories) {
- mCategoryByKeyMap.put(category.key, category);
- }
- backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
- sortCategories(context, mCategoryByKeyMap);
- filterDuplicateTiles(mCategoryByKeyMap);
- }
- }
-
- @VisibleForTesting
- synchronized void backwardCompatCleanupForCategory(
- Map<Pair<String, String>, Tile> tileByComponentCache,
- Map<String, DashboardCategory> categoryByKeyMap) {
- // A package can use a) CategoryKey, b) old category keys, c) both.
- // Check if a package uses old category key only.
- // If yes, map them to new category key.
-
- // Build a package name -> tile map first.
- final Map<String, List<Tile>> packageToTileMap = new HashMap<>();
- for (Entry<Pair<String, String>, Tile> tileEntry : tileByComponentCache.entrySet()) {
- final String packageName = tileEntry.getKey().first;
- List<Tile> tiles = packageToTileMap.get(packageName);
- if (tiles == null) {
- tiles = new ArrayList<>();
- packageToTileMap.put(packageName, tiles);
- }
- tiles.add(tileEntry.getValue());
- }
-
- for (Entry<String, List<Tile>> entry : packageToTileMap.entrySet()) {
- final List<Tile> tiles = entry.getValue();
- // Loop map, find if all tiles from same package uses old key only.
- boolean useNewKey = false;
- boolean useOldKey = false;
- for (Tile tile : tiles) {
- if (CategoryKey.KEY_COMPAT_MAP.containsKey(tile.category)) {
- useOldKey = true;
- } else {
- useNewKey = true;
- break;
- }
- }
- // Uses only old key, map them to new keys one by one.
- if (useOldKey && !useNewKey) {
- for (Tile tile : tiles) {
- final String newCategoryKey = CategoryKey.KEY_COMPAT_MAP.get(tile.category);
- tile.category = newCategoryKey;
- // move tile to new category.
- DashboardCategory newCategory = categoryByKeyMap.get(newCategoryKey);
- if (newCategory == null) {
- newCategory = new DashboardCategory();
- categoryByKeyMap.put(newCategoryKey, newCategory);
- }
- newCategory.addTile(tile);
- }
- }
- }
- }
-
- /**
- * Sort the tiles injected from all apps such that if they have the same priority value,
- * they wil lbe sorted by package name.
- * <p/>
- * A list of tiles are considered sorted when their priority value decreases in a linear
- * scan.
- */
- @VisibleForTesting
- synchronized void sortCategories(Context context,
- Map<String, DashboardCategory> categoryByKeyMap) {
- for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
- categoryEntry.getValue().sortTiles(context.getPackageName());
- }
- }
-
- /**
- * Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the
- * same intent.
- */
- @VisibleForTesting
- synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
- for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
- final DashboardCategory category = categoryEntry.getValue();
- final int count = category.getTilesCount();
- final Set<ComponentName> components = new ArraySet<>();
- for (int i = count - 1; i >= 0; i--) {
- final Tile tile = category.getTile(i);
- if (tile.intent == null) {
- continue;
- }
- final ComponentName tileComponent = tile.intent.getComponent();
- if (components.contains(tileComponent)) {
- category.removeTile(i);
- } else {
- components.add(tileComponent);
- }
- }
- }
- }
-
- /**
- * Sort priority value for tiles within a single {@code DashboardCategory}.
- *
- * @see #sortCategories(Context, Map)
- */
- private synchronized void sortCategoriesForExternalTiles(Context context,
- DashboardCategory dashboardCategory) {
- dashboardCategory.sortTiles(context.getPackageName());
-
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index 3a03644..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 f54ba8c..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;
@@ -35,6 +34,8 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -48,6 +49,8 @@
private static final boolean DEBUG_TIMING = false;
private static final String LOG_TAG = "TileUtils";
+ @VisibleForTesting
+ static final String SETTING_PKG = "com.android.settings";
/**
* Settings will search for system activities of this action and add them as a top level
@@ -200,7 +203,7 @@
* categories
*/
public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache, String extraAction, String settingPkg) {
+ Map<Pair<String, String>, Tile> cache, String extraAction) {
final long startTime = System.currentTimeMillis();
boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
!= 0;
@@ -210,22 +213,18 @@
// TODO: Needs much optimization, too many PM queries going on here.
if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
// Only add Settings for this user.
- getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,
- settingPkg);
+ getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);
getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
- OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
+ OPERATOR_DEFAULT_CATEGORY, tiles, false, true);
getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
- MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
+ MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true);
}
if (setup) {
- getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
- settingPkg);
- getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
- settingPkg);
- if (extraAction != null) {
- getTilesForAction(context, user, extraAction, cache, null, tiles, false,
- settingPkg);
- }
+ getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
+ getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false);
+ if (extraAction != null) {
+ getTilesForAction(context, user, extraAction, cache, null, tiles, false);
+ }
}
}
@@ -256,19 +255,18 @@
private static void getTilesForAction(Context context,
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
- String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
- String settingPkg) {
+ String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) {
getTilesForAction(context, user, action, addedCache, defaultCategory, outTiles,
- requireSettings, requireSettings, settingPkg);
+ requireSettings, requireSettings);
}
private static void getTilesForAction(Context context,
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
- boolean usePriority, String settingPkg) {
+ boolean usePriority) {
Intent intent = new Intent(action);
if (requireSettings) {
- intent.setPackage(settingPkg);
+ intent.setPackage(SETTING_PKG);
}
getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
usePriority, true, true);
@@ -281,7 +279,6 @@
PackageManager pm = context.getPackageManager();
List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
PackageManager.GET_META_DATA, user.getIdentifier());
- Map<String, IContentProvider> providerMap = new HashMap<>();
for (ResolveInfo resolved : results) {
if (!resolved.system) {
// Do not allow any app to add to settings, only system ones.
@@ -305,14 +302,14 @@
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;
tile.priority = usePriority ? resolved.priority : 0;
tile.metaData = activityInfo.metaData;
updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
- pm, providerMap, forceTintExternalIcon);
+ pm, forceTintExternalIcon);
if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
addedCache.put(key, tile);
}
@@ -328,11 +325,9 @@
private static boolean updateTileData(Context context, Tile tile,
ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm,
- Map<String, IContentProvider> providerMap, boolean forceTintExternalIcon) {
+ boolean forceTintExternalIcon) {
if (applicationInfo.isSystemApp()) {
boolean forceTintIcon = false;
- int icon = 0;
- Pair<String, Integer> iconFromUri = null;
CharSequence title = null;
String summary = null;
String keyHint = null;
@@ -350,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);
@@ -393,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/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 92044c3..7ed357c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -36,7 +36,8 @@
"/system/etc/NOTICE.xml.gz",
"/vendor/etc/NOTICE.xml.gz",
"/odm/etc/NOTICE.xml.gz",
- "/oem/etc/NOTICE.xml.gz"};
+ "/oem/etc/NOTICE.xml.gz",
+ "/product/etc/NOTICE.xml.gz"};
private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
private Context mContext;
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/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
deleted file mode 100644
index 4efcb7e..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.drawer;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Pair;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class CategoryManagerTest {
-
- private Context mContext;
- private CategoryManager mCategoryManager;
- private Map<Pair<String, String>, Tile> mTileByComponentCache;
- private Map<String, DashboardCategory> mCategoryByKeyMap;
-
- @Before
- public void setUp() {
- mContext = ShadowApplication.getInstance().getApplicationContext();
- mTileByComponentCache = new HashMap<>();
- mCategoryByKeyMap = new HashMap<>();
- mCategoryManager = CategoryManager.get(mContext);
- }
-
- @Test
- public void getInstance_shouldBeSingleton() {
- assertThat(mCategoryManager).isSameAs(CategoryManager.get(mContext));
- }
-
- @Test
- public void backwardCompatCleanupForCategory_shouldNotChangeCategoryForNewKeys() {
- final Tile tile1 = new Tile();
- final Tile tile2 = new Tile();
- tile1.category = CategoryKey.CATEGORY_ACCOUNT;
- tile2.category = CategoryKey.CATEGORY_ACCOUNT;
- final DashboardCategory category = new DashboardCategory();
- category.addTile(tile1);
- category.addTile(tile2);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_ACCOUNT, category);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "1"), tile1);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "2"), tile2);
-
- mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
- assertThat(mCategoryByKeyMap.size()).isEqualTo(1);
- assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT)).isNotNull();
- }
-
- @Test
- public void backwardCompatCleanupForCategory_shouldNotChangeCategoryForMixedKeys() {
- final Tile tile1 = new Tile();
- final Tile tile2 = new Tile();
- final String oldCategory = "com.android.settings.category.wireless";
- tile1.category = CategoryKey.CATEGORY_ACCOUNT;
- tile2.category = oldCategory;
- final DashboardCategory category1 = new DashboardCategory();
- category1.addTile(tile1);
- final DashboardCategory category2 = new DashboardCategory();
- category2.addTile(tile2);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_ACCOUNT, category1);
- mCategoryByKeyMap.put(oldCategory, category2);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS1"), tile1);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS2"), tile2);
-
- mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
- assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
- assertThat(
- mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).getTilesCount()).isEqualTo(1);
- assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
- }
-
- @Test
- public void backwardCompatCleanupForCategory_shouldChangeCategoryForOldKeys() {
- final Tile tile1 = new Tile();
- final String oldCategory = "com.android.settings.category.wireless";
- tile1.category = oldCategory;
- final DashboardCategory category1 = new DashboardCategory();
- category1.addTile(tile1);
- mCategoryByKeyMap.put(oldCategory, category1);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS1"), tile1);
-
- mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
- // Added 1 more category to category map.
- assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
- // The new category map has CATEGORY_NETWORK type now, which contains 1 tile.
- assertThat(
- mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).getTilesCount()).isEqualTo(1);
- // Old category still exists.
- assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
- }
-
- @Test
- public void sortCategories_singlePackage_shouldReorderBasedOnPriority() {
- // Create some fake tiles that are not sorted.
- final String testPackage = "com.android.test";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 50;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile3.priority = 200;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify they are now sorted.
- assertThat(category.getTile(0)).isSameAs(tile3);
- assertThat(category.getTile(1)).isSameAs(tile1);
- assertThat(category.getTile(2)).isSameAs(tile2);
- }
-
- @Test
- public void sortCategories_multiPackage_shouldReorderBasedOnPackageAndPriority() {
- // Create some fake tiles that are not sorted.
- final String testPackage1 = "com.android.test1";
- final String testPackage2 = "com.android.test2";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage2, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage1, "class2"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage1, "class3"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify they are now sorted.
- assertThat(category.getTile(0)).isSameAs(tile2);
- assertThat(category.getTile(1)).isSameAs(tile1);
- assertThat(category.getTile(2)).isSameAs(tile3);
- }
-
- @Test
- public void sortCategories_internalPackageTiles_shouldSkipTileForInternalPackage() {
- // Create some fake tiles that are not sorted.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify the sorting order is not changed
- assertThat(category.getTile(0)).isSameAs(tile1);
- assertThat(category.getTile(1)).isSameAs(tile2);
- assertThat(category.getTile(2)).isSameAs(tile3);
- }
-
- @Test
- public void sortCategories_internalAndExternalPackageTiles_shouldRetainPriorityOrdering() {
- // Inject one external tile among internal tiles.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final String testPackage2 = "com.google.test2";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent = new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 2;
- final Tile tile2 = new Tile();
- tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 1;
- final Tile tile3 = new Tile();
- tile3.intent = new Intent().setComponent(new ComponentName(testPackage2, "class0"));
- tile3.priority = 0;
- final Tile tile4 = new Tile();
- tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile4.priority = -1;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- category.addTile(tile4);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify the sorting order is not changed
- assertThat(category.getTile(0)).isSameAs(tile1);
- assertThat(category.getTile(1)).isSameAs(tile2);
- assertThat(category.getTile(2)).isSameAs(tile3);
- assertThat(category.getTile(3)).isSameAs(tile4);
- }
-
- @Test
- public void sortCategories_samePriority_internalPackageTileShouldTakePrecedence() {
- // Inject one external tile among internal tiles with same priority.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final String testPackage2 = "com.google.test2";
- final String testPackage3 = "com.abcde.test3";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent = new Intent().setComponent(new ComponentName(testPackage2, "class1"));
- tile1.priority = 1;
- final Tile tile2 = new Tile();
- tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 1;
- final Tile tile3 = new Tile();
- tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
- tile3.priority = 1;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify the sorting order is internal first, follow by package name ordering
- assertThat(category.getTile(0)).isSameAs(tile2);
- assertThat(category.getTile(1)).isSameAs(tile3);
- assertThat(category.getTile(2)).isSameAs(tile1);
- }
-
- @Test
- public void filterTiles_noDuplicate_noChange() {
- // Create some unique tiles
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
-
- assertThat(category.getTilesCount()).isEqualTo(3);
- }
-
- @Test
- public void filterTiles_hasDuplicate_shouldOnlyKeepUniqueTiles() {
- // Create tiles pointing to same intent.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
-
- assertThat(category.getTilesCount()).isEqualTo(1);
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
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 31621af..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
@@ -177,8 +177,7 @@
event -> testAction.equals(event.getAction())), anyInt(), anyInt()))
.thenReturn(info);
- List<DashboardCategory> categoryList = TileUtils.getCategories(
- mContext, cache, testAction, CategoryManager.SETTING_PKG);
+ List<DashboardCategory> categoryList = TileUtils.getCategories(mContext, cache, testAction);
assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
}
@@ -193,12 +192,12 @@
userHandleList.add(new UserHandle(ActivityManager.getCurrentUser()));
when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
- TileUtils.getCategories(mContext, cache, null /* action */, CategoryManager.SETTING_PKG);
+ TileUtils.getCategories(mContext, cache, null /* action */);
verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser(
intentCaptor.capture(), anyInt(), anyInt());
assertThat(intentCaptor.getAllValues().get(0).getPackage())
- .isEqualTo(CategoryManager.SETTING_PKG);
+ .isEqualTo(TileUtils.SETTING_PKG);
}
@Test
@@ -315,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.
@@ -333,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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index f811665..da870bd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -144,11 +144,9 @@
<uses-permission android:name="android.permission.MANAGE_SENSORS" />
<uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
<uses-permission android:name="android.permission.MANAGE_CAMERA" />
- <!-- Permission needed to enable/disable Bluetooth/Wifi when on permission review mode -->
- <uses-permission
- android:name="android.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED" />
- <uses-permission
- android:name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED" />
+ <!-- Permission needed to enable/disable Bluetooth/Wifi -->
+ <uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
+ <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
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/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 2a22bf2..38d266d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -154,12 +154,11 @@
}
public void fadeInTranslating() {
- float translation = mShelfIcons.getTranslationY();
- mShelfIcons.setTranslationY(translation - mShelfAppearTranslation);
+ mShelfIcons.setTranslationY(-mShelfAppearTranslation);
mShelfIcons.setAlpha(0);
mShelfIcons.animate()
.setInterpolator(Interpolators.DECELERATE_QUINT)
- .translationY(translation)
+ .translationY(0)
.setDuration(SHELF_IN_TRANSLATION_DURATION)
.start();
mShelfIcons.animate()
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/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index e804460..4df1e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -118,7 +118,7 @@
mPanel.startExpandingFromPeek();
// This call needs to be after the expansion start otherwise we will get a
// flicker of one frame as it's not expanded yet.
- mHeadsUpManager.unpinAll();
+ mHeadsUpManager.unpinAll(true);
mPanel.clearNotificationEffects();
endMotion();
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/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/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 6694b93a3..677dd73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -389,13 +389,23 @@
/**
* Unpins all pinned Heads Up Notifications.
+ * @param userUnPinned The unpinned action is trigger by user real operation.
*/
- public void unpinAll() {
+ public void unpinAll(boolean userUnPinned) {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
setEntryPinned(entry, false /* isPinned */);
// maybe it got un sticky
entry.updateEntry(false /* updatePostTime */);
+
+ // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
+ // on the screen.
+ if (userUnPinned && entry.entry != null && entry.entry.row != null) {
+ ExpandableNotificationRow row = entry.entry.row;
+ if (row.mustStayOnScreen()) {
+ row.setHeadsUpIsVisible();
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/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/Android.bp b/proto/Android.bp
index 5fd2885..f3811bd 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -17,33 +17,3 @@
},
},
}
-
-cc_library {
- name: "libmetricprotos",
- host_supported: true,
- proto: {
- export_proto_headers: true,
- include_dirs: ["external/protobuf/src"],
- },
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- ],
- srcs: ["src/metrics_constants.proto"],
- target: {
- host: {
- proto: {
- type: "full",
- },
- },
- android: {
- proto: {
- type: "lite",
- },
- shared: {
- enabled: false,
- },
- },
- },
-}
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/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2da0818..9c0e110 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,12 +16,18 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_WITH_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -1807,7 +1813,6 @@
updateDisplayDaltonizerLocked(userState);
updateDisplayInversionLocked(userState);
updateMagnificationLocked(userState);
- updateSoftKeyboardShowModeLocked(userState);
scheduleUpdateFingerprintGestureHandling(userState);
scheduleUpdateInputFilter(userState);
scheduleUpdateClientsIfNeededLocked(userState);
@@ -2001,18 +2006,6 @@
return false;
}
- private boolean readSoftKeyboardShowModeChangedLocked(UserState userState) {
- final int softKeyboardShowMode = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0,
- userState.mUserId);
- if (softKeyboardShowMode != userState.mSoftKeyboardShowMode) {
- userState.mSoftKeyboardShowMode = softKeyboardShowMode;
- return true;
- }
- return false;
- }
-
private void updateTouchExplorationLocked(UserState userState) {
boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
final int serviceCount = userState.mBoundServices.size();
@@ -2211,34 +2204,6 @@
return false;
}
- private void updateSoftKeyboardShowModeLocked(UserState userState) {
- final int userId = userState.mUserId;
- // Only check whether we need to reset the soft keyboard mode if it is not set to the
- // default.
- if ((userId == mCurrentUserId) && (userState.mSoftKeyboardShowMode != 0)) {
- // Check whether the last AccessibilityService that changed the soft keyboard mode to
- // something other than the default is still enabled and, if not, remove flag and
- // reset to the default soft keyboard behavior.
- boolean serviceChangingSoftKeyboardModeIsEnabled =
- userState.mEnabledServices.contains(userState.mServiceChangingSoftKeyboardMode);
-
- if (!serviceChangingSoftKeyboardModeIsEnabled) {
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- 0,
- userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- userState.mSoftKeyboardShowMode = 0;
- userState.mServiceChangingSoftKeyboardMode = null;
- notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
- }
- }
- }
-
private void updateFingerprintGestureHandling(UserState userState) {
final List<AccessibilityServiceConnection> services;
synchronized (mLock) {
@@ -2473,6 +2438,15 @@
}
}
+ private void putSecureIntForUser(String key, int value, int userid) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
class RemoteAccessibilityConnection implements DeathRecipient {
private final int mUid;
private final String mPackageName;
@@ -3683,7 +3657,7 @@
public int mLastSentClientState = -1;
- public int mSoftKeyboardShowMode = 0;
+ private int mSoftKeyboardShowMode = 0;
public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
public ComponentName mServiceAssignedToAccessibilityButton;
@@ -3744,7 +3718,6 @@
mServiceAssignedToAccessibilityButton = null;
mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
- mSoftKeyboardShowMode = 0;
}
public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -3766,6 +3739,11 @@
public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
mBoundServices.remove(serviceConnection);
serviceConnection.onRemoved();
+ if ((mServiceChangingSoftKeyboardMode != null)
+ && (mServiceChangingSoftKeyboardMode.equals(
+ serviceConnection.getServiceInfo().getComponentName()))) {
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ }
// It may be possible to bind a service twice, which confuses the map. Rebuild the map
// to make sure we can still reach a service
mComponentNameToServiceMap.clear();
@@ -3792,6 +3770,134 @@
public Set<ComponentName> getBindingServicesLocked() {
return mBindingServices;
}
+
+ public int getSoftKeyboardShowMode() {
+ return mSoftKeyboardShowMode;
+ }
+
+ /**
+ * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+ * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+ * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+ * setting can be changed by the user, and prevents the system from suppressing the soft
+ * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+ * to the user's preference, if they have supplied one.
+ *
+ * @param newMode The new mode
+ * @param requester The service requesting the change, so we can undo it when the
+ * service stops. Set to null if something other than a service is forcing
+ * the change.
+ *
+ * @return Whether or not the soft keyboard mode equals the new mode after the call
+ */
+ public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
+ if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
+ && (newMode != SHOW_MODE_WITH_HARD_KEYBOARD))
+ {
+ Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+ return false;
+ }
+ if (mSoftKeyboardShowMode == newMode) return true;
+
+ if (newMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ if (hasUserOverriddenHardKeyboardSettingLocked()) {
+ // The user has specified a default for this setting
+ return false;
+ }
+ // Save the original value. But don't do this if the value in settings is already
+ // the new mode. That happens when we start up after a reboot, and we don't want
+ // to overwrite the value we had from when we first started controlling the setting.
+ if (getSoftKeyboardValueFromSettings() != SHOW_MODE_WITH_HARD_KEYBOARD) {
+ setOriginalHardKeyboardValue(
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
+ }
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+ } else if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+ getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+ }
+
+ saveSoftKeyboardValueToSettings(newMode);
+ mSoftKeyboardShowMode = newMode;
+ mServiceChangingSoftKeyboardMode = requester;
+ notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+ return true;
+ }
+
+ /**
+ * If the settings are inconsistent with the internal state, make the internal state
+ * match the settings.
+ */
+ public void reconcileSoftKeyboardModeWithSettingsLocked() {
+ final ContentResolver cr = mContext.getContentResolver();
+ final boolean showWithHardKeyboardSettings =
+ Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
+ if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ if (!showWithHardKeyboardSettings) {
+ // The user has overridden the setting. Respect that and prevent further changes
+ // to this behavior.
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ setUserOverridesHardKeyboardSettingLocked();
+ }
+ }
+
+ // If the setting and the internal state are out of sync, set both to default
+ if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
+ {
+ Slog.e(LOG_TAG,
+ "Show IME setting inconsistent with internal state. Overwriting");
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO, mUserId);
+ }
+ }
+
+ private void setUserOverridesHardKeyboardSettingLocked() {
+ final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+ mUserId);
+ }
+
+ private boolean hasUserOverriddenHardKeyboardSettingLocked() {
+ final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+ != 0;
+ }
+
+ private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+ final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+ & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+ | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+ final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+ | softKeyboardShowMode;
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private int getSoftKeyboardValueFromSettings() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO) & SHOW_MODE_MASK;
+ }
+
+ private boolean getOriginalHardKeyboardValue() {
+ return (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
+ & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+ }
}
private final class AccessibilityContentObserver extends ContentObserver {
@@ -3829,6 +3935,9 @@
private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
+ private final Uri mShowImeWithHardKeyboardUri = Settings.Secure.getUriFor(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+
private final Uri mAccessibilityShortcutServiceIdUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
@@ -3864,6 +3973,8 @@
contentResolver.registerContentObserver(
mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
+ mShowImeWithHardKeyboardUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
@@ -3906,11 +4017,9 @@
if (readHighTextContrastEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
- } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
- if (readSoftKeyboardShowModeChangedLocked(userState)) {
- notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
- onUserStateChangedLocked(userState);
- }
+ } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)
+ || mShowImeWithHardKeyboardUri.equals(uri)) {
+ userState.reconcileSoftKeyboardModeWithSettingsLocked();
} else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
if (readAccessibilityShortcutSettingLocked(userState)) {
onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 8f92145..6237212 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -218,26 +218,20 @@
if (!isCalledForCurrentUserLocked()) {
return false;
}
+ final UserState userState = mUserStateWeakReference.get();
+ if (userState == null) return false;
+ return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
}
- UserState userState = mUserStateWeakReference.get();
- if (userState == null) return false;
- final long identity = Binder.clearCallingIdentity();
- try {
- // Keep track of the last service to request a non-default show mode. The show mode
- // should be restored to default should this service be disabled.
- userState.mServiceChangingSoftKeyboardMode = (showMode == SHOW_MODE_AUTO)
- ? null : mComponentName;
-
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, showMode,
- userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return true;
}
@Override
+ public int getSoftKeyboardShowMode() {
+ final UserState userState = mUserStateWeakReference.get();
+ return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+ }
+
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index ed3b3e7..ff29311 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -259,6 +259,11 @@
}
@Override
+ public int getSoftKeyboardShowMode() {
+ return 0;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
return false;
}
diff --git a/services/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/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 65ad596..ad80cc26 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -123,8 +123,7 @@
}
public void destroy() {
- mHandler.sendMessage(obtainMessage(
- RemoteFillService::handleDestroy, this));
+ mHandler.sendMessage(obtainMessage(RemoteFillService::handleDestroy, this));
}
private void handleDestroy() {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 18255c5..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;
@@ -839,7 +838,8 @@
// FillServiceCallbacks
@Override
public void onServiceDied(RemoteFillService service) {
- // TODO(b/337565347): implement
+ Slog.w(TAG, "removing session because service died");
+ forceRemoveSelfLocked();
}
// AutoFillUiCallback
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/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 78b7385..f81541e 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -211,7 +211,7 @@
// bluetooth profile services
private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>();
- private final boolean mPermissionReviewRequired;
+ private final boolean mWirelessConsentRequired;
private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
@Override
@@ -368,8 +368,8 @@
mContext = context;
- mPermissionReviewRequired = context.getResources()
- .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired);
+ mWirelessConsentRequired = context.getResources()
+ .getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired);
mCrashes = 0;
mBluetooth = null;
@@ -885,7 +885,7 @@
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
- if (!isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+ if (!isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName,
callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
return false;
}
@@ -922,7 +922,7 @@
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
- if (isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+ if (isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName,
callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
return false;
}
@@ -945,7 +945,7 @@
private boolean startConsentUiIfNeeded(String packageName,
int callingUid, String intentAction) throws RemoteException {
- if (checkBluetoothPermissionWhenPermissionReviewRequired()) {
+ if (checkBluetoothPermissionWhenWirelessConsentRequired()) {
return false;
}
try {
@@ -978,21 +978,18 @@
/**
* Check if the caller must still pass permission check or if the caller is exempted
- * from the consent UI via the MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED check.
+ * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check.
*
* Commands from some callers may be exempted from triggering the consent UI when
* enabling bluetooth. This exemption is checked via the
- * MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED and allows calls to skip
+ * MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED and allows calls to skip
* the consent UI where it may otherwise be required.
*
* @hide
*/
- private boolean checkBluetoothPermissionWhenPermissionReviewRequired() {
- if (!mPermissionReviewRequired) {
- return false;
- }
+ private boolean checkBluetoothPermissionWhenWirelessConsentRequired() {
int result = mContext.checkCallingPermission(
- android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED);
+ android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED);
return result == PackageManager.PERMISSION_GRANTED;
}
diff --git a/services/core/java/com/android/server/InputContentUriTokenHandler.java b/services/core/java/com/android/server/InputContentUriTokenHandler.java
index 57cdc94..6338b2f 100644
--- a/services/core/java/com/android/server/InputContentUriTokenHandler.java
+++ b/services/core/java/com/android/server/InputContentUriTokenHandler.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.UriGrantsManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
@@ -27,6 +28,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.server.uri.UriGrantsManagerInternal;
final class InputContentUriTokenHandler extends IInputContentUriToken.Stub {
@@ -63,12 +65,8 @@
return;
}
- try {
- mPermissionOwnerToken = ActivityManager.getService()
- .newUriPermissionOwner("InputContentUriTokenHandler");
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
+ mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
+ .newUriPermissionOwner("InputContentUriTokenHandler");
doTakeLocked(mPermissionOwnerToken);
}
@@ -78,7 +76,7 @@
long origId = Binder.clearCallingIdentity();
try {
try {
- ActivityManager.getService().grantUriPermissionFromOwner(
+ UriGrantsManager.getService().grantUriPermissionFromOwner(
permissionOwner, mSourceUid, mTargetPackage, mUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId, mTargetUserId);
} catch (RemoteException e) {
@@ -96,11 +94,9 @@
return;
}
try {
- ActivityManager.getService().revokeUriPermissionFromOwner(
- mPermissionOwnerToken, mUri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ LocalServices.getService(UriGrantsManagerInternal.class)
+ .revokeUriPermissionFromOwner(mPermissionOwnerToken, mUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
} finally {
mPermissionOwnerToken = null;
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fd74613..784dfb4 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -51,6 +51,7 @@
import org.xmlpull.v1.XmlSerializer;
import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.ColorInt;
@@ -888,10 +889,12 @@
if (showImeUri.equals(uri)) {
updateKeyboardFromSettingsLocked();
} else if (accessibilityRequestingNoImeUri.equals(uri)) {
- mAccessibilityRequestingNoSoftKeyboard = Settings.Secure.getIntForUser(
+ final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- 0, mUserId) == 1;
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId);
+ mAccessibilityRequestingNoSoftKeyboard =
+ (accessibilitySoftKeyboardSetting & AccessibilityService.SHOW_MODE_MASK)
+ == AccessibilityService.SHOW_MODE_HIDDEN;
if (mAccessibilityRequestingNoSoftKeyboard) {
final boolean showRequested = mShowRequested;
hideCurrentInputLocked(0, null);
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/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 74d8755..9631e46 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2271,6 +2271,7 @@
return ruleName;
}
+ @GuardedBy("mRulesLock")
private @NonNull SparseIntArray getUidFirewallRulesLR(int chain) {
switch (chain) {
case FIREWALL_CHAIN_STANDBY:
diff --git a/services/core/java/com/android/server/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 9c55de7..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,19 +512,18 @@
fgRequired = false;
}
- NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
+ NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
callingUid, r.packageName, service, service.getFlags(), null, r.userId);
// If permissions need a review before any of the app components can run,
// we do not start the service and launch a review activity if the calling app
// is in the foreground passing it a pending intent to start the service when
// review is completed.
- if (mAm.mPermissionReviewRequired) {
- // XXX This is not dealing with fgRequired!
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
- callingUid, service, callerFg, userId)) {
- return null;
- }
+
+ // XXX This is not dealing with fgRequired!
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
+ callingUid, service, callerFg, userId)) {
+ return null;
}
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
@@ -1535,75 +1534,73 @@
// we schedule binding to the service but do not start its process, then
// we launch a review activity to which is passed a callback to invoke
// when done to start the bound service's process to completing the binding.
- if (mAm.mPermissionReviewRequired) {
- if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
- s.packageName, s.userId)) {
+ if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ s.packageName, s.userId)) {
- permissionsReviewRequired = true;
+ permissionsReviewRequired = true;
- // Show a permission review UI only for binding from a foreground app
- if (!callerFg) {
- Slog.w(TAG, "u" + s.userId + " Binding to a service in package"
- + s.packageName + " requires a permissions review");
- return 0;
- }
+ // Show a permission review UI only for binding from a foreground app
+ if (!callerFg) {
+ Slog.w(TAG, "u" + s.userId + " Binding to a service in package"
+ + s.packageName + " requires a permissions review");
+ return 0;
+ }
- final ServiceRecord serviceRecord = s;
- final Intent serviceIntent = service;
+ final ServiceRecord serviceRecord = s;
+ final Intent serviceIntent = service;
- RemoteCallback callback = new RemoteCallback(
- new RemoteCallback.OnResultListener() {
- @Override
- public void onResult(Bundle result) {
- synchronized(mAm) {
- final long identity = Binder.clearCallingIdentity();
- try {
- if (!mPendingServices.contains(serviceRecord)) {
- return;
- }
- // If there is still a pending record, then the service
- // binding request is still valid, so hook them up. We
- // proceed only if the caller cleared the review requirement
- // otherwise we unbind because the user didn't approve.
- if (!mAm.getPackageManagerInternalLocked()
- .isPermissionsReviewRequired(
- serviceRecord.packageName,
- serviceRecord.userId)) {
- try {
- bringUpServiceLocked(serviceRecord,
- serviceIntent.getFlags(),
- callerFg, false, false);
- } catch (RemoteException e) {
- /* ignore - local call */
- }
- } else {
- unbindServiceLocked(connection);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
+ RemoteCallback callback = new RemoteCallback(
+ new RemoteCallback.OnResultListener() {
+ @Override
+ public void onResult(Bundle result) {
+ synchronized(mAm) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (!mPendingServices.contains(serviceRecord)) {
+ return;
}
+ // If there is still a pending record, then the service
+ // binding request is still valid, so hook them up. We
+ // proceed only if the caller cleared the review requirement
+ // otherwise we unbind because the user didn't approve.
+ if (!mAm.getPackageManagerInternalLocked()
+ .isPermissionsReviewRequired(
+ serviceRecord.packageName,
+ serviceRecord.userId)) {
+ try {
+ bringUpServiceLocked(serviceRecord,
+ serviceIntent.getFlags(),
+ callerFg, false, false);
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ }
+ } else {
+ unbindServiceLocked(connection);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
- });
-
- final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);
- intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
-
- if (DEBUG_PERMISSIONS_REVIEW) {
- Slog.i(TAG, "u" + s.userId + " Launching permission review for package "
- + s.packageName);
}
+ });
- mAm.mHandler.post(new Runnable() {
- @Override
- public void run() {
- mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
- }
- });
+ final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);
+ intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
+
+ if (DEBUG_PERMISSIONS_REVIEW) {
+ Slog.i(TAG, "u" + s.userId + " Launching permission review for package "
+ + s.packageName);
}
+
+ mAm.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
+ }
+ });
}
final long origId = Binder.clearCallingIdentity();
@@ -2581,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 6091434..594b391 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;
@@ -1591,8 +1478,6 @@
PackageManagerInternal mPackageManagerInt;
- final boolean mPermissionReviewRequired;
-
boolean mHasHeavyWeightFeature;
/**
@@ -1955,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.
@@ -2557,11 +2438,9 @@
mBatteryStatsService = null;
mCompatModePackages = null;
mConstants = null;
- mGrantFile = null;
mHandler = null;
mHandlerThread = null;
mIntentFirewall = null;
- mPermissionReviewRequired = false;
mProcessCpuThread = null;
mProcessStats = null;
mProviderMap = null;
@@ -2587,9 +2466,6 @@
Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
- mPermissionReviewRequired = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_permissionReviewRequired);
-
mHandlerThread = new ServiceThread(TAG,
THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
@@ -2622,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);
@@ -2641,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);
@@ -2730,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.
@@ -2930,7 +2805,6 @@
@Override
public void batteryStatsReset() {
- BinderCallsStatsService.reset();
mOomAdjProfiler.reset();
}
@@ -3332,6 +3206,14 @@
// Turn this condition on to cause killing to happen regularly, for testing.
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ proc.lastCachedPss, holder.appVersion);
+ }
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
} else if (proc != null && !keepIfLarge
@@ -3341,6 +3223,14 @@
if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ proc.lastCachedPss, holder.appVersion);
+ }
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
}
@@ -5018,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();
@@ -5365,6 +5253,19 @@
infos[i].getTotalUss(), infos[i].getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
proc.pkgList.mPkgList);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ infos[i].getTotalPss(),
+ infos[i].getTotalUss(),
+ infos[i].getTotalRss(),
+ ProcessStats.ADD_PSS_EXTERNAL_SLOW,
+ endTime-startTime,
+ holder.appVersion);
+ }
}
}
}
@@ -5395,6 +5296,16 @@
// Record this for posterity if the process has been stable.
proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false,
ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList.mPkgList);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ pss[i], tmpUss[0], tmpUss[2],
+ ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime,
+ holder.appVersion);
+ }
}
}
}
@@ -5693,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--) {
@@ -7184,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)
@@ -7456,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.
@@ -7538,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;
}
/**
@@ -7957,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.
@@ -8097,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) {
@@ -8909,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;
@@ -8955,7 +7732,8 @@
}
}
}
- if (!checkedGrants && checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
+ if (!checkedGrants
+ && mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
return null;
}
@@ -8974,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) {
@@ -9344,10 +8087,8 @@
// If permissions need a review before any of the app components can run,
// we return no provider and launch a review activity if the calling app
// is in the foreground.
- if (mPermissionReviewRequired) {
- if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
- return null;
- }
+ if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
+ return null;
}
try {
@@ -10029,7 +8770,6 @@
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
- BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
if (isolated) {
@@ -10068,7 +8808,7 @@
StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
- final ProcessRecord r = new ProcessRecord(this, stats, info, proc, uid);
+ final ProcessRecord r = new ProcessRecord(this, info, proc, uid);
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
@@ -10147,6 +8887,7 @@
abiOverride);
}
+ @GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, String abiOverride) {
ProcessRecord app;
@@ -11492,9 +10233,7 @@
retrieveSettings();
final int currentUserId = mUserController.getCurrentUserId();
- synchronized (this) {
- readGrantedUriPermissionsLocked();
- }
+ mUgmInternal.onSystemReady();
final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
if (pmi != null) {
@@ -11667,6 +10406,15 @@
: StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN
);
+ final int relaunchReason = r == null ? ActivityRecord.RELAUNCH_REASON_NONE
+ : r.getWindowProcessController().computeRelaunchReason();
+ final String relaunchReasonString = ActivityRecord.relaunchReasonToString(relaunchReason);
+ if (crashInfo.crashTag == null) {
+ crashInfo.crashTag = relaunchReasonString;
+ } else {
+ crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
+ }
+
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
@@ -11993,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
@@ -12125,6 +10876,7 @@
return imp;
}
+ @GuardedBy("this")
private void fillInProcMemInfoLocked(ProcessRecord app,
ActivityManager.RunningAppProcessInfo outInfo,
int clientTargetSdk) {
@@ -12375,6 +11127,11 @@
pw.println("-------------------------------------------------------------------------------");
}
mOomAdjProfiler.dump(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpBinderProxies(pw);
}
}
@@ -12536,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++;
@@ -12934,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++) {
@@ -12962,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) {
@@ -14257,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,
@@ -15233,6 +13957,16 @@
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime-startTime, r.pkgList.mPkgList);
+ for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ r.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ myTotalPss, myTotalUss, myTotalRss, reportType,
+ endTime-startTime,
+ holder.appVersion);
+ }
}
}
@@ -15731,6 +14465,15 @@
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime-startTime, r.pkgList.mPkgList);
+ for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ r.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime,
+ holder.appVersion);
+ }
}
}
@@ -17676,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);
@@ -19601,8 +18344,10 @@
}
if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to external provider: " + app);
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to external provider: " + app);
+ }
}
}
}
@@ -19760,6 +18505,15 @@
proc.lastPssTime = now;
proc.baseProcessTracker.addPss(
pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
+ for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ pss, uss, rss, statType, pssDuration,
+ holder.appVersion);
+ }
if (DEBUG_PSS) Slog.d(TAG_PSS,
"pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+ " state=" + ProcessList.makeProcStateString(procState));
@@ -20111,6 +18865,14 @@
app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
+ " dur=" + checkDur + " limit=" + cpuLimit, true);
app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList);
+ for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
+ app.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ holder.appVersion);
+ }
}
}
app.lastCpuTime = app.curCpuTime;
@@ -20946,6 +19708,7 @@
}
if (memFactor != mLastMemoryLevel) {
EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
+ StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
}
mLastMemoryLevel = memFactor;
mLastNumProcesses = mLruProcesses.size();
@@ -21536,6 +20299,7 @@
}
}
+ @GuardedBy("this")
final void trimApplicationsLocked() {
// First remove any unused application processes whose package
// has been removed.
@@ -22056,15 +20820,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 0572ca9..8be5ada 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -771,7 +771,7 @@
// If permissions need a review before any of the app components can run, we
// launch the review activity and pass a pending intent to start the activity
// we are to launching now after the review is completed.
- if (mService.mAm.mPermissionReviewRequired && aInfo != null) {
+ if (aInfo != null) {
if (mService.mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
aInfo.packageName, userId)) {
IIntentSender target = mService.mAm.getIntentSenderLocked(
@@ -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 dbade5f..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();
}
}
@@ -4676,6 +4681,8 @@
// will wake up stacks or put them to sleep as appropriate.
if (wasSleeping) {
mSleeping = false;
+ StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+ StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__AWAKE);
startTimeTrackingFocusedActivityLocked();
mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
@@ -4686,6 +4693,8 @@
}
} else if (!mSleeping && shouldSleep) {
mSleeping = true;
+ StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+ StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__ASLEEP);
if (mCurAppTimeTracker != null) {
mCurAppTimeTracker.stop();
}
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/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index d9a8818c..2541352 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -15,6 +15,8 @@
*/
package com.android.server.am;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_LINK_LAYER_STATS;
+
import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
@@ -410,8 +412,11 @@
if (mWifiManager != null) {
try {
- wifiReceiver = new SynchronousResultReceiver("wifi");
- mWifiManager.requestActivityInfo(wifiReceiver);
+ // Only fetch WiFi power data if it is supported.
+ if ((mWifiManager.getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0) {
+ wifiReceiver = new SynchronousResultReceiver("wifi");
+ mWifiManager.requestActivityInfo(wifiReceiver);
+ }
} catch (RemoteException e) {
// Oh well.
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a9fd51d..046cfc7 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -664,12 +664,10 @@
// the broadcast and if the calling app is in the foreground and the broadcast is
// explicit we launch the review UI passing it a pending intent to send the skipped
// broadcast.
- if (mService.mPermissionReviewRequired) {
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
- filter.owningUserId)) {
- r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
- return;
- }
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
+ filter.owningUserId)) {
+ r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+ return;
}
r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
@@ -1240,7 +1238,7 @@
// the broadcast and if the calling app is in the foreground and the broadcast is
// explicit we launch the review UI passing it a pending intent to send the skipped
// broadcast.
- if (mService.mPermissionReviewRequired && !skip) {
+ if (!skip) {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
info.activityInfo.packageName, UserHandle.getUserId(
info.activityInfo.applicationInfo.uid))) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index ea6d134..b33ce2b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -43,6 +43,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -58,7 +59,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
private final ActivityManagerService mService; // where we came from
- private final BatteryStatsImpl mBatteryStats; // where to collect runtime statistics
final ApplicationInfo info; // all about the first app in the process
final boolean isolated; // true if this is a special isolated process
final int uid; // uid of process; may be different from 'info' if isolated
@@ -506,10 +506,9 @@
}
}
- ProcessRecord(ActivityManagerService _service, BatteryStatsImpl _batteryStats,
- ApplicationInfo _info, String _processName, int _uid) {
+ ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
+ int _uid) {
mService = _service;
- mBatteryStats = _batteryStats;
info = _info;
isolated = _info.uid != _uid;
uid = _uid;
@@ -540,6 +539,12 @@
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
origBase.makeInactive();
}
baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
@@ -569,6 +574,12 @@
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
origBase.makeInactive();
}
baseProcessTracker = null;
@@ -831,6 +842,12 @@
public void forceProcessStateUpTo(int newState) {
if (mRepProcState > newState) {
curProcState = mRepProcState = newState;
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(mRepProcState),
+ pkgList.valueAt(ipkg).appVersion);
+ }
}
}
@@ -843,6 +860,12 @@
long now = SystemClock.uptimeMillis();
baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), now, pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
if (N != 1) {
for (int i=0; i<N; i++) {
ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
@@ -894,6 +917,12 @@
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(mRepProcState),
+ pkgList.valueAt(ipkg).appVersion);
+ }
mWindowProcessController.setReportedProcState(repProcState);
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index f0bd8fa..8ce650c 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -612,7 +612,8 @@
stats.dumpCheckinLocked(pw, reqPackage);
} else {
if (dumpDetails || dumpFullDetails) {
- stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly);
+ stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll,
+ activeOnly);
} else {
stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
}
@@ -974,8 +975,8 @@
if (checkedIn) pw.print(" (checked in)");
pw.println(":");
if (dumpDetails || dumpFullDetails) {
- processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
- activeOnly);
+ processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
+ dumpAll, activeOnly);
if (dumpAll) {
pw.print(" mFile="); pw.println(mFile.getBaseFile());
}
@@ -1030,7 +1031,7 @@
// much crud.
if (dumpFullDetails) {
processStats.dumpLocked(pw, reqPackage, now, false, false,
- activeOnly);
+ false, activeOnly);
} else {
processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
}
@@ -1060,8 +1061,8 @@
}
pw.println("CURRENT STATS:");
if (dumpDetails || dumpFullDetails) {
- mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
- activeOnly);
+ mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
+ dumpAll, activeOnly);
if (dumpAll) {
pw.print(" mFile="); pw.println(mFile.getBaseFile());
}
diff --git a/services/core/java/com/android/server/am/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/ArcTerminationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
new file mode 100644
index 0000000..7e73321
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Feature action that handles Audio Return Channel terminated by AVR devices.
+ */
+public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction {
+
+ // State in which waits for ARC response.
+ private static final int STATE_WAITING_FOR_INITIATE_ARC_RESPONSE = 1;
+ private static final int STATE_ARC_TERMINATED = 2;
+
+ // the required maximum response time specified in CEC 9.2
+ private static final int TIMEOUT_MS = 1000;
+
+ ArcTerminationActionFromAvr(HdmiCecLocalDevice source) {
+ super(source);
+ }
+
+ @Override
+ boolean start() {
+ mState = STATE_WAITING_FOR_INITIATE_ARC_RESPONSE;
+ addTimer(mState, TIMEOUT_MS);
+ sendTerminateArc();
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_INITIATE_ARC_RESPONSE) {
+ return false;
+ }
+ switch (cmd.getOpcode()) {
+ case Constants.MESSAGE_REPORT_ARC_TERMINATED:
+ mState = STATE_ARC_TERMINATED;
+ audioSystem().setArcStatus(false);
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ switch (mState) {
+ case STATE_WAITING_FOR_INITIATE_ARC_RESPONSE:
+ handleTerminateArcTimeout();
+ break;
+ }
+ }
+
+ protected void sendTerminateArc() {
+ sendCommand(HdmiCecMessageBuilder.buildTerminateArc(getSourceAddress(), Constants.ADDR_TV),
+ result -> {
+ if (result != SendMessageResult.SUCCESS) {
+ HdmiLogger.debug("Terminate ARC was not successfully sent.");
+ finish();
+ }
+ });
+ }
+
+ private void handleTerminateArcTimeout() {
+ HdmiLogger.debug("handleTerminateArcTimeout");
+ finish();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 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 7ba8e14..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;
@@ -66,11 +70,7 @@
Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
mSystemAudioActivated ? "true" : "false");
}
- if (setSystemAudioMode(false)) {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildSetSystemAudioMode(
- mAddress, Constants.ADDR_BROADCAST, false));
- }
+ terminateSystemAudioMode();
}
@Override
@@ -102,6 +102,18 @@
}
}
+ @ServiceThreadOnly
+ protected boolean handleActiveSource(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int logicalAddress = message.getSource();
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
+ if (!mActiveSource.equals(activeSource)) {
+ setActiveSource(activeSource);
+ }
+ return true;
+ }
+
@Override
@ServiceThreadOnly
protected int getPreferredAddress() {
@@ -224,7 +236,6 @@
return true;
}
- mSystemAudioSource = systemAudioStatusOn ? message.getSource() : null;
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
@@ -251,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();
@@ -279,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 =
@@ -303,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);
@@ -320,6 +394,21 @@
}
}
+ protected void terminateSystemAudioMode() {
+ // remove pending initiation actions
+ removeAction(SystemAudioInitiationActionFromAvr.class);
+ if (!isSystemAudioActivated()) {
+ return;
+ }
+
+ if (setSystemAudioMode(false)) {
+ // send <Set System Audio Mode> [“Off”]
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ mAddress, Constants.ADDR_BROADCAST, false));
+ }
+ }
+
/** Reports if System Audio Mode is supported by the connected TV */
interface TvSystemAudioModeSupportedCallback {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index f1cb246..649a2da 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -18,6 +18,7 @@
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
+import com.android.server.hdmi.Constants.AudioCodec;
/**
* A helper class to build {@link HdmiCecMessage} from various cec commands.
@@ -265,6 +266,25 @@
return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
}
+
+ /**
+ * Build <Request Short Audio Descriptor> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param audioFormats the {@link AudioCodec}s desired
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRequestShortAudioDescriptor(int src, int dest,
+ @AudioCodec int[] audioFormats) {
+ byte[] params = new byte[Math.min(audioFormats.length,4)] ;
+ for (int i = 0; i < params.length ; i++){
+ params[i] = (byte) (audioFormats[i] & 0xff);
+ }
+ return buildCommand(src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
+ }
+
+
/**
* Build <Text View On> command.
*
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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ebb3209..76f9695 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -628,6 +628,7 @@
mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
}
+ @GuardedBy("mUidRulesFirstLock")
void updatePowerSaveWhitelistUL() {
try {
int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
@@ -654,6 +655,7 @@
*
* @return whether any uid has been whitelisted.
*/
+ @GuardedBy("mUidRulesFirstLock")
boolean addDefaultRestrictBackgroundWhitelistUidsUL() {
final List<UserInfo> users = mUserManager.getUsers();
final int numberUsers = users.size();
@@ -666,6 +668,7 @@
return changed;
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean addDefaultRestrictBackgroundWhitelistUidsUL(int userId) {
final SystemConfig sysConfig = SystemConfig.getInstance();
final PackageManager pm = mContext.getPackageManager();
@@ -1120,6 +1123,7 @@
* Check {@link NetworkPolicy} against current {@link INetworkStatsService}
* to show visible notifications as needed.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
void updateNotificationsNL() {
if (LOGV) Slog.v(TAG, "updateNotificationsNL()");
Trace.traceBegin(TRACE_TAG_NETWORK, "updateNotificationsNL");
@@ -1266,6 +1270,7 @@
* @return relevant subId, or {@link #INVALID_SUBSCRIPTION_ID} when no
* matching subId found.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private int findRelevantSubIdNL(NetworkTemplate template) {
// Mobile template is relevant when any active subscriber matches
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
@@ -1285,6 +1290,7 @@
* Notify that given {@link NetworkTemplate} is over
* {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void notifyOverLimitNL(NetworkTemplate template) {
if (!mOverLimitNotified.contains(template)) {
mContext.startActivity(buildNetworkOverLimitIntent(mContext.getResources(), template));
@@ -1292,6 +1298,7 @@
}
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void notifyUnderLimitNL(NetworkTemplate template) {
mOverLimitNotified.remove(template);
}
@@ -1462,6 +1469,7 @@
* @param subId that has its associated NetworkPolicy updated if necessary
* @return if any policies were updated
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private boolean maybeUpdateMobilePolicyCycleAL(int subId, String subscriberId) {
if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleAL()");
@@ -1619,6 +1627,7 @@
* @param shouldNormalizePolicies true iff network policies need to be normalized after the
* update.
*/
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
void handleNetworkPoliciesUpdateAL(boolean shouldNormalizePolicies) {
if (shouldNormalizePolicies) {
normalizePoliciesNL();
@@ -1633,6 +1642,7 @@
* Proactively control network data connections when they exceed
* {@link NetworkPolicy#limitBytes}.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
void updateNetworkEnabledNL() {
if (LOGV) Slog.v(TAG, "updateNetworkEnabledNL()");
Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkEnabledNL");
@@ -1773,6 +1783,7 @@
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
void updateNetworkRulesNL() {
if (LOGV) Slog.v(TAG, "updateNetworkRulesNL()");
Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkRulesNL");
@@ -1957,6 +1968,7 @@
* Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we
* have at least a default mobile policy defined.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void ensureActiveMobilePolicyAL() {
if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyAL()");
if (mSuppressDefaultPolicy) return;
@@ -1977,6 +1989,7 @@
* @param subscriberId that we check for an existing policy
* @return true if a mobile network policy was added, or false one already existed.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) {
// Poke around to see if we already have a policy
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
@@ -2036,6 +2049,7 @@
*
* @return if the policy was modified
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private boolean updateDefaultMobilePolicyAL(int subId, NetworkPolicy policy) {
if (!policy.inferred) {
if (LOGD) Slog.d(TAG, "Ignoring user-defined policy " + policy);
@@ -2091,6 +2105,7 @@
}
}
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
private void readPolicyAL() {
if (LOGV) Slog.v(TAG, "readPolicyAL()");
@@ -2322,6 +2337,7 @@
* Perform upgrade step of moving any user-defined meterness overrides over
* into {@link WifiConfiguration}.
*/
+ @GuardedBy({"mNetworkPoliciesSecondLock", "mUidRulesFirstLock"})
private void upgradeWifiMeteredOverrideAL() {
boolean modified = false;
final WifiManager wm = mContext.getSystemService(WifiManager.class);
@@ -2352,6 +2368,7 @@
}
}
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
void writePolicyAL() {
if (LOGV) Slog.v(TAG, "writePolicyAL()");
@@ -2523,6 +2540,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void setUidPolicyUncheckedUL(int uid, int oldPolicy, int policy, boolean persist) {
setUidPolicyUncheckedUL(uid, policy, false);
@@ -2554,6 +2572,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void setUidPolicyUncheckedUL(int uid, int policy, boolean persist) {
if (policy == POLICY_NONE) {
mUidPolicy.delete(uid);
@@ -2601,6 +2620,7 @@
* Removes any persistable state associated with given {@link UserHandle}, persisting
* if any changes that are made.
*/
+ @GuardedBy("mUidRulesFirstLock")
boolean removeUserStateUL(int userId, boolean writePolicy) {
mLogger.removingUserState(userId);
@@ -2702,10 +2722,12 @@
}
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void normalizePoliciesNL() {
normalizePoliciesNL(getNetworkPolicies(mContext.getOpPackageName()));
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void normalizePoliciesNL(NetworkPolicy[] policies) {
mNetworkPolicy.clear();
for (NetworkPolicy policy : policies) {
@@ -2795,6 +2817,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void setRestrictBackgroundUL(boolean restrictBackground) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackgroundUL");
try {
@@ -3447,11 +3470,13 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictBackgroundUL(int uid) {
final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
return isProcStateAllowedWhileOnRestrictBackground(procState);
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictPowerUL(int uid) {
final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
return isProcStateAllowedWhileIdleOrPowerSaveMode(procState);
@@ -3467,6 +3492,7 @@
* {@link #updateRulesForDataUsageRestrictionsUL(int)} and
* {@link #updateRulesForPowerRestrictionsUL(int)}
*/
+ @GuardedBy("mUidRulesFirstLock")
private void updateUidStateUL(int uid, int uidState) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
try {
@@ -3493,6 +3519,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void removeUidStateUL(int uid) {
final int index = mUidState.indexOfKey(uid);
if (index >= 0) {
@@ -3537,6 +3564,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForPowerSaveUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
try {
@@ -3547,10 +3575,12 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRuleForRestrictPowerUL(int uid) {
updateRulesForWhitelistedPowerSaveUL(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForDeviceIdleUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDeviceIdleUL");
try {
@@ -3561,12 +3591,14 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRuleForDeviceIdleUL(int uid) {
updateRulesForWhitelistedPowerSaveUL(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
}
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,
SparseIntArray rules) {
if (enabled) {
@@ -3611,6 +3643,7 @@
* {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
* whitelisted.
*/
+ @GuardedBy("mUidRulesFirstLock")
private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
final int appId = UserHandle.getAppId(uid);
boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
@@ -3623,6 +3656,7 @@
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
@@ -3635,6 +3669,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForAppIdleUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForAppIdleUL");
try {
@@ -3664,6 +3699,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRuleForAppIdleUL(int uid) {
if (!isUidValidForBlacklistRules(uid)) return;
@@ -3687,6 +3723,7 @@
* Toggle the firewall standby chain and inform listeners if the uid rules have effectively
* changed.
*/
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForAppIdleParoleUL() {
boolean paroled = mUsageStats.isAppIdleParoleOn();
boolean enableChain = !paroled;
@@ -3719,6 +3756,7 @@
* Update rules that might be changed by {@link #mRestrictBackground},
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
private void updateRulesForGlobalChangeAL(boolean restrictedNetworksChanged) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
@@ -3740,6 +3778,7 @@
}
// TODO: rename / document to make it clear these are global (not app-specific) rules
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForRestrictPowerUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
try {
@@ -3751,6 +3790,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForRestrictBackgroundUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictBackgroundUL");
try {
@@ -3771,6 +3811,7 @@
}
// TODO: refactor / consolidate all those updateXyz methods, there are way too many of them...
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForAllAppsUL(@RestrictType int type) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL-" + type);
@@ -3822,6 +3863,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForTempWhitelistChangeUL(int appId) {
final List<UserInfo> users = mUserManager.getUsers();
final int numUsers = users.size();
@@ -3886,6 +3928,7 @@
/**
* Clears all state - internal and external - associated with an UID.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void onUidDeletedUL(int uid) {
// First cleanup in-memory state synchronously...
mUidRules.delete(uid);
@@ -3914,6 +3957,7 @@
*
* <p>This method changes both the external firewall rules and the internal state.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void updateRestrictionRulesForUidUL(int uid) {
// Methods below only changes the firewall rules for the power-related modes.
updateRuleForDeviceIdleUL(uid);
@@ -4104,6 +4148,7 @@
* <p>
* <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForPowerRestrictionsUL(int uid) {
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
@@ -4540,6 +4585,7 @@
* @param uidRules new UID rules; if {@code null}, only toggles chain state.
* @param toggle whether the chain should be enabled, disabled, or not changed.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules,
@ChainToggleType int toggle) {
if (uidRules != null) {
@@ -4606,6 +4652,7 @@
/**
* Add or remove a uid to the firewall blacklist for all network ifaces.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void enableFirewallChainUL(int chain, boolean enable) {
if (mFirewallChainStates.indexOfKey(chain) >= 0 &&
mFirewallChainStates.get(chain) == enable) {
@@ -4717,6 +4764,7 @@
mHandler.getLooper().getQueue().addIdleHandler(handler);
}
+ @GuardedBy("mUidRulesFirstLock")
@VisibleForTesting
public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
mRestrictBackgroundPowerState = result;
@@ -5048,6 +5096,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean isRestrictedByAdminUL(int uid) {
final Set<Integer> restrictedUids = mMeteredRestrictedUids.get(
UserHandle.getUserId(uid));
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index c0fbfbb..18f4bc7 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -150,6 +150,7 @@
try {
provider.onConnected();
} catch (RemoteException e) {
+ Slog.e(TAG, "can't connect to service " + info, e);
// we tried
}
if (mCallback != null) {
diff --git a/services/core/java/com/android/server/notification/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/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index dfc61d9..432d17c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -582,16 +582,15 @@
updatedChannel.setLockscreenVisibility(
NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
}
- if (!fromUser) {
- updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
- }
if (fromUser) {
updatedChannel.lockFields(channel.getUserLockedFields());
lockFieldsForUpdate(channel, updatedChannel);
+ } else {
+ updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
}
r.channels.put(updatedChannel.getId(), updatedChannel);
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
+ if (onlyHasDefaultChannel(pkg, uid)) {
// copy settings to app level so they are inherited by new channels
// when the app migrates
r.importance = updatedChannel.getImportance();
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 6760875..b016faf 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -36,7 +36,8 @@
public class ZenLog {
private static final String TAG = "ZenLog";
- private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+ // the ZenLog is *very* verbose, so be careful about setting this to true
+ private static final boolean DEBUG = false;
private static final int SIZE = Build.IS_DEBUGGABLE ? 100 : 20;
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 63c0baf..8013f7a 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -59,7 +59,8 @@
pw.print(prefix); pw.print("mSubscriptions="); pw.println(mSubscriptions);
}
- public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
+ public void evaluateConfig(ZenModeConfig config, ComponentName trigger,
+ boolean processSubscriptions) {
if (config == null) return;
if (config.manualRule != null && config.manualRule.condition != null
&& !config.manualRule.isTrueOrUnknown()) {
@@ -67,9 +68,9 @@
config.manualRule = null;
}
final ArraySet<Uri> current = new ArraySet<>();
- evaluateRule(config.manualRule, current, processSubscriptions);
+ evaluateRule(config.manualRule, current, null, processSubscriptions);
for (ZenRule automaticRule : config.automaticRules.values()) {
- evaluateRule(automaticRule, current, processSubscriptions);
+ evaluateRule(automaticRule, current, trigger, processSubscriptions);
updateSnoozing(automaticRule);
}
@@ -102,7 +103,7 @@
@Override
public void onServiceAdded(ComponentName component) {
if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
- mHelper.setConfig(mHelper.getConfig(), "zmc.onServiceAdded");
+ mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded");
}
@Override
@@ -110,17 +111,22 @@
if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
ZenModeConfig config = mHelper.getConfig();
if (config == null) return;
+ ComponentName trigger = null;
boolean updated = updateCondition(id, condition, config.manualRule);
for (ZenRule automaticRule : config.automaticRules.values()) {
updated |= updateCondition(id, condition, automaticRule);
updated |= updateSnoozing(automaticRule);
+ if (updated) {
+ trigger = automaticRule.component;
+ }
}
if (updated) {
- mHelper.setConfig(config, "conditionChanged");
+ mHelper.setConfig(config, trigger, "conditionChanged");
}
}
- private void evaluateRule(ZenRule rule, ArraySet<Uri> current, boolean processSubscriptions) {
+ private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger,
+ boolean processSubscriptions) {
if (rule == null || rule.conditionId == null) return;
final Uri id = rule.conditionId;
boolean isSystemCondition = false;
@@ -146,7 +152,8 @@
if (current != null) {
current.add(id);
}
- if (processSubscriptions) {
+ if (processSubscriptions && trigger != null && trigger.equals(rule.component)) {
+ if (DEBUG) Log.d(TAG, "Subscribing to " + rule.component);
if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) {
synchronized (mSubscriptions) {
mSubscriptions.put(rule.conditionId, rule.component);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 1954ed4..9c5e064 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -225,7 +225,7 @@
config.user = user;
}
synchronized (mConfig) {
- setConfigLocked(config, reason);
+ setConfigLocked(config, null, reason);
}
cleanUpZenRules();
}
@@ -312,7 +312,7 @@
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
- if (setConfigLocked(newConfig, reason, true)) {
+ if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
} else {
throw new AndroidRuntimeException("Could not create rule");
@@ -342,7 +342,7 @@
}
populateZenRule(automaticZenRule, rule, false);
newConfig.automaticRules.put(ruleId, rule);
- return setConfigLocked(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, rule.component, true);
}
}
@@ -360,7 +360,7 @@
throw new SecurityException(
"Cannot delete rules not owned by your condition provider");
}
- return setConfigLocked(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, null, true);
}
}
@@ -376,7 +376,7 @@
newConfig.automaticRules.removeAt(i);
}
}
- return setConfigLocked(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, null, true);
}
}
@@ -537,7 +537,7 @@
newRule.enabler = caller;
newConfig.manualRule = newRule;
}
- setConfigLocked(newConfig, reason, setRingerMode);
+ setConfigLocked(newConfig, reason, null, setRingerMode);
}
}
@@ -644,7 +644,7 @@
}
if (DEBUG) Log.d(TAG, reason);
synchronized (mConfig) {
- setConfigLocked(config, reason);
+ setConfigLocked(config, null, reason);
}
}
}
@@ -673,7 +673,7 @@
synchronized (mConfig) {
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
- setConfigLocked(newConfig, "setNotificationPolicy");
+ setConfigLocked(newConfig, null, "setNotificationPolicy");
}
}
@@ -697,7 +697,7 @@
}
}
}
- setConfigLocked(newConfig, "cleanUpZenRules");
+ setConfigLocked(newConfig, null, "cleanUpZenRules");
}
}
@@ -710,17 +710,19 @@
}
}
- public boolean setConfigLocked(ZenModeConfig config, String reason) {
- return setConfigLocked(config, reason, true /*setRingerMode*/);
+ public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+ String reason) {
+ return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/);
}
- public void setConfig(ZenModeConfig config, String reason) {
+ public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason) {
synchronized (mConfig) {
- setConfigLocked(config, reason);
+ setConfigLocked(config, triggeringComponent, reason);
}
}
- private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
+ private boolean setConfigLocked(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
final long identity = Binder.clearCallingIdentity();
try {
if (config == null || !config.isValid()) {
@@ -733,7 +735,8 @@
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
- mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
+ // may modify config
+ mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
mConfigs.put(config.user, config);
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(reason, mConfig, config);
@@ -746,7 +749,7 @@
dispatchOnPolicyChanged();
}
mConfig = config;
- mHandler.postApplyConfig(config, reason, setRingerMode);
+ mHandler.postApplyConfig(config, reason, triggeringComponent, setRingerMode);
return true;
} catch (SecurityException e) {
Log.wtf(TAG, "Invalid rule in config", e);
@@ -756,13 +759,14 @@
}
}
- private void applyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+ private void applyConfig(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
if (!evaluateZenMode(reason, setRingerMode)) {
applyRestrictions(); // evaluateZenMode will also apply restrictions if changed
}
- mConditions.evaluateConfig(config, true /*processSubscriptions*/);
+ mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
}
private int getZenModeSetting() {
@@ -1268,13 +1272,16 @@
private final class ConfigMessageData {
public final ZenModeConfig config;
+ public ComponentName triggeringComponent;
public final String reason;
public final boolean setRingerMode;
- ConfigMessageData(ZenModeConfig config, String reason, boolean setRingerMode) {
+ ConfigMessageData(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
this.config = config;
this.reason = reason;
this.setRingerMode = setRingerMode;
+ this.triggeringComponent = triggeringComponent;
}
}
@@ -1294,9 +1301,10 @@
sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS);
}
- private void postApplyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+ private void postApplyConfig(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
sendMessage(obtainMessage(MSG_APPLY_CONFIG,
- new ConfigMessageData(config, reason, setRingerMode)));
+ new ConfigMessageData(config, reason, triggeringComponent, setRingerMode)));
}
@Override
@@ -1311,7 +1319,7 @@
case MSG_APPLY_CONFIG:
ConfigMessageData applyConfigData = (ConfigMessageData) msg.obj;
applyConfig(applyConfigData.config, applyConfigData.reason,
- applyConfigData.setRingerMode);
+ applyConfigData.triggeringComponent, applyConfigData.setRingerMode);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b0be4a9..55b1940 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -34,6 +34,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
@@ -289,7 +290,8 @@
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
- profileName, dexMetadataPath, getReasonName(compilationReason));
+ profileName, dexMetadataPath,
+ getAugmentedReasonName(compilationReason, dexMetadataPath != null));
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -302,6 +304,12 @@
}
}
+ private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) {
+ String annotation = useDexMetadata
+ ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : "";
+ return getReasonName(compilationReason) + annotation;
+ }
+
/**
* Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
*
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 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 3919f50..ac33454 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -452,7 +452,6 @@
static final int SCAN_NEW_INSTALL = 1<<2;
static final int SCAN_UPDATE_TIME = 1<<3;
static final int SCAN_BOOTING = 1<<4;
- static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<6;
static final int SCAN_REQUIRE_KNOWN = 1<<7;
static final int SCAN_MOVE = 1<<8;
static final int SCAN_INITIAL = 1<<9;
@@ -475,7 +474,6 @@
SCAN_NEW_INSTALL,
SCAN_UPDATE_TIME,
SCAN_BOOTING,
- SCAN_DELETE_DATA_ON_FAILURES,
SCAN_REQUIRE_KNOWN,
SCAN_MOVE,
SCAN_INITIAL,
@@ -1390,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;
@@ -2518,7 +2517,7 @@
}
}
- if (mFirstBoot) {
+ if (!mOnlyCore && mFirstBoot) {
requestCopyPreoptedFiles();
}
@@ -3242,6 +3241,7 @@
mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
SharedLibraryInfo.VERSION_UNDEFINED);
+ mRequiredPermissionControllerPackage = getRequiredPermissionsControllerLPr();
} else {
mRequiredVerifierPackage = null;
mRequiredInstallerPackage = null;
@@ -3250,6 +3250,7 @@
mIntentFilterVerifier = null;
mServicesSystemSharedLibraryPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
+ mRequiredPermissionControllerPackage = null;
}
mInstallerService = new PackageInstallerService(context, this);
@@ -3597,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);
@@ -5403,6 +5423,12 @@
@Override
public String getPermissionControllerPackageName() {
synchronized (mPackages) {
+ return mRequiredPermissionControllerPackage;
+ }
+ }
+
+ String getPackageInstallerPackageName() {
+ synchronized (mPackages) {
return mRequiredInstallerPackage;
}
}
@@ -8951,15 +8977,20 @@
}
}
- final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
+ final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
+ if (scanResult.success) {
+ synchronized (mPackages) {
+ commitScanResultLocked(scanResult);
+ }
+ }
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
- return scannedPkg;
+ return scanResult.pkgSetting.pkg;
}
private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
@@ -9985,7 +10016,7 @@
}
@GuardedBy({"mInstallLock", "mPackages"})
- private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg,
+ private List<ScanResult> scanPackageTracedLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
@@ -10002,16 +10033,16 @@
scanFlags &= ~SCAN_CHECK_ONLY;
}
- final PackageParser.Package scannedPkg;
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ final List<ScanResult> scanResults = new ArrayList<>(1 + childCount);
try {
// Scan the parent
- scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user);
+ scanResults.add(scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user));
// Scan the children
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
- scanPackageNewLI(childPkg, parseFlags,
- scanFlags, currentTime, user);
+ scanResults.add(scanPackageNewLI(childPkg, parseFlags,
+ scanFlags, currentTime, user));
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -10021,11 +10052,13 @@
return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
}
- return scannedPkg;
+ return scanResults;
}
/** The result of a package scan. */
private static class ScanResult {
+ /** The request that initiated the scan that produced this result. */
+ public final ScanRequest request;
/** Whether or not the package scan was successful */
public final boolean success;
/**
@@ -10036,9 +10069,10 @@
/** ABI code paths that have changed in the package scan */
@Nullable public final List<String> changedAbiCodePath;
public ScanResult(
- boolean success,
+ ScanRequest request, boolean success,
@Nullable PackageSetting pkgSetting,
@Nullable List<String> changedAbiCodePath) {
+ this.request = request;
this.success = success;
this.pkgSetting = pkgSetting;
this.changedAbiCodePath = changedAbiCodePath;
@@ -10181,7 +10215,7 @@
// method. Also, we need to solve the problem of potentially creating a new shared user
// setting. That can probably be done later and patch things up after the fact.
@GuardedBy({"mInstallLock", "mPackages"})
- private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
+ private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
@@ -10217,28 +10251,30 @@
+ " packages=" + sharedUserSetting.packages);
}
}
+ final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
+ pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
+ originalPkgSetting, realPkgName, parseFlags, scanFlags,
+ (pkg == mPlatformPackage), user);
+ return scanPackageOnlyLI(request, mFactoryTest, currentTime);
+ }
+ }
- boolean scanSucceeded = false;
- try {
- final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
- originalPkgSetting, realPkgName, parseFlags, scanFlags,
- (pkg == mPlatformPackage), user);
- final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
- if (result.success) {
- commitScanResultsLocked(request, result);
+
+ private void commitSuccessfulScanResults(@NonNull List<ScanResult> results)
+ throws PackageManagerException {
+ synchronized(mPackages) {
+ for (ScanResult result : results) {
+ // failures should have been caught earlier, but in case it wasn't,
+ // let's double check
+ if (!result.success) {
+ throw new PackageManagerException(
+ "Scan failed for " + result.request.pkg.packageName);
}
- scanSucceeded = true;
- } finally {
- if (!scanSucceeded && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
- // DELETE_DATA_ON_FAILURES is only used by frozen paths
- destroyAppDataLIF(pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
- destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
- }
+ }
+ for (ScanResult result : results) {
+ commitScanResultLocked(result);
}
}
- return pkg;
}
/**
@@ -10249,8 +10285,8 @@
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy({"mPackages", "mInstallLock"})
- private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result)
- throws PackageManagerException {
+ private void commitScanResultLocked(@NonNull ScanResult result) throws PackageManagerException {
+ final ScanRequest request = result.request;
final PackageParser.Package pkg = request.pkg;
final PackageParser.Package oldPkg = request.oldPkg;
final @ParseFlags int parseFlags = request.parseFlags;
@@ -10831,7 +10867,7 @@
pkgSetting.volumeUuid = volumeUuid;
}
- return new ScanResult(true, pkgSetting, changedAbiCodePath);
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath);
}
/**
@@ -14505,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");
@@ -16384,14 +16426,18 @@
}
try {
- PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
+ final PackageParser.Package newPackage;
+ List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
-
+ commitSuccessfulScanResults(scanResults);
+ // TODO(b/109941548): Child packages may return >1 result with the first being the base;
+ // we need to treat child packages as an atomic install and remove this hack
+ final ScanResult basePackageScanResult = scanResults.get(0);
+ newPackage = basePackageScanResult.pkgSetting.pkg;
updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
prepareAppDataAfterInstallLIF(newPackage);
-
} else {
// Remove package from internal structures, but keep around any
// data that might have already existed
@@ -16399,6 +16445,9 @@
PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
+ destroyAppDataLIF(pkg, UserHandle.USER_ALL,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
@@ -16655,8 +16704,10 @@
| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
try {
- final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
+ final List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
+ commitSuccessfulScanResults(scanResults);
+ final PackageParser.Package newPackage = scanResults.get(0).pkgSetting.pkg;
updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
installReason);
@@ -16794,8 +16845,11 @@
PackageParser.Package newPackage = null;
try {
+ final List<ScanResult> scanResults =
+ scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
// Add the package to the internal data structures
- newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
+ commitSuccessfulScanResults(scanResults);
+ newPackage = scanResults.get(0).pkgSetting.pkg;
// Set the update and install times
PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
@@ -16852,7 +16906,9 @@
}
// Add back the old system package
try {
- scanPackageTracedLI(deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+ final List<ScanResult> scanResults = scanPackageTracedLI(
+ deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+ commitSuccessfulScanResults(scanResults);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
}
@@ -17663,7 +17719,7 @@
replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
installerPackageName, res, args.installReason);
} else {
- installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
+ installNewPackageLIF(pkg, parseFlags, scanFlags,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
@@ -19509,8 +19565,7 @@
// If permission review is enabled and this is a legacy app, mark the
// permission as requiring a review as this is the initial state.
int flags = 0;
- if (mSettings.mPermissions.mPermissionReviewRequired
- && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
}
if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
@@ -23340,17 +23395,10 @@
void onNewUserCreated(final int userId) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
synchronized(mPackages) {
- // If permission review for legacy apps is required, we represent
- // dagerous permissions for such apps as always granted runtime
- // permissions to keep per user flag state whether review is needed.
- // Hence, if a new user is added we have to propagate dangerous
- // permission grants for these legacy apps.
- if (mSettings.mPermissions.mPermissionReviewRequired) {
-// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
- mPermissionManager.updateAllPermissions(
- StorageManager.UUID_PRIVATE_INTERNAL, true, mPackages.values(),
- mPermissionCallback);
- }
+ // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
+ mPermissionManager.updateAllPermissions(
+ StorageManager.UUID_PRIVATE_INTERNAL, true, mPackages.values(),
+ mPermissionCallback);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d06437e..3834a88 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -547,6 +547,10 @@
case "-e":
listEnabled = true;
break;
+ case "-a":
+ getFlags |= PackageManager.MATCH_KNOWN_PACKAGES;
+ getFlags |= PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
+ break;
case "-f":
showSourceDir = true;
break;
@@ -2419,30 +2423,30 @@
private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final ParcelFileDescriptor fd;
- if (STDIN_PATH.equals(inPath)) {
- fd = new ParcelFileDescriptor(getInFileDescriptor());
- } else if (inPath != null) {
- fd = openFileForSystem(inPath, "r");
- if (fd == null) {
- return -1;
- }
- sizeBytes = fd.getStatSize();
- if (sizeBytes < 0) {
- getErrPrintWriter().println("Unable to get size of: " + inPath);
- return -1;
- }
- } else {
- fd = new ParcelFileDescriptor(getInFileDescriptor());
- }
- if (sizeBytes <= 0) {
- getErrPrintWriter().println("Error: must specify a APK size");
- return 1;
- }
-
PackageInstaller.Session session = null;
try {
+ final PrintWriter pw = getOutPrintWriter();
+ final ParcelFileDescriptor fd;
+ if (STDIN_PATH.equals(inPath)) {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ } else if (inPath != null) {
+ fd = openFileForSystem(inPath, "r");
+ if (fd == null) {
+ return -1;
+ }
+ sizeBytes = fd.getStatSize();
+ if (sizeBytes < 0) {
+ getErrPrintWriter().println("Unable to get size of: " + inPath);
+ return -1;
+ }
+ } else {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ }
+ if (sizeBytes <= 0) {
+ getErrPrintWriter().println("Error: must specify a APK size");
+ return 1;
+ }
+
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
session.write(splitName, 0, sizeBytes, fd);
@@ -2688,6 +2692,7 @@
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
+ pw.println(" -a: all known packages");
pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
pw.println(" -s: filter to only show system packages");
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 0ba7822..21daa39 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -519,6 +519,11 @@
private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
private static final int TRON_COMPILATION_REASON_INACTIVE = 7;
private static final int TRON_COMPILATION_REASON_SHARED = 8;
+ private static final int TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA = 9;
+
+ // The annotation to add as a suffix to the compilation reason when dexopt was
+ // performed with dex metadata.
+ public static final String DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION = "-dm";
/**
* Convert the compilation reason to an int suitable to be logged to TRON.
@@ -534,6 +539,10 @@
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
case "shared" : return TRON_COMPILATION_REASON_SHARED;
+ // This is a special marker for dex metadata installation that does not
+ // have an equivalent as a system property.
+ case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
+ return TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA;
default: return TRON_COMPILATION_REASON_UNKNOWN;
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8a6fbaa..76832ed 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -161,7 +161,7 @@
mLock = externalLock;
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
- mSettings = new PermissionSettings(context, mLock);
+ mSettings = new PermissionSettings(mLock);
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
@@ -845,10 +845,7 @@
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
- if (!appSupportsRuntimePermissions && !mSettings.mPermissionReviewRequired) {
- // For legacy apps dangerous permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (origPermissions.hasInstallPermission(bp.getName())) {
+ if (origPermissions.hasInstallPermission(bp.getName())) {
// For legacy apps that became modern, install becomes runtime.
grant = GRANT_UPGRADE;
} else if (isLegacySystemApp) {
@@ -934,7 +931,7 @@
updatedUserIds = ArrayUtils.appendInt(
updatedUserIds, userId);
}
- if (!mSettings.mPermissionReviewRequired || !revokeOnUpgrade) {
+ if (!revokeOnUpgrade) {
if (permissionsState.grantRuntimePermission(bp, userId) ==
PermissionsState.PERMISSION_OPERATION_FAILURE) {
// If we cannot put the permission as it was,
@@ -945,8 +942,7 @@
}
// If the app supports runtime permissions no need for a review.
- if (mSettings.mPermissionReviewRequired
- && appSupportsRuntimePermissions
+ if (appSupportsRuntimePermissions
&& (flags & PackageManager
.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -954,8 +950,7 @@
updatedUserIds = ArrayUtils.appendInt(
updatedUserIds, userId);
}
- } else if (mSettings.mPermissionReviewRequired
- && !appSupportsRuntimePermissions) {
+ } else if (!appSupportsRuntimePermissions) {
// For legacy apps that need a permission review, every new
// runtime permission is granted but it is pending a review.
// We also need to review only platform defined runtime
@@ -1329,10 +1324,6 @@
}
private boolean isPermissionsReviewRequired(PackageParser.Package pkg, int userId) {
- if (!mSettings.mPermissionReviewRequired) {
- return false;
- }
-
// Permission review applies only to apps not supporting the new permission model.
if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
return false;
@@ -1434,7 +1425,7 @@
grantRuntimePermission(permission, pkg.packageName, false, callingUid,
userId, callback);
}
- } else if (mSettings.mPermissionReviewRequired) {
+ } else {
// In permission review mode we clear the review flag when we
// are asked to install the app with all permissions granted.
if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
@@ -1485,8 +1476,7 @@
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
- if (mSettings.mPermissionReviewRequired
- && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
&& bp.isRuntime()) {
return;
}
@@ -1607,8 +1597,7 @@
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
- if (mSettings.mPermissionReviewRequired
- && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
&& bp.isRuntime()) {
return;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index b3f2833..9208032 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -47,8 +47,6 @@
*/
public class PermissionSettings {
- public final boolean mPermissionReviewRequired;
-
/**
* All of the permissions known to the system. The mapping is from permission
* name to permission object.
@@ -82,9 +80,7 @@
private final Object mLock;
- PermissionSettings(@NonNull Context context, @NonNull Object lock) {
- mPermissionReviewRequired =
- context.getResources().getBoolean(R.bool.config_permissionReviewRequired);
+ PermissionSettings(@NonNull Object lock) {
mLock = lock;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b6222bb..e46c03e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4391,17 +4391,9 @@
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
return;
- } else if (mKeyguardOccluded && mKeyguardDelegate.isShowing()) {
- mKeyguardDelegate.dismiss(new KeyguardDismissCallback() {
- @Override
- public void onDismissSucceeded() throws RemoteException {
- mHandler.post(() -> {
- startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
- });
- }
- }, null /* message */);
- return;
- } else if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
+ }
+
+ if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@@ -4669,7 +4661,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/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index ded2c15..73775b4 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -219,12 +219,12 @@
}
@Override
- public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
+ public int checkSlicePermission(Uri uri, String callingPkg, String pkg, int pid, int uid,
String[] autoGrantPermissions) {
int userId = UserHandle.getUserId(uid);
if (pkg == null) {
for (String p : mContext.getPackageManager().getPackagesForUid(uid)) {
- if (checkSlicePermission(uri, p, pid, uid, autoGrantPermissions)
+ if (checkSlicePermission(uri, callingPkg, p, pid, uid, autoGrantPermissions)
== PERMISSION_GRANTED) {
return PERMISSION_GRANTED;
}
@@ -237,9 +237,9 @@
if (mPermissions.hasPermission(pkg, userId, uri)) {
return PackageManager.PERMISSION_GRANTED;
}
- if (autoGrantPermissions != null) {
+ if (autoGrantPermissions != null && callingPkg != null) {
// Need to own the Uri to call in with permissions to grant.
- enforceOwner(pkg, uri, userId);
+ enforceOwner(callingPkg, uri, userId);
for (String perm : autoGrantPermissions) {
if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
int providerUser = ContentProvider.getUserIdFromUri(uri, userId);
@@ -391,7 +391,7 @@
}
protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
- return checkSlicePermission(uri, pkg, uid, pid, null);
+ return checkSlicePermission(uri, null, pkg, uid, pid, null);
}
private String getProviderPkg(Uri uri, int user) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c7c24a5..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;
@@ -1179,6 +1209,7 @@
}
}
+ @GuardedBy("StatsCompanionService.sStatsdLock")
private void forgetEverythingLocked() {
sStatsd = null;
mContext.unregisterReceiver(mAppUpdateReceiver);
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 85de581..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();
@@ -1552,13 +1558,15 @@
}
traceEnd();
- traceBeginAndSlog("StartPruneInstantAppsJobService");
- try {
- PruneInstantAppsJobService.schedule(context);
- } catch (Throwable e) {
- reportWtf("StartPruneInstantAppsJobService", e);
+ if (!isWatch) {
+ traceBeginAndSlog("StartPruneInstantAppsJobService");
+ try {
+ PruneInstantAppsJobService.schedule(context);
+ } catch (Throwable e) {
+ reportWtf("StartPruneInstantAppsJobService", e);
+ }
+ traceEnd();
}
- traceEnd();
// LauncherAppsService uses ShortcutService.
traceBeginAndSlog("StartShortcutServiceLifecycle");
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index c1c32c2..774a3bc 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -708,8 +708,10 @@
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
- clearClientLocked();
- mRemoteInstance = null;
+ if (mRemoteInstance != null) {
+ clearClientLocked();
+ mRemoteInstance = null;
+ }
}
}
}
diff --git a/services/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/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 01f5384..aeae11a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -66,7 +66,6 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AppOpsService;
import org.junit.After;
@@ -121,7 +120,6 @@
@Mock private Context mContext;
@Mock private AppOpsService mAppOpsService;
@Mock private PackageManager mPackageManager;
- @Mock private BatteryStatsImpl mBatteryStatsImpl;
private TestInjector mInjector;
private ActivityManagerService mAms;
@@ -258,8 +256,7 @@
uidRec.hasInternetPermission = true;
mAms.mActiveUids.put(uid, uidRec);
- final ProcessRecord appRec = new ProcessRecord(mAms, mBatteryStatsImpl,
- new ApplicationInfo(), TAG, uid);
+ final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid);
appRec.thread = Mockito.mock(IApplicationThread.class);
mAms.mLruProcesses.add(appRec);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 19a3e4a..409a2a8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -41,6 +41,7 @@
import android.content.pm.ActivityInfo.WindowLayout;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
@@ -72,8 +73,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;
-import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.am.ActivityStarter.Factory;
import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
import com.android.server.am.TaskRecord.TaskRecordFactory;
@@ -202,8 +201,7 @@
// If no caller app, return {@code null} {@link ProcessRecord}.
final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
- ? null : new ProcessRecord(service.mAm, mock(BatteryStatsImpl.class),
- mock(ApplicationInfo.class), null, 0);
+ ? null : new ProcessRecord(service.mAm, mock(ApplicationInfo.class), null, 0);
doReturn(record).when(service.mAm).getRecordForAppLocked(anyObject());
@@ -327,6 +325,16 @@
doReturn(stack).when(mService.mStackSupervisor)
.getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
+ // Set up mock package manager internal and make sure no unmocked methods are called
+ PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
+ invocation -> {
+ throw new RuntimeException("Not stubbed");
+ });
+ doReturn(mockPackageManager).when(mService.mAm).getPackageManagerInternalLocked();
+
+ // Never review permissions
+ doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
+
final Intent intent = new Intent();
intent.addFlags(launchFlags);
intent.setComponent(ActivityBuilder.getDefaultComponent());
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/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 3d11c4c..243c1b3 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -66,7 +66,7 @@
@UiThreadTest
public void testCreateWorks() throws Exception {
AppErrorDialog.Data data = new AppErrorDialog.Data();
- data.proc = new ProcessRecord(null, null, mContext.getApplicationInfo(), "name", 12345);
+ data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
data.result = new AppErrorResult();
AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 2d5afad..525135c 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -134,7 +134,12 @@
}
@Override
- public boolean isPermissionReviewModeEnabled() {
+ public boolean arePermissionsIndividuallyControlled() {
+ return false;
+ }
+
+ @Override
+ public boolean isWirelessConsentModeEnabled() {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
new file mode 100644
index 0000000..14695c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link ArcTerminationActionFromAvr} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class ArcTerminationActionFromAvrTest {
+
+ private HdmiDeviceInfo mDeviceInfoForTests;
+ private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private ArcTerminationActionFromAvr mAction;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private boolean mSendCecCommandSuccess;
+ private boolean mShouldDispatchReportArcTerminated;
+ private boolean mArcEnabled;
+ private boolean mSetArcStatusCalled;
+
+ @Before
+ public void setUp() {
+ mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+
+ HdmiControlService hdmiControlService =
+ new HdmiControlService(null) {
+ @Override
+ void sendCecCommand(
+ HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_TERMINATE_ARC:
+ if (callback != null) {
+ callback.onSendCompleted(
+ mSendCecCommandSuccess
+ ? SendMessageResult.SUCCESS
+ : SendMessageResult.NACK);
+ }
+ if (mShouldDispatchReportArcTerminated) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildReportArcTerminated(
+ Constants.ADDR_TV,
+ mHdmiCecLocalDeviceAudioSystem.mAddress));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
+ }
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ boolean isAddressAllocated() {
+ return true;
+ }
+
+ @Override
+ Looper getServiceLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
+
+ mHdmiCecLocalDeviceAudioSystem =
+ new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
+ @Override
+ HdmiDeviceInfo getDeviceInfo() {
+ return mDeviceInfoForTests;
+ }
+
+ @Override
+ void setArcStatus(boolean enabled) {
+ mSetArcStatusCalled = true;
+ mArcEnabled = enabled;
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem.init();
+ Looper looper = mTestLooper.getLooper();
+ hdmiControlService.setIoLooper(looper);
+
+ mArcEnabled = true;
+ mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
+ }
+
+ @Test
+ public void testSendMessage_NotSuccess() {
+ mSendCecCommandSuccess = false;
+ mShouldDispatchReportArcTerminated = false;
+ mSetArcStatusCalled = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+ mTestLooper.dispatchAll();
+ assertThat(mSetArcStatusCalled).isFalse();
+ assertThat(mArcEnabled).isTrue();
+ }
+
+ @Test
+ public void testReportArcTerminated_NotReceived() {
+ mSendCecCommandSuccess = true;
+ mShouldDispatchReportArcTerminated = false;
+ mSetArcStatusCalled = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+ mTestLooper.moveTimeForward(1000);
+ mTestLooper.dispatchAll();
+ assertThat(mSetArcStatusCalled).isFalse();
+ assertThat(mArcEnabled).isTrue();
+ }
+
+ @Test
+ public void testReportArcTerminated_Received() {
+ mSendCecCommandSuccess = true;
+ mShouldDispatchReportArcTerminated = true;
+ mSetArcStatusCalled = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+ mTestLooper.moveTimeForward(1000);
+ mTestLooper.dispatchAll();
+ assertThat(mSetArcStatusCalled).isTrue();
+ assertThat(mArcEnabled).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index d437ca1..e114e03 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -23,16 +23,13 @@
import android.os.Looper;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-
import com.android.server.hdmi.HdmiCecLocalDeviceAudioSystem.TvSystemAudioModeSupportedCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link DetectTvSystemAudioModeSupportAction} class.
- */
+/** Tests for {@link DetectTvSystemAudioModeSupportAction} class. */
@SmallTest
@RunWith(JUnit4.class)
public class DetectTvSystemAudioModeSupportActionTest {
@@ -49,46 +46,49 @@
@Before
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
- HdmiControlService hdmiControlService = new HdmiControlService(null) {
+ HdmiControlService hdmiControlService =
+ new HdmiControlService(null) {
- @Override
- void sendCecCommand(HdmiCecMessage command,
- @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
- if (callback != null) {
- callback.onSendCompleted(mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS : SendMessageResult.NACK);
+ @Override
+ void sendCecCommand(
+ HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ if (callback != null) {
+ callback.onSendCompleted(
+ mSendCecCommandSuccess
+ ? SendMessageResult.SUCCESS
+ : SendMessageResult.NACK);
+ }
+ if (mShouldDispatchFeatureAbort) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ Constants.ADDR_TV,
+ mHdmiCecLocalDeviceAudioSystem.mAddress,
+ Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
+ Constants.ABORT_UNRECOGNIZED_OPCODE));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
}
- if (mShouldDispatchFeatureAbort) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildFeatureAbortCommand(
- Constants.ADDR_TV,
- mHdmiCecLocalDeviceAudioSystem.mAddress,
- Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
- Constants.ABORT_UNRECOGNIZED_OPCODE));
- }
- break;
- default:
- throw new IllegalArgumentException("Unexpected message");
- }
- }
+ }
- @Override
- boolean isPowerStandby() {
- return false;
- }
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
- @Override
- boolean isAddressAllocated() {
- return true;
- }
+ @Override
+ boolean isAddressAllocated() {
+ return true;
+ }
- @Override
- Looper getServiceLooper() {
- return mTestLooper.getLooper();
- }
- };
+ @Override
+ Looper getServiceLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
@@ -100,13 +100,14 @@
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
- mAction = new DetectTvSystemAudioModeSupportAction(
- mHdmiCecLocalDeviceAudioSystem,
- new TvSystemAudioModeSupportedCallback() {
- public void onResult(boolean supported) {
- mSupported = Boolean.valueOf(supported);
- }
- });
+ mAction =
+ new DetectTvSystemAudioModeSupportAction(
+ mHdmiCecLocalDeviceAudioSystem,
+ new TvSystemAudioModeSupportedCallback() {
+ public void onResult(boolean supported) {
+ mSupported = Boolean.valueOf(supported);
+ }
+ });
mSupported = null;
}
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..7484edd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -18,7 +18,10 @@
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.MessageQueue;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
+import java.util.ArrayList;
+import java.util.List;
/** Fake {@link NativeWrapper} useful for testing. */
final class FakeNativeWrapper implements NativeWrapper {
@@ -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/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 84e1b7e..6cf5f00 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -15,17 +15,6 @@
*/
package com.android.server.hdmi;
-import android.content.Context;
-import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
-import android.util.Log;
-import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
-import com.android.server.hdmi.HdmiCecController.NativeWrapper;
-
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
@@ -37,14 +26,19 @@
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link com.android.server.hdmi.HdmiCecController} class.
- */
+/** Tests for {@link com.android.server.hdmi.HdmiCecController} class. */
@SmallTest
@RunWith(JUnit4.class)
public class HdmiCecControllerTest {
@@ -71,12 +65,13 @@
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private int mLogicalAddress = 16;
- private AllocateAddressCallback mCallback = new AllocateAddressCallback() {
- @Override
- public void onAllocated(int deviceType, int logicalAddress) {
- mLogicalAddress = logicalAddress;
- }
- };
+ private AllocateAddressCallback mCallback =
+ new AllocateAddressCallback() {
+ @Override
+ public void onAllocated(int deviceType, int logicalAddress) {
+ mLogicalAddress = logicalAddress;
+ }
+ };
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
@@ -86,13 +81,11 @@
mMyLooper = mTestLooper.getLooper();
mHdmiControlService = new MyHdmiControlService(null);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper);
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
}
- /**
- * Tests for {@link HdmiCecController#allocateLogicalAddress}
- */
+ /** Tests for {@link HdmiCecController#allocateLogicalAddress} */
@Test
public void testAllocatLogicalAddress_TvDevicePreferredNotOcupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_TV, mCallback);
@@ -128,7 +121,7 @@
@Test
public void testAllocatLogicalAddress_AudioSystemNonPreferredNotOcupied() {
mHdmiCecController.allocateLogicalAddress(
- DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
+ DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_AUDIO_SYSTEM, mLogicalAddress);
}
@@ -137,7 +130,7 @@
public void testAllocatLogicalAddress_AudioSystemNonPreferredAllOcupied() {
mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(
- DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
+ DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
}
@@ -152,8 +145,7 @@
@Test
public void testAllocatLogicalAddress_PlaybackPreferredOcuppied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
- mHdmiCecController.allocateLogicalAddress(
- DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
+ mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_2, mLogicalAddress);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index b392935..6845b2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -21,15 +21,12 @@
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertEquals;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-
+import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
@@ -38,9 +35,7 @@
@SmallTest
@RunWith(JUnit4.class)
-/**
- * Tests for {@link HdmiCecLocalDeviceAudioSystem} class.
- */
+/** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
public class HdmiCecLocalDeviceAudioSystemTest {
private static final String TAG = "HdmiCecLocalDeviceAudioSystemTest";
@@ -54,82 +49,77 @@
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() {
- return new AudioManager() {
+ mHdmiControlService =
+ new HdmiControlService(null) {
@Override
- public int getStreamVolume(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicVolume;
- default:
- return 0;
- }
- }
-
- @Override
- public boolean isStreamMute(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicMute;
- default:
- return false;
- }
- }
-
- @Override
- public int getStreamMaxVolume(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicMaxVolume;
- default:
- return 100;
- }
- }
-
- @Override
- public void adjustStreamVolume(int streamType, int direction, int flags) {
- switch (streamType) {
- case STREAM_MUSIC:
- if (direction == AudioManager.ADJUST_UNMUTE) {
- mMusicMute = false;
- } else if (direction == AudioManager.ADJUST_MUTE) {
- mMusicMute = true;
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public int getStreamVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicVolume;
+ default:
+ return 0;
}
- default:
- }
- }
- };
- }
+ }
- @Override
- void wakeUp() {
- isAwake = true;
- }
- };
+ @Override
+ public boolean isStreamMute(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMute;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public int getStreamMaxVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMaxVolume;
+ default:
+ return 100;
+ }
+ }
+
+ @Override
+ public void adjustStreamVolume(
+ int streamType, int direction, int flags) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ if (direction == AudioManager.ADJUST_UNMUTE) {
+ mMusicMute = false;
+ } else if (direction == AudioManager.ADJUST_MUTE) {
+ mMusicMute = true;
+ }
+ default:
+ }
+ }
+ };
+ }
+
+ @Override
+ void wakeUp() {}
+ };
mMyLooper = mTestLooper.getLooper();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
mHdmiCecLocalDeviceAudioSystem.init();
mHdmiControlService.setIoLooper(mMyLooper);
-
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper);
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
-
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
mHdmiControlService.initPortInfo();
// No TV device interacts with AVR so system audio control won't be turned on here
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
-
mTestLooper.dispatchAll();
}
@@ -139,161 +129,240 @@
mMusicMute = true;
mMusicMaxVolume = 20;
int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder.buildReportAudioStatus(
- ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
- HdmiCecMessage messageGive = HdmiCecMessageBuilder.buildGiveAudioStatus(
- ADDR_TV, ADDR_AUDIO_SYSTEM);
-
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive));
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportAudioStatus(
+ ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
public void handleGiveSystemAudioModeStatus_originalOff() {
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
- HdmiCecMessage messageGive = HdmiCecMessageBuilder
- .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
public void handleRequestArcInitiate() {
// TODO(b/80296911): Add tests when finishing handler impl.
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
- .buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message));
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
public void handleRequestArcTermination() {
// TODO(b/80297105): Add tests when finishing handler impl.
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
- .buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
- HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
- .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff));
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+ HdmiCecMessage messageRequestOff =
+ HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
public void handleSetSystemAudioMode_setOn_orignalOff() {
mMusicMute = true;
- HdmiCecMessage messageSet = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
- HdmiCecMessage messageGive = HdmiCecMessageBuilder
- .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+ HdmiCecMessage messageSet =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
// Check if originally off
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
-
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
-
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
// Check if correctly turned on
- expectMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
-
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet));
+ expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
- assertFalse(mMusicMute);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mMusicMute).isEqualTo(false);
}
@Test
public void handleSystemAudioModeRequest_turnOffByTv() {
- assertFalse(mMusicMute);
-
+ assertThat(mMusicMute).isEqualTo(false);
// Check if feature correctly turned off
- HdmiCecMessage messageGive = HdmiCecMessageBuilder
- .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
- HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
- .buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
-
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff));
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage messageRequestOff =
+ HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
-
- expectMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
- assertTrue(mMusicMute);
+ 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 expectMessage = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
- assertTrue(mMusicMute);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.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);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
- Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
-
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+ Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
+ assertThat(
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isNotEmpty();
}
@Test
public void systemAudioControlOnPowerOn_neverOn() {
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
- Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
-
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+ Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
+ assertThat(
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isEmpty();
}
@Test
public void systemAudioControlOnPowerOn_useLastState_off() {
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
- Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
-
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+ Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
+ assertThat(
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isEmpty();
}
@Test
public void systemAudioControlOnPowerOn_useLastState_on() {
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(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();
+ }
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+ @Test
+ public void handleActiveSource_updateActiveSource() {
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)).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.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.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/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 833ffa6..3cd8481 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -27,13 +27,8 @@
import static junit.framework.Assert.assertTrue;
import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiPortInfo;
-import android.os.PowerManager;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-import android.os.MessageQueue;
-import com.android.server.hdmi.HdmiCecController.NativeWrapper;
-import junit.framework.Assert;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
@@ -42,20 +37,19 @@
@SmallTest
@RunWith(JUnit4.class)
-/**
- * Tests for {@link HdmiCecLocalDevice} class.
- */
+/** Tests for {@link HdmiCecLocalDevice} class. */
public class HdmiCecLocalDeviceTest {
-
private static int SendCecCommandFactory(int srcAddress, int dstAddress, byte[] body) {
- switch(body[0] & 0xFF) {
- /** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */
+ switch (body[0] & 0xFF) {
+ /** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */
case MESSAGE_REPORT_PHYSICAL_ADDRESS:
case MESSAGE_DEVICE_VENDOR_ID:
- return srcAddress == mSrcAddr &&
- dstAddress == mDesAddr &&
- Arrays.equals(Arrays.copyOfRange(body, 1, body.length), param)? 0 : 1;
+ return srcAddress == mSrcAddr
+ && dstAddress == mDesAddr
+ && Arrays.equals(Arrays.copyOfRange(body, 1, body.length), param)
+ ? 0
+ : 1;
default:
return 1;
}
@@ -63,15 +57,12 @@
private class MyHdmiCecLocalDevice extends HdmiCecLocalDevice {
-
protected MyHdmiCecLocalDevice(HdmiControlService service, int deviceType) {
super(service, deviceType);
}
@Override
- protected void onAddressAllocated(int logicalAddress, int reason) {
-
- }
+ protected void onAddressAllocated(int logicalAddress, int reason) {}
@Override
protected int getPreferredAddress() {
@@ -79,9 +70,7 @@
}
@Override
- protected void setPreferredAddress(int addr) {
-
- }
+ protected void setPreferredAddress(int addr) {}
}
private MyHdmiCecLocalDevice mHdmiLocalDevice;
@@ -100,42 +89,48 @@
@Before
public void SetUp() {
- mHdmiControlService = new HdmiControlService(null) {
- @Override
- boolean isControlEnabled() {
- return isControlEnabled;
- }
+ mHdmiControlService =
+ new HdmiControlService(null) {
+ @Override
+ boolean isControlEnabled() {
+ return isControlEnabled;
+ }
- @Override
- boolean isPowerOnOrTransient() {
- return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
- || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
- }
+ @Override
+ boolean isPowerOnOrTransient() {
+ return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
+ || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+ }
- @Override
- void standby() {
- mStandbyMessageReceived = true;
- }
- };
+ @Override
+ void standby() {
+ mStandbyMessageReceived = true;
+ }
+ };
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, new FakeNativeWrapper());
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, new FakeNativeWrapper());
mHdmiControlService.setCecController(mHdmiCecController);
- mHdmiLocalDevice = new MyHdmiCecLocalDevice(
- mHdmiControlService, DEVICE_TV);
- mMessageValidator = new HdmiCecMessageValidator(mHdmiControlService){
- @Override
- int isValid(HdmiCecMessage message) {
- return HdmiCecMessageValidator.OK;
- }
- };
+ mHdmiLocalDevice = new MyHdmiCecLocalDevice(mHdmiControlService, DEVICE_TV);
+ mMessageValidator =
+ new HdmiCecMessageValidator(mHdmiControlService) {
+ @Override
+ int isValid(HdmiCecMessage message) {
+ return HdmiCecMessageValidator.OK;
+ }
+ };
mHdmiControlService.setMessageValidator(mMessageValidator);
}
@Test
public void dispatchMessage_desNotValid() {
- HdmiCecMessage msg = new HdmiCecMessage(
- ADDR_TV, ADDR_TV, Constants.MESSAGE_CEC_VERSION, HdmiCecMessage.EMPTY_PARAM);
+ HdmiCecMessage msg =
+ new HdmiCecMessage(
+ ADDR_TV,
+ ADDR_TV,
+ Constants.MESSAGE_CEC_VERSION,
+ HdmiCecMessage.EMPTY_PARAM);
boolean handleResult = mHdmiLocalDevice.dispatchMessage(msg);
assertFalse(handleResult);
}
@@ -144,18 +139,19 @@
public void handleGivePhysicalAddress_success() {
mSrcAddr = ADDR_UNREGISTERED;
mDesAddr = ADDR_BROADCAST;
- param = new byte[] {
- (byte) ((mPhysicalAddr >> 8) & 0xFF),
- (byte) (mPhysicalAddr & 0xFF),
- (byte) (DEVICE_TV & 0xFF)
- };
+ param =
+ new byte[] {
+ (byte) ((mPhysicalAddr >> 8) & 0xFF),
+ (byte) (mPhysicalAddr & 0xFF),
+ (byte) (DEVICE_TV & 0xFF)
+ };
callbackResult = -1;
- boolean handleResult = mHdmiLocalDevice.handleGivePhysicalAddress(
- (int finalResult) -> callbackResult = finalResult);
+ boolean handleResult =
+ mHdmiLocalDevice.handleGivePhysicalAddress(
+ (int finalResult) -> callbackResult = finalResult);
mTestLooper.dispatchAll();
/**
- * Test if CecMessage is sent successfully
- * SendMessageResult#SUCCESS is defined in HAL as 0
+ * Test if CecMessage is sent successfully SendMessageResult#SUCCESS is defined in HAL as 0
*/
assertEquals(0, callbackResult);
assertTrue(handleResult);
@@ -166,14 +162,10 @@
mSrcAddr = ADDR_UNREGISTERED;
mDesAddr = ADDR_BROADCAST;
/** nativeGetVendorId returns 0 */
- param = new byte[] {
- (byte) ((0 >> 8) & 0xFF),
- (byte) (0 & 0xFF),
- (byte) (0 & 0xFF)
- };
+ param = new byte[] {(byte) ((0 >> 8) & 0xFF), (byte) (0 & 0xFF), (byte) (0 & 0xFF)};
callbackResult = -1;
mHdmiLocalDevice.handleGiveDeviceVendorId(
- (int finalResult) -> callbackResult = finalResult);
+ (int finalResult) -> callbackResult = finalResult);
mTestLooper.dispatchAll();
assertEquals(0, callbackResult);
}
@@ -184,7 +176,7 @@
isControlEnabled = true;
assertFalse(mStandbyMessageReceived);
mHdmiLocalDevice.handleStandby(
- HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM));
+ HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM));
assertTrue(mStandbyMessageReceived);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 50c6880..1ca48d4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -15,8 +15,9 @@
*/
package com.android.server.hdmi;
-import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.google.common.truth.Truth.assertThat;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -34,13 +35,34 @@
public void buildReportPhysicalAddressCommand() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- ADDR_PLAYBACK_1, 01234, HdmiDeviceInfo.DEVICE_PLAYBACK);
- assertThat(message)
- .isEqualTo(
- new HdmiCecMessage(
- ADDR_PLAYBACK_1,
- ADDR_BROADCAST,
- Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
- new byte[] {012, 034}));
+ ADDR_PLAYBACK_1, 0x1234, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ assertThat(message).isEqualTo(buildMessage("4f:84:12:34:04"));
+ }
+
+ @Test
+ public void buildRequestShortAudioDescriptor() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM,
+ new int[] {Constants.AUDIO_CODEC_AAC, Constants.AUDIO_CODEC_LPCM});
+ assertThat(message).isEqualTo(buildMessage("05:A4:06:01"));
+ }
+
+ /**
+ * Build a CEC message from a hex byte string with bytes separated by {@code :}.
+ *
+ * <p>This format is used by both cec-client and www.cec-o-matic.com
+ */
+ private static HdmiCecMessage buildMessage(String message) {
+ String[] parts = message.split(":");
+ int src = Integer.parseInt(parts[0].substring(0, 1), 16);
+ int dest = Integer.parseInt(parts[0].substring(1, 2), 16);
+ int opcode = Integer.parseInt(parts[1], 16);
+ byte[] params = new byte[parts.length - 2];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = Byte.parseByte(parts[i + 2], 16);
+ }
+ return new HdmiCecMessage(src, dest, opcode, params);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 90ad349..7de637b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -30,9 +30,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link HdmiControlService} class.
- */
+/** Tests for {@link HdmiControlService} class. */
@SmallTest
@RunWith(JUnit4.class)
public class HdmiControlServiceTest {
@@ -48,9 +46,7 @@
}
@Override
- protected void onAddressAllocated(int logicalAddress, int reason) {
-
- }
+ protected void onAddressAllocated(int logicalAddress, int reason) {}
@Override
protected int getPreferredAddress() {
@@ -58,9 +54,7 @@
}
@Override
- protected void setPreferredAddress(int addr) {
-
- }
+ protected void setPreferredAddress(int addr) {}
@Override
protected boolean canGoToStandby() {
@@ -68,8 +62,8 @@
}
@Override
- protected void disableDevice(boolean initiatedByCec,
- final PendingActionClearedCallback originalCallback) {
+ protected void disableDevice(
+ boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
mIsDisabled = true;
originalCallback.onCleared(this);
}
@@ -105,26 +99,26 @@
@Before
public void SetUp() {
- mHdmiControlService = new HdmiControlService(null) {
- @Override
- boolean isStandbyMessageReceived() {
- return mStandbyMessageReceived;
- }
- };
+ mHdmiControlService =
+ new HdmiControlService(null) {
+ @Override
+ boolean isStandbyMessageReceived() {
+ return mStandbyMessageReceived;
+ }
+ };
mMyLooper = mTestLooper.getLooper();
- mMyAudioSystemDevice = new HdmiCecLocalDeviceMyDevice(
- mHdmiControlService, DEVICE_AUDIO_SYSTEM);
- mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(
- mHdmiControlService, DEVICE_PLAYBACK);
+ mMyAudioSystemDevice =
+ new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_AUDIO_SYSTEM);
+ mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_PLAYBACK);
mMyAudioSystemDevice.init();
mMyPlaybackDevice.init();
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper);
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
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..e6ff143 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -26,15 +26,12 @@
import android.os.Looper;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link SystemAudioInitiationActionFromAvr}
- */
+/** Tests for {@link SystemAudioInitiationActionFromAvr} */
@SmallTest
@RunWith(JUnit4.class)
public class SystemAudioInitiationActionFromAvrTest {
@@ -54,83 +51,86 @@
@Before
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
- HdmiControlService hdmiControlService = new HdmiControlService(null) {
-
- @Override
- void sendCecCommand(HdmiCecMessage command,
- @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
- mMsgRequestActiveSourceCount++;
- if (mTryCountBeforeSucceed >= mMsgRequestActiveSourceCount
- && callback != null) {
- callback.onSendCompleted(SendMessageResult.NACK);
- break;
- }
- if (mShouldDispatchActiveSource) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildActiveSource(
- Constants.ADDR_TV, 1002));
- }
- break;
- case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
- mMsgSetSystemAudioModeCount++;
- if (mTryCountBeforeSucceed >= mMsgSetSystemAudioModeCount
- && callback != null) {
- callback.onSendCompleted(SendMessageResult.NACK);
- }
- break;
- default:
- throw new IllegalArgumentException("Unexpected message");
- }
- }
-
- @Override
- AudioManager getAudioManager() {
- return new AudioManager() {
+ HdmiControlService hdmiControlService =
+ new HdmiControlService(null) {
@Override
- public int setHdmiSystemAudioSupported(boolean on) {
- return 0;
+ void sendCecCommand(
+ HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
+ mMsgRequestActiveSourceCount++;
+ if (mTryCountBeforeSucceed >= mMsgRequestActiveSourceCount
+ && callback != null) {
+ callback.onSendCompleted(SendMessageResult.NACK);
+ break;
+ }
+ if (mShouldDispatchActiveSource) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildActiveSource(
+ Constants.ADDR_TV, 1002));
+ }
+ break;
+ case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ mMsgSetSystemAudioModeCount++;
+ if (mTryCountBeforeSucceed >= mMsgSetSystemAudioModeCount
+ && callback != null) {
+ callback.onSendCompleted(SendMessageResult.NACK);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
+ }
}
@Override
- public int getStreamVolume(int streamType) {
- return 0;
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+
+ @Override
+ public int setHdmiSystemAudioSupported(boolean on) {
+ return 0;
+ }
+
+ @Override
+ public int getStreamVolume(int streamType) {
+ return 0;
+ }
+
+ @Override
+ public boolean isStreamMute(int streamType) {
+ return false;
+ }
+
+ @Override
+ public int getStreamMaxVolume(int streamType) {
+ return 100;
+ }
+
+ @Override
+ public void adjustStreamVolume(
+ int streamType, int direction, int flags) {}
+ };
}
@Override
- public boolean isStreamMute(int streamType) {
+ boolean isPowerStandby() {
return false;
}
@Override
- public int getStreamMaxVolume(int streamType) {
- return 100;
+ boolean isAddressAllocated() {
+ return true;
}
@Override
- public void adjustStreamVolume(int streamType, int direction, int flags) {
+ void wakeUp() {}
+ @Override
+ int getPhysicalAddress() {
+ return 0;
}
};
- }
-
- @Override
- boolean isPowerStandby() {
- return false;
- }
-
- @Override
- boolean isAddressAllocated() {
- return true;
- }
-
- @Override
- void wakeUp() {
-
- }
- };
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
@@ -205,7 +205,6 @@
assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress).isEqualTo(1002);
-
}
@Test
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/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 02d5869..73adf25 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -93,11 +94,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PreferencesHelperTest extends UiServiceTestCase {
- private static final String PKG = "com.android.server.notification";
- private static final int UID = 0;
+ private static final int UID_N_MR1 = 0;
private static final UserHandle USER = UserHandle.of(0);
- private static final String UPDATED_PKG = "updatedPkg";
- private static final int UID2 = 1111;
+ private static final int UID_O = 1111;
private static final String SYSTEM_PKG = "android";
private static final int SYSTEM_UID= 1000;
private static final UserHandle USER2 = UserHandle.of(10);
@@ -130,16 +129,16 @@
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
final ApplicationInfo upgrade = new ApplicationInfo();
upgrade.targetSdkVersion = Build.VERSION_CODES.O;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
- when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
- when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
- when(mPm.getPackageUidAsUser(eq(UPDATED_PKG), anyInt())).thenReturn(UID2);
+ when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
+ when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
PackageInfo info = mock(PackageInfo.class);
info.signatures = new Signature[] {mock(Signature.class)};
when(mPm.getPackageInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(info);
- when(mPm.getPackageInfoAsUser(eq(PKG), anyInt(), anyInt()))
+ when(mPm.getPackageInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt()))
.thenReturn(mock(PackageInfo.class));
when(mContext.getResources()).thenReturn(
InstrumentationRegistry.getContext().getResources());
@@ -151,7 +150,7 @@
TestableContentResolver contentResolver = getContext().getContentResolver();
contentResolver.setFallbackToExisting(false);
Secure.putIntForUser(contentResolver,
- Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
+ Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID_N_MR1));
ContentProvider testContentProvider = mock(ContentProvider.class);
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
@@ -270,28 +269,29 @@
channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
channel2.setLightColor(Color.BLUE);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
- mHelper.setShowBadge(PKG, UID, true);
- mHelper.setAppImportanceLocked(PKG, UID);
+ mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+ mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, channel1.getId(),
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, channel1.getId(),
channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
loadStreamXml(baos, false);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
+ assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
- mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -332,36 +332,36 @@
NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
channel3.enableVibration(true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
- mHelper.setShowBadge(PKG, UID, true);
+ mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
- mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE);
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG},
- new int[]{UID, UID2});
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1, PKG_O},
+ new int[]{UID_N_MR1, UID_O});
- mHelper.setShowBadge(UPDATED_PKG, UID2, true);
+ mHelper.setShowBadge(PKG_O, UID_O, true);
loadStreamXml(baos, true);
- assertEquals(IMPORTANCE_NONE, mHelper.getImportance(UPDATED_PKG, UID2));
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+ assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O));
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
- mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
compareChannels(channel3,
- mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -388,9 +388,9 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
// Testing that in restore we are given the canonical version
loadStreamXml(baos, true);
@@ -414,13 +414,13 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
loadStreamXml(baos, true);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(localUri, actualChannel.getSound());
}
@@ -435,13 +435,13 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
loadStreamXml(baos, true);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
}
@@ -456,7 +456,7 @@
when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
- + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
+ + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
+ "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+ "sound=\"" + SOUND_URI + "\" "
+ "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
@@ -467,7 +467,7 @@
loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
- NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
}
@@ -476,13 +476,13 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(null, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
loadStreamXml(baos, true);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(null, actualChannel.getSound());
}
@@ -498,19 +498,20 @@
new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
channel3.setGroup(ncg.getId());
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
- mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId());
+ mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg.getId());
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
@@ -518,21 +519,21 @@
parser.nextTag();
mHelper.readXml(parser, true);
- assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
- assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
- assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID));
- //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+ assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
+ assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG_N_MR1, UID_N_MR1));
+ //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG_N_MR1, UID_N_MR1));
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
}
@Test
public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID);
loadStreamXml(baos, false);
- final NotificationChannel updated = mHelper.getNotificationChannel(PKG, UID,
+ final NotificationChannel updated = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
assertFalse(updated.canBypassDnd());
@@ -542,28 +543,29 @@
@Test
public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
- final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+ UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false);
defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID);
loadStreamXml(baos, false);
assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
}
@Test
public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
final String preupgradeXml = "<ranking version=\"1\">\n"
- + "<package name=\"" + PKG
+ + "<package name=\"" + PKG_N_MR1
+ "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH
+ "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
- + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID + "\" />\n"
- + "<package name=\"" + UPDATED_PKG + "\" uid=\"" + UID2 + "\" visibility=\""
+ + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID_N_MR1 + "\" />\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" visibility=\""
+ Notification.VISIBILITY_PRIVATE + "\" />\n"
+ "</ranking>";
XmlPullParser parser = Xml.newPullParser();
@@ -573,7 +575,7 @@
mHelper.readXml(parser, false);
final NotificationChannel updated1 =
- mHelper.getNotificationChannel(PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
assertTrue(updated1.canBypassDnd());
assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
@@ -583,71 +585,71 @@
updated1.getUserLockedFields());
// No Default Channel created for updated packages
- assertEquals(null, mHelper.getNotificationChannel(UPDATED_PKG, UID2,
+ assertEquals(null, mHelper.getNotificationChannel(PKG_O, UID_O,
NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testChannelXml_upgradeDeletesDefaultChannel() throws Exception {
final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertTrue(defaultChannel != null);
ByteArrayOutputStream baos =
- writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID);
+ writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, NotificationChannel.DEFAULT_CHANNEL_ID);
// Load package at higher sdk.
final ApplicationInfo upgraded = new ApplicationInfo();
upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded);
loadStreamXml(baos, false);
// Default Channel should be gone.
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
+ assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
// Load package at higher sdk.
final ApplicationInfo upgraded = new ApplicationInfo();
upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded);
loadStreamXml(baos, false);
// Default Channel should be gone.
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
+ assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testLoadingOldChannelsDoesNotDeleteNewlyCreatedChannels() throws Exception {
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
loadStreamXml(baos, false);
// Should still have the newly created channel that wasn't in the xml.
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "bananas", false) != null);
+ assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "bananas", false) != null);
}
@Test
public void testCreateChannel_blocked() throws Exception {
- mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
+ mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE);
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
}
@Test
public void testCreateChannel_badImportance() throws Exception {
try {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
true, false);
fail("Was allowed to create a channel with invalid importance");
@@ -655,7 +657,7 @@
// yay
}
try {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
true, false);
fail("Was allowed to create a channel with invalid importance");
@@ -663,16 +665,16 @@
// yay
}
try {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
}
@@ -687,7 +689,7 @@
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.createNotificationChannel(PKG, UID, channel, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, false, false);
// same id, try to update all fields
final NotificationChannel channel2 =
@@ -697,70 +699,109 @@
channel2.setBypassDnd(false);
channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
- mHelper.updateNotificationChannel(PKG, UID, channel2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true);
// all fields should be changed
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false));
verify(mHandler, times(1)).requestSort();
}
@Test
public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
- mHelper.setImportance(PKG, UID, IMPORTANCE_UNSPECIFIED);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_UNSPECIFIED);
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
- assertFalse(mHelper.getIsAppImportanceLocked(PKG, UID));
+ mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
NotificationChannel defaultChannel = mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
defaultChannel.setShowBadge(false);
defaultChannel.setImportance(IMPORTANCE_NONE);
defaultChannel.setBypassDnd(true);
defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.setAppImportanceLocked(PKG, UID);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
+ mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
// ensure app level fields are changed
- assertFalse(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG, UID));
- assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG, UID));
- assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG, UID));
- assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
+ assertFalse(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG_N_MR1,
+ UID_N_MR1));
+ assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
+ assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
}
@Test
public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, false, false);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, false, false);
+ assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
+ mHelper.getPackageVisibility(PKG_O, UID_O));
channel.setShowBadge(false);
channel.setImportance(IMPORTANCE_NONE);
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ mHelper.updateNotificationChannel(PKG_O, UID_O, channel, true);
// ensure app level fields are not changed
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
+ mHelper.getPackageVisibility(PKG_O, UID_O));
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O,
+ UID_O));
+ assertFalse(mHelper.getIsAppImportanceLocked(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testUpdate_preUpgrade_noUpdateAppFieldsWithMultipleChannels() throws Exception {
+ final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, false, false);
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+ mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+
+ channel.setShowBadge(false);
+ channel.setImportance(IMPORTANCE_NONE);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
+
+ NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+
+ defaultChannel.setShowBadge(false);
+ defaultChannel.setImportance(IMPORTANCE_NONE);
+ defaultChannel.setBypassDnd(true);
+ defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+ // ensure app level fields are not changed
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+ mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+ UID_N_MR1));
}
@Test
public void testGetNotificationChannel_ReturnsNullForUnknownChannel() throws Exception {
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID, "garbage", false));
+ assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "garbage", false));
}
@Test
@@ -778,10 +819,10 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
NotificationChannel savedChannel =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(channel.getName(), savedChannel.getName());
assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
@@ -807,10 +848,10 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
NotificationChannel savedChannel =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(channel.getName(), savedChannel.getName());
assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
@@ -833,103 +874,103 @@
@Test
public void testLockFields_soundAndVibration() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setSound(new Uri.Builder().scheme("test").build(),
new AudioAttributes.Builder().build());
update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_SOUND,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
NotificationChannel update2 = getChannel();
update2.enableVibration(true);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_SOUND
| NotificationChannel.USER_LOCKED_VIBRATION,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
}
@Test
public void testLockFields_vibrationAndLights() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setVibrationPattern(new long[]{7945, 46 ,246});
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.enableLights(true);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
| NotificationChannel.USER_LOCKED_LIGHTS,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
}
@Test
public void testLockFields_lightsAndImportance() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setLightColor(Color.GREEN);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.setImportance(IMPORTANCE_DEFAULT);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
| NotificationChannel.USER_LOCKED_IMPORTANCE,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
}
@Test
public void testLockFields_visibilityAndDndAndBadge() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
assertEquals(0,
- mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
.getUserLockedFields());
final NotificationChannel update1 = getChannel();
update1.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_VISIBILITY,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
final NotificationChannel update3 = getChannel();
update3.setShowBadge(false);
- mHelper.updateNotificationChannel(PKG, UID, update3, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update3, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_VISIBILITY
| NotificationChannel.USER_LOCKED_SHOW_BADGE,
- mHelper.getNotificationChannel(PKG, UID, update3.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update3.getId(), false)
.getUserLockedFields());
}
@Test
public void testDeleteNonExistentChannel() throws Exception {
- mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist");
+ mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, "does not exist");
}
@Test
@@ -942,16 +983,16 @@
channel.enableVibration(true);
channel.setVibrationPattern(new long[]{100, 67, 145, 156});
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
// Does not return deleted channel
NotificationChannel response =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertNull(response);
// Returns deleted channel
- response = mHelper.getNotificationChannel(PKG, UID, channel.getId(), true);
+ response = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true);
compareChannels(channel, response);
assertTrue(response.isDeleted());
}
@@ -971,14 +1012,14 @@
NotificationChannel channel2 =
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
channelMap.put(channel2.getId(), channel2);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
// Returns only non-deleted channels
List<NotificationChannel> channels =
- mHelper.getNotificationChannels(PKG, UID, false).getList();
+ mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList();
assertEquals(2, channels.size()); // Default channel + non-deleted channel
for (NotificationChannel nc : channels) {
if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
@@ -987,7 +1028,7 @@
}
// Returns deleted channels too
- channels = mHelper.getNotificationChannels(PKG, UID, true).getList();
+ channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList();
assertEquals(3, channels.size()); // Includes default channel
for (NotificationChannel nc : channels) {
if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
@@ -1004,15 +1045,15 @@
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
- mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId());
- assertEquals(2, mHelper.getDeletedChannelCount(PKG, UID));
- assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID2));
+ assertEquals(2, mHelper.getDeletedChannelCount(PKG_N_MR1, UID_N_MR1));
+ assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID_O));
}
@Test
@@ -1023,14 +1064,14 @@
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_NONE);
NotificationChannel channel3 =
new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_NONE);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId());
- assertEquals(1, mHelper.getBlockedChannelCount(PKG, UID));
- assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID2));
+ assertEquals(1, mHelper.getBlockedChannelCount(PKG_N_MR1, UID_N_MR1));
+ assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID_O));
}
@Test
@@ -1039,7 +1080,7 @@
// expected result: areChannelsBypassingDnd = false
// setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1048,18 +1089,18 @@
// expected result: areChannelsBypassingDnd = true
NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel2.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, true);
assertTrue(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
// delete channels
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
- mHelper.deleteNotificationChannel(PKG, UID, channel2.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId());
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1071,7 +1112,7 @@
// expected result: areChannelsBypassingDnd = false
// setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1079,7 +1120,7 @@
// update channel so it CAN bypass dnd:
// expected result: areChannelsBypassingDnd = true
channel.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
assertTrue(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1087,7 +1128,7 @@
// update channel so it can't bypass dnd:
// expected result: areChannelsBypassingDnd = false
channel.setBypassDnd(false);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1124,33 +1165,33 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, false);
// No long deleted, using old settings
compareChannels(channel,
- mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel.getId(), false));
}
@Test
public void testOnlyHasDefaultChannel() throws Exception {
- assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
- assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
- assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
}
@Test
public void testCreateChannel_defaultChannelId() throws Exception {
try {
- mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
fail("Allowed to create default channel");
} catch (IllegalArgumentException e) {
@@ -1165,17 +1206,17 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, false);
// Old settings not overridden
compareChannels(channel,
- mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel.getId(), false));
}
@Test
@@ -1184,9 +1225,9 @@
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
channel.setSound(sound, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
assertEquals(sound, mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false).getSound());
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false).getSound());
}
@Test
@@ -1196,13 +1237,13 @@
NotificationChannel channel2 =
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
- mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
+ mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1);
// Only default channel remains
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+ assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
}
@Test
@@ -1218,28 +1259,28 @@
new NotificationChannel("deleted", "belongs to deleted", IMPORTANCE_DEFAULT);
groupedAndDeleted.setGroup("totally");
- mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
- mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
- mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
- mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
- mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted, true, false);
- mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
+ mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted.getId());
- assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG, UID));
- assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG, UID));
+ assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG_N_MR1, UID_N_MR1));
+ assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1));
- assertNull(mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), false));
+ assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), false));
compareChannels(groupedAndDeleted,
- mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), true));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), true));
compareChannels(groupedButNotDeleted,
- mHelper.getNotificationChannel(PKG, UID, groupedButNotDeleted.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted.getId(), false));
compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
- PKG, UID, nonGroupedNonDeletedChannel.getId(), false));
+ PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel.getId(), false));
// notDeleted
- assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
+ assertEquals(1, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).size());
verify(mHandler, never()).requestSort();
}
@@ -1253,11 +1294,11 @@
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
// create records with the default channel for all user 0 and user 1 uids
- mHelper.getImportance(PKG, user0Uids[i]);
- mHelper.getImportance(PKG, user1Uids[i]);
+ mHelper.getImportance(PKG_N_MR1, user0Uids[i]);
+ mHelper.getImportance(PKG_N_MR1, user1Uids[i]);
}
mHelper.onUserRemoved(1);
@@ -1265,12 +1306,12 @@
// user 0 records remain
for (int i = 0; i < user0Uids.length; i++) {
assertEquals(1,
- mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
+ mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false).getList().size());
}
// user 1 records are gone
for (int i = 0; i < user1Uids.length; i++) {
assertEquals(0,
- mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
+ mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false).getList().size());
}
}
@@ -1279,71 +1320,77 @@
// Deleted
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
- assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+ assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
// Not deleted
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
+ mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
+ assertEquals(2, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
}
@Test
public void testOnPackageChanged_packageRemoval_importance() throws Exception {
- mHelper.setImportance(PKG, UID, NotificationManager.IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_N_MR1, UID_N_MR1, NotificationManager.IMPORTANCE_HIGH);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+ UID_N_MR1));
}
@Test
public void testOnPackageChanged_packageRemoval_groups() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
assertEquals(0,
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList().size());
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList().size());
}
@Test
public void testOnPackageChange_downgradeTargetSdk() throws Exception {
// create channel as api 26
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
// install new app version targeting 25
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(legacy);
mHelper.onPackagesChanged(
- false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
+ false, UserHandle.USER_SYSTEM, new String[]{PKG_O}, new int[]{UID_O});
// make sure the default channel was readded
- //assertEquals(2, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size());
+ //assertEquals(2, mHelper.getNotificationChannels(PKG_O, UID_O, false).getList().size());
assertNotNull(mHelper.getNotificationChannel(
- UPDATED_PKG, UID2, NotificationChannel.DEFAULT_CHANNEL_ID, false));
+ PKG_O, UID_O, NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testRecordDefaults() throws Exception {
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
- assertEquals(true, mHelper.canShowBadge(PKG, UID));
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+ UID_N_MR1));
+ assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
}
@Test
public void testCreateGroup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).iterator().next());
verify(mHandler, never()).requestSort();
}
@@ -1353,7 +1400,7 @@
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup("garbage");
try {
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
fail("Created a channel with a bad group");
} catch (IllegalArgumentException e) {
}
@@ -1362,45 +1409,45 @@
@Test
public void testCannotCreateChannel_goodGroup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
assertEquals(ncg.getId(),
- mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false).getGroup());
}
@Test
public void testGetChannelGroups() throws Exception {
NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
- mHelper.createNotificationChannelGroup(PKG, UID, unused, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, unused, true);
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
NotificationChannel channel1a =
new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1a.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1a, true, false);
NotificationChannel channel2 =
new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
channel2.setGroup(ncg2.getId());
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
NotificationChannel channel3 =
new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
@@ -1426,19 +1473,19 @@
@Test
public void testGetChannelGroups_noSideEffects() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
channel1.setImportance(IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, channel1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true);
List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -1451,14 +1498,14 @@
@Test
public void testCreateChannel_updateName() throws Exception {
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+ NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertEquals("hello", actual.getName());
nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
- actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertEquals("goodbye", actual.getName());
assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
@@ -1468,17 +1515,17 @@
@Test
public void testCreateChannel_addToGroup() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("group", "");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+ NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertNull(actual.getGroup());
nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
nc.setGroup(group.getId());
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
- actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertNotNull(actual.getGroup());
assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
@@ -1500,7 +1547,7 @@
String pkgName = "pkg" + i;
int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
for (int j = 0; j < numChannels; j++) {
- mHelper.createNotificationChannel(pkgName, UID,
+ mHelper.createNotificationChannel(pkgName, UID_N_MR1,
new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
}
expectedChannels.put(pkgName, numChannels);
@@ -1508,7 +1555,7 @@
// delete the first channel of the first package
String pkg = expectedChannels.keyAt(0);
- mHelper.deleteNotificationChannel("pkg" + 0, UID, "0");
+ mHelper.deleteNotificationChannel("pkg" + 0, UID_N_MR1, "0");
// dump should not include deleted channels
int count = expectedChannels.get(pkg);
expectedChannels.put(pkg, count - 1);
@@ -1566,7 +1613,8 @@
@Test
public void testOnLocaleChanged_updatesDefaultChannels() throws Exception {
String newLabel = "bananas!";
- final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+ UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertFalse(newLabel.equals(defaultChannel.getName()));
@@ -1577,56 +1625,56 @@
mHelper.onLocaleChanged(mContext, USER.getIdentifier());
- assertEquals(newLabel, mHelper.getNotificationChannel(PKG, UID,
+ assertEquals(newLabel, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false).getName());
}
@Test
public void testIsGroupBlocked_noGroup() throws Exception {
- assertFalse(mHelper.isGroupBlocked(PKG, UID, null));
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, null));
- assertFalse(mHelper.isGroupBlocked(PKG, UID, "non existent group"));
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, "non existent group"));
}
@Test
public void testIsGroupBlocked_notBlocked() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
- assertFalse(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
}
@Test
public void testIsGroupBlocked_blocked() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
group.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG, UID, group, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, false);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
}
@Test
public void testIsGroup_appCannotResetBlock() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
NotificationChannelGroup group2 = group.clone();
group2.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG, UID, group2, false);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group2, false);
+ assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
NotificationChannelGroup group3 = group.clone();
group3.setBlocked(false);
- mHelper.createNotificationChannelGroup(PKG, UID, group3, true);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true);
+ assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
}
@Test
public void testGetNotificationChannelGroupWithChannels() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("group", "");
NotificationChannelGroup other = new NotificationChannelGroup("something else", "");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
- mHelper.createNotificationChannelGroup(PKG, UID, other, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, other, true);
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
a.setGroup(group.getId());
@@ -1636,20 +1684,20 @@
c.setGroup(group.getId());
NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, a, true, false);
- mHelper.createNotificationChannel(PKG, UID, b, true, false);
- mHelper.createNotificationChannel(PKG, UID, c, true, false);
- mHelper.createNotificationChannel(PKG, UID, d, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, c.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, a, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, b, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, c, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, d, true, false);
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, c.getId());
NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
- PKG, UID, group.getId(), true);
+ PKG_N_MR1, UID_N_MR1, group.getId(), true);
assertEquals(2, retrieved.getChannels().size());
compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
compareChannels(c, findChannel(retrieved.getChannels(), c.getId()));
retrieved = mHelper.getNotificationChannelGroupWithChannels(
- PKG, UID, group.getId(), false);
+ PKG_N_MR1, UID_N_MR1, group.getId(), false);
assertEquals(1, retrieved.getChannels().size());
compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
}
@@ -1670,9 +1718,9 @@
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, test, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, test, true, true);
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "A", false).canBypassDnd());
}
@Test
@@ -1680,9 +1728,9 @@
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, 1000, test, true, false);
- assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+ assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 1000, "A", false).canBypassDnd());
}
@Test
@@ -1701,23 +1749,23 @@
@Test
public void testDndPkgCanBypassDnd_update() throws Exception {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, test, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, test, true, true);
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, update, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true, true);
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "A", false).canBypassDnd());
}
@Test
public void testNormalPkgCannotBypassDnd_update() {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, 1000, test, true, false);
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, update, true, false);
- assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+ mHelper.createNotificationChannel(PKG_N_MR1, 1000, update, true, false);
+ assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 1000, "A", false).canBypassDnd());
}
@Test
@@ -1727,16 +1775,16 @@
@Test
public void testGetBlockedAppCount_noAppsForUserId() {
- mHelper.setEnabled(PKG, 100, false);
+ mHelper.setEnabled(PKG_N_MR1, 100, false);
assertEquals(0, mHelper.getBlockedAppCount(9));
}
@Test
public void testGetBlockedAppCount_appsForUserId() {
- mHelper.setEnabled(PKG, 1020, false);
- mHelper.setEnabled(PKG, 1030, false);
- mHelper.setEnabled(PKG, 1060, false);
- mHelper.setEnabled(PKG, 1000, true);
+ mHelper.setEnabled(PKG_N_MR1, 1020, false);
+ mHelper.setEnabled(PKG_N_MR1, 1030, false);
+ mHelper.setEnabled(PKG_N_MR1, 1060, false);
+ mHelper.setEnabled(PKG_N_MR1, 1000, true);
assertEquals(3, mHelper.getBlockedAppCount(0));
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 2ecda48..2a22600 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -116,7 +116,7 @@
mZenModeHelperSpy.writeXml(serializer, forBackup, version);
serializer.endDocument();
serializer.flush();
- mZenModeHelperSpy.setConfig(new ZenModeConfig(), "writing xml");
+ mZenModeHelperSpy.setConfig(new ZenModeConfig(), null, "writing xml");
return baos;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 1db8967..6d4f5f8 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -120,7 +120,8 @@
.thenReturn(PERMISSION_DENIED);
when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
.thenReturn(PERMISSION_GRANTED);
- mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(),
+ mService.checkSlicePermission(TEST_URI, mContext.getPackageName(),
+ mContext.getPackageName(), Process.myPid(),
Process.myUid(), testPerms);
verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
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/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
new file mode 100644
index 0000000..9c874bf
--- /dev/null
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom;
+
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.ICallRedirectionService;
+import com.android.internal.telecom.ICallRedirectionAdapter;
+
+/**
+ * This service can be implemented to interact between Telecom and its implementor
+ * for making outgoing call with optional redirection/cancellation purposes.
+ *
+ * <p>
+ * Below is an example manifest registration for a {@code CallRedirectionService}.
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourCallRedirectionServiceImplementation"
+ * android:permission="android.permission.BIND_REDIRECTION_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telecom.CallRedirectionService"/>
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ */
+public abstract class CallRedirectionService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
+
+ /**
+ * An adapter to inform Telecom the response from the implementor of the Call
+ * Redirection service
+ */
+ private ICallRedirectionAdapter mCallRedirectionAdapter;
+
+ /**
+ * Telecom calls this method to inform the implemented {@link CallRedirectionService} of
+ * a new outgoing call which is being placed.
+ * @param handle the phone number dialed by the user
+ * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+ */
+ public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount);
+
+ /**
+ * The implemented {@link CallRedirectionService} calls this method to response a request
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes
+ * are required to the outgoing call, and that the call should be placed as-is.
+ */
+ public final void placeCallUnmodified() {
+ try {
+ mCallRedirectionAdapter.placeCallUnmodified();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * The implemented {@link CallRedirectionService} calls this method to response a request
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes
+ * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call.
+ * @param handle the new phone number to dial
+ * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
+ * If {@code null}, no change will be made to the
+ * {@link PhoneAccountHandle} used to place the call.
+ */
+ public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
+ try {
+ mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * The implemented {@link CallRedirectionService} calls this method to response a request
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing
+ * call should be canceled entirely.
+ */
+ public final void cancelCall() {
+ try {
+ mCallRedirectionAdapter.cancelCall();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * A handler message to process the attempt to place call with redirection service from Telecom
+ */
+ private static final int MSG_PLACE_CALL = 1;
+
+ /**
+ * A handler to process the attempt to place call with redirection service from Telecom
+ */
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PLACE_CALL:
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
+ onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ }
+ };
+
+ private final class CallRedirectionBinder extends ICallRedirectionService.Stub {
+
+ /**
+ * Telecom calls this method to inform the CallRedirectionService of a new outgoing call
+ * which is about to be placed.
+ * @param handle the phone number dialed by the user
+ * @param targetPhoneAccount the URI of the number the user dialed
+ */
+ @Override
+ public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
+ PhoneAccountHandle targetPhoneAccount) {
+ Log.v(this, "placeCall");
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = adapter;
+ args.arg2 = handle;
+ args.arg3 = targetPhoneAccount;
+ mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.v(this, "onBind");
+ return new CallRedirectionBinder();
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.v(this, "onUnbind");
+ return false;
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
new file mode 100644
index 0000000..46bf983
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.net.Uri;
+import android.telecom.PhoneAccountHandle;
+
+/**
+ * Internal remote callback interface for call redirection services.
+ *
+ * @see android.telecom.CallRedirectionService
+ *
+ * {@hide}
+ */
+oneway interface ICallRedirectionAdapter {
+ void cancelCall();
+
+ void placeCallUnmodified();
+
+ void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
new file mode 100644
index 0000000..d8d360b
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.net.Uri;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telecom.ICallRedirectionAdapter;
+
+/**
+ * Internal remote interface for a call redirection service.
+ *
+ * @see android.telecom.CallRedirectionService
+ *
+ * @hide
+ */
+oneway interface ICallRedirectionService {
+ void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
+ in PhoneAccountHandle targetPhoneAccount);
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cabf444..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.
@@ -2012,6 +2021,15 @@
public static final String KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME =
"undelivered_sms_message_expiration_time";
+ /**
+ * Specifies a carrier-defined {@link CallRedirectionService} which Telecom will bind
+ * to for outgoing calls. An empty string indicates that no carrier-defined
+ * {@link CallRedirectionService} is specified.
+ * @hide
+ */
+ public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
+ "call_redirection_service_component_name_string";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -2020,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/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 3aab3fc..bffeb17 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -124,6 +125,14 @@
mTimeStamp = timeStamp;
}
+ /** @hide */
+ @NonNull
+ public abstract CellIdentity getCellIdentity();
+
+ /** @hide */
+ @NonNull
+ public abstract CellSignalStrength getCellSignalStrength();
+
/**
* Gets the connection status of this cell.
*
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 6403bc5..8b8d1bb 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -45,6 +45,7 @@
this.mCellSignalStrengthCdma = ci.mCellSignalStrengthCdma.copy();
}
+ @Override
public CellIdentityCdma getCellIdentity() {
return mCellIdentityCdma;
}
@@ -53,6 +54,7 @@
mCellIdentityCdma = cid;
}
+ @Override
public CellSignalStrengthCdma getCellSignalStrength() {
return mCellSignalStrengthCdma;
}
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index a3a9b31..f7af1b2 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -45,6 +45,7 @@
this.mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
}
+ @Override
public CellIdentityGsm getCellIdentity() {
return mCellIdentityGsm;
}
@@ -53,6 +54,7 @@
mCellIdentityGsm = cid;
}
+ @Override
public CellSignalStrengthGsm getCellSignalStrength() {
return mCellSignalStrengthGsm;
}
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index b892e89..97d856e 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -45,6 +45,7 @@
this.mCellSignalStrengthLte = ci.mCellSignalStrengthLte.copy();
}
+ @Override
public CellIdentityLte getCellIdentity() {
if (DBG) log("getCellIdentity: " + mCellIdentityLte);
return mCellIdentityLte;
@@ -55,6 +56,7 @@
mCellIdentityLte = cid;
}
+ @Override
public CellSignalStrengthLte getCellSignalStrength() {
if (DBG) log("getCellSignalStrength: " + mCellSignalStrengthLte);
return mCellSignalStrengthLte;
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index 7084c51..4fb1bce 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -48,6 +48,7 @@
this.mCellSignalStrengthTdscdma = ci.mCellSignalStrengthTdscdma.copy();
}
+ @Override
public CellIdentityTdscdma getCellIdentity() {
return mCellIdentityTdscdma;
}
@@ -56,6 +57,7 @@
mCellIdentityTdscdma = cid;
}
+ @Override
public CellSignalStrengthTdscdma getCellSignalStrength() {
return mCellSignalStrengthTdscdma;
}
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index 005f3d3..4f9dcb1 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -47,6 +47,7 @@
this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
}
+ @Override
public CellIdentityWcdma getCellIdentity() {
return mCellIdentityWcdma;
}
@@ -55,6 +56,7 @@
mCellIdentityWcdma = cid;
}
+ @Override
public CellSignalStrengthWcdma getCellSignalStrength() {
return mCellSignalStrengthWcdma;
}
diff --git a/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
similarity index 70%
rename from telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
rename to telephony/java/com/android/internal/telephony/ISmsImplBase.java
index cc1d105..1cdf44d 100644
--- a/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -1,4 +1,5 @@
-/* Copyright (C) 2018 The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,17 +12,19 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
*/
package com.android.internal.telephony;
import android.app.PendingIntent;
import android.net.Uri;
-import java.lang.UnsupportedOperationException;
+
import java.util.List;
-public class ISmsBaseImpl extends ISms.Stub {
+/**
+ * Base class for ISms that facilitates forward compatibility with new features.
+ */
+public class ISmsImplBase extends ISms.Stub {
@Override
public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPkg) {
@@ -29,45 +32,42 @@
}
@Override
- public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPkg,
- int messageIndex, int newStatus, byte[] pdu) throws UnsupportedOperationException {
+ public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPkg, int messageIndex,
+ int newStatus, byte[] pdu) {
throw new UnsupportedOperationException();
}
@Override
public boolean copyMessageToIccEfForSubscriber(int subId, String callingPkg, int status,
- byte[] pdu, byte[] smsc) throws UnsupportedOperationException {
+ byte[] pdu, byte[] smsc) {
throw new UnsupportedOperationException();
}
@Override
public void sendDataForSubscriber(int subId, String callingPkg, String destAddr,
String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
- PendingIntent deliveryIntent) throws UnsupportedOperationException {
+ PendingIntent deliveryIntent) {
throw new UnsupportedOperationException();
}
@Override
public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg,
- String destAddr, String scAddr, int destPort, byte[] data,
- PendingIntent sentIntent, PendingIntent deliveryIntent)
- throws UnsupportedOperationException {
+ String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) {
throw new UnsupportedOperationException();
}
@Override
public void sendTextForSubscriber(int subId, String callingPkg, String destAddr,
String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp)
- throws UnsupportedOperationException {
+ PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) {
throw new UnsupportedOperationException();
}
@Override
public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPkg,
String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, boolean persistMessage)
- throws UnsupportedOperationException {
+ PendingIntent deliveryIntent, boolean persistMessage) {
throw new UnsupportedOperationException();
}
@@ -75,15 +75,13 @@
public void sendTextForSubscriberWithOptions(int subId, String callingPkg, String destAddr,
String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp,
- int priority, boolean expectMore, int validityPeriod)
- throws UnsupportedOperationException {
+ int priority, boolean expectMore, int validityPeriod) {
throw new UnsupportedOperationException();
}
@Override
public void injectSmsPduForSubscriber(
- int subId, byte[] pdu, String format, PendingIntent receivedIntent)
- throws UnsupportedOperationException {
+ int subId, byte[] pdu, String format, PendingIntent receivedIntent) {
throw new UnsupportedOperationException();
}
@@ -91,8 +89,7 @@
public void sendMultipartTextForSubscriber(int subId, String callingPkg,
String destinationAddress, String scAddress,
List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
- throws UnsupportedOperationException {
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
throw new UnsupportedOperationException();
}
@@ -101,99 +98,94 @@
String destinationAddress, String scAddress,
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
- int priority, boolean expectMore, int validityPeriod)
- throws UnsupportedOperationException {
+ int priority, boolean expectMore, int validityPeriod) {
throw new UnsupportedOperationException();
}
@Override
- public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws UnsupportedOperationException {
+ public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType) {
throw new UnsupportedOperationException();
}
@Override
- public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws UnsupportedOperationException {
+ public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier,
+ int ranType) {
throw new UnsupportedOperationException();
}
@Override
public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws UnsupportedOperationException {
+ int endMessageId, int ranType) {
throw new UnsupportedOperationException();
}
@Override
public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws UnsupportedOperationException {
+ int endMessageId, int ranType) {
throw new UnsupportedOperationException();
}
@Override
- public int getPremiumSmsPermission(String packageName) throws UnsupportedOperationException {
+ public int getPremiumSmsPermission(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public int getPremiumSmsPermissionForSubscriber(int subId, String packageName)
- throws UnsupportedOperationException {
+ public int getPremiumSmsPermissionForSubscriber(int subId, String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void setPremiumSmsPermission(String packageName, int permission) throws UnsupportedOperationException {
+ public void setPremiumSmsPermission(String packageName, int permission) {
throw new UnsupportedOperationException();
}
@Override
public void setPremiumSmsPermissionForSubscriber(int subId, String packageName,
- int permission) throws UnsupportedOperationException {
+ int permission) {
throw new UnsupportedOperationException();
}
@Override
- public boolean isImsSmsSupportedForSubscriber(int subId) throws UnsupportedOperationException {
+ public boolean isImsSmsSupportedForSubscriber(int subId) {
throw new UnsupportedOperationException();
}
@Override
- public boolean isSmsSimPickActivityNeeded(int subId) throws UnsupportedOperationException {
+ public boolean isSmsSimPickActivityNeeded(int subId) {
throw new UnsupportedOperationException();
}
@Override
- public int getPreferredSmsSubscription() throws UnsupportedOperationException {
+ public int getPreferredSmsSubscription() {
throw new UnsupportedOperationException();
}
@Override
- public String getImsSmsFormatForSubscriber(int subId) throws UnsupportedOperationException {
+ public String getImsSmsFormatForSubscriber(int subId) {
throw new UnsupportedOperationException();
}
@Override
- public boolean isSMSPromptEnabled() throws UnsupportedOperationException {
+ public boolean isSMSPromptEnabled() {
throw new UnsupportedOperationException();
}
@Override
public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
- PendingIntent sentIntent, PendingIntent deliveryIntent)
- throws UnsupportedOperationException {
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
throw new UnsupportedOperationException();
}
@Override
public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
- String scAddress, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents) throws UnsupportedOperationException {
+ String scAddress, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) {
throw new UnsupportedOperationException();
}
@Override
- public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent)
- throws UnsupportedOperationException {
+ public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) {
throw new UnsupportedOperationException();
}
}
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index c2aca6b..89734e3 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -159,7 +159,13 @@
/** @hide */
@Override
- public boolean isPermissionReviewModeEnabled() {
+ public boolean arePermissionsIndividuallyControlled() {
+ return false;
+ }
+
+ /** @hide */
+ @Override
+ public boolean isWirelessConsentModeEnabled() {
return false;
}
diff --git a/tests/ActivityViewTest/Android.mk b/tests/ActivityViewTest/Android.mk
new file mode 100644
index 0000000..9c80764
--- /dev/null
+++ b/tests/ActivityViewTest/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := ActivityViewTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
new file mode 100644
index 0000000..de54cc9
--- /dev/null
+++ b/tests/ActivityViewTest/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.test.activityview">
+ <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+ <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+
+ <uses-sdk android:targetSdkVersion="27"/>
+ <application android:label="ActivityViewTest">
+ <activity android:name=".ActivityViewMainActivity"
+ android:label="AV Main"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".ActivityViewActivity"
+ android:label="AV"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+
+ <activity android:name=".ActivityViewResizeActivity"
+ android:label="AV Resize"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+
+ <activity android:name=".ActivityViewScrollActivity"
+ android:label="AV Scroll"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+
+ <activity android:name=".ActivityViewTestActivity"
+ android:resizeableActivity="true"
+ android:theme="@*android:style/Theme.NoTitleBar"
+ android:exported="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_activity.xml
new file mode 100644
index 0000000..67c01f8
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_activity.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#cfd8dc">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/activity_launch_button"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:text="Launch test activity" />
+
+ <Button
+ android:id="@+id/activity_pick_launch_button"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:text="Launch from picker" />
+
+ </LinearLayout>
+
+ <ActivityView
+ android:id="@+id/activity_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
new file mode 100644
index 0000000..ba2e911
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/activity_view_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Test ActivityView"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/scroll_activity_view_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Test Scroll ActivityView"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/resize_activity_view_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Test Resize ActivityView"
+ android:textAllCaps="false"/>
+
+</LinearLayout>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
new file mode 100644
index 0000000..18d86e3
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#cfd8dc">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/activity_launch_button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:text="Launch" />
+
+ <Button
+ android:id="@+id/activity_resize_button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:text="Resize" />
+ </LinearLayout>
+
+ <SeekBar
+ android:id="@+id/activity_view_seek_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ActivityView
+ android:id="@+id/activity_view"
+ android:layout_width="match_parent"
+ android:layout_height="600dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
new file mode 100644
index 0000000..879c2c20
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/activity_launch_button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:text="Launch" />
+
+ <ScrollView
+ android:id="@+id/activity_view_host_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:color="#cfd8dc">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:layout_gravity="center_horizontal"
+ android:background="#eeeeee" />
+
+ <ActivityView
+ android:id="@+id/activity_view"
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:background="#fce4ec" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:layout_gravity="center_horizontal"
+ android:background="#eeeeee" />
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
new file mode 100644
index 0000000..f7ec562
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#ffe0b2">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:orientation="vertical"
+ android:background="#00000000" >
+ <TextView
+ android:id="@+id/test_activity_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+ <TextView
+ android:id="@+id/test_activity_touch_state"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/test_activity_width_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+
+ <TextView
+ android:id="@+id/test_activity_height_text"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+
+ <View
+ android:id="@+id/touch_intercept_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#00000000"
+ />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
new file mode 100644
index 0000000..1548d6e
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+
+public class ActivityViewActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_activity);
+
+ final ActivityView activityView = findViewById(R.id.activity_view);
+ final Button launchButton = findViewById(R.id.activity_launch_button);
+ launchButton.setOnClickListener(v -> {
+ final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ activityView.startActivity(intent);
+ });
+ final Button pickActivityLaunchButton = findViewById(R.id.activity_pick_launch_button);
+ pickActivityLaunchButton.setOnClickListener(v -> {
+ final Intent intent = Intent.makeMainActivity(null);
+ final Intent chooser = Intent.createChooser(intent,
+ "Pick an app to launch in ActivityView");
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ activityView.startActivity(chooser);
+ }
+ });
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
new file mode 100644
index 0000000..66f0c6a
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activityview;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class ActivityViewMainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_main_activity);
+
+ findViewById(R.id.activity_view_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ActivityViewActivity.class)));
+
+ findViewById(R.id.scroll_activity_view_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ActivityViewScrollActivity.class)));
+
+ findViewById(R.id.resize_activity_view_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ActivityViewResizeActivity.class)));
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
new file mode 100644
index 0000000..8860a77
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+public class ActivityViewResizeActivity extends Activity {
+ private static final int SMALL_SIZE = 600;
+ private static final int LARGE_SIZE = 1200;
+
+ private ActivityView mActivityView;
+
+ private boolean mFlipSize;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_resize_activity);
+
+ mActivityView = findViewById(R.id.activity_view);
+
+ final Button launchButton = findViewById(R.id.activity_launch_button);
+ launchButton.setOnClickListener(v -> {
+ final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ mActivityView.startActivity(intent);
+ });
+ final Button resizeButton = findViewById(R.id.activity_resize_button);
+ if (resizeButton != null) {
+ resizeButton.setOnClickListener(v -> {
+ LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
+ params.height = mFlipSize ? SMALL_SIZE : LARGE_SIZE;
+ mFlipSize = !mFlipSize;
+ mActivityView.setLayoutParams(params);
+ });
+ }
+ final SeekBar seekBar = findViewById(R.id.activity_view_seek_bar);
+ if (seekBar != null) {
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ final LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
+ params.height = SMALL_SIZE + progress * 10;
+ mActivityView.setLayoutParams(params);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ }
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
new file mode 100644
index 0000000..5654366
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+public class ActivityViewScrollActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_scroll_activity);
+
+ final ActivityView activityView = findViewById(R.id.activity_view);
+ final Button launchButton = findViewById(R.id.activity_launch_button);
+ launchButton.setOnClickListener(v -> {
+ final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ activityView.startActivity(intent);
+ });
+ findViewById(R.id.activity_view_host_scroll_view).setOnScrollChangeListener(
+ (View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
+ -> activityView.onLocationChanged());
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
new file mode 100644
index 0000000..0d62786
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activityview;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.TextView;
+
+public class ActivityViewTestActivity extends Activity implements View.OnTouchListener {
+
+ private TextView mTextView;
+ private TextView mWidthTextView;
+ private TextView mHeightTextView;
+ private TextView mTouchStateTextView;
+ private View mTouchInterceptView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_test_activity);
+
+ mTextView = findViewById(R.id.test_activity_title);
+ mWidthTextView = findViewById(R.id.test_activity_width_text);
+ mHeightTextView = findViewById(R.id.test_activity_height_text);
+ mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
+ mTouchInterceptView = findViewById(R.id.touch_intercept_view);
+ mTouchInterceptView.setOnTouchListener(this);
+ ViewTreeObserver viewTreeObserver = mTouchInterceptView.getViewTreeObserver();
+ if (viewTreeObserver.isAlive()) {
+ viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
+ }
+ updateStateText("CREATED");
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ updateStateText("STARTED");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ updateStateText("RESUMED");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ updateStateText("PAUSED");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ updateStateText("STOPPED");
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateDimensionTexts();
+ }
+
+ private void updateStateText(String state) {
+ mTextView.setText(state);
+ }
+
+ private void updateDimensionTexts() {
+ mWidthTextView.setText("" + mTouchInterceptView.getWidth());
+ mHeightTextView.setText("" + mTouchInterceptView.getHeight());
+ }
+
+ private void updateTouchState(MotionEvent event) {
+ switch (event.getAction()) {
+ case ACTION_DOWN:
+ case ACTION_MOVE:
+ mTouchStateTextView.setText("[" + event.getX() + "," + event.getY() + "]");
+ break;
+ case ACTION_UP:
+ case ACTION_CANCEL:
+ mTouchStateTextView.setText("");
+ break;
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ updateTouchState(event);
+ return true;
+ }
+}
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk b/tests/SystemMemoryTest/Android.mk
similarity index 87%
rename from tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
rename to tests/SystemMemoryTest/Android.mk
index 5d7a6f7..09a1618 100644
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
+++ b/tests/SystemMemoryTest/Android.mk
@@ -1,4 +1,3 @@
-#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
+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/aapt2/integration-tests/AutoNamespaceTest/Android.mk b/tests/SystemMemoryTest/device/Android.mk
similarity index 68%
copy from tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
copy to tests/SystemMemoryTest/device/Android.mk
index 5d7a6f7..75408df 100644
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
+++ b/tests/SystemMemoryTest/device/Android.mk
@@ -1,4 +1,3 @@
-#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,7 +11,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_PACKAGE_NAME := SystemMemoryTestDevice
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_COMPATIBILITY_SUITE := general-tests
+include $(BUILD_PACKAGE)
diff --git a/tests/SystemMemoryTest/device/AndroidManifest.xml b/tests/SystemMemoryTest/device/AndroidManifest.xml
new file mode 100644
index 0000000..e5e7844f
--- /dev/null
+++ b/tests/SystemMemoryTest/device/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.sysmem.device">
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <instrumentation
+ android:name="com.android.tests.sysmem.device.Cujs"
+ android:targetPackage="com.android.tests.sysmem.device" />
+
+ <application
+ android:allowBackup="false"
+ android:label="System Memory Test">
+ </application>
+</manifest>
diff --git a/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java b/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java
new file mode 100644
index 0000000..6c0c593
--- /dev/null
+++ b/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.sysmem.device;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Critical user journeys used to exercise the system for test, driven from
+ * the device.
+ */
+public class Cujs extends Instrumentation {
+
+ private static final String TAG = "SystemMemoryTest";
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ start();
+ }
+
+ @Override
+ public void onStart() {
+ // TODO: Exercise the system in more interesting ways.
+ // Mostly what matters is that whatever we do here is sustainable: it
+ // can be repeated indefinitely on the system without causing
+ // problems. For example, launching activities is fine. Installing
+ // applications is only fine if we also uninstall them as part of the
+ // test.
+ try {
+ Log.i(TAG, "Sleeping for 10 seconds...");
+ Thread.sleep(10 * 1000);
+ } catch (InterruptedException ignored) {
+ }
+
+ finish(Activity.RESULT_OK, null);
+ }
+}
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk b/tests/SystemMemoryTest/host/Android.mk
similarity index 71%
copy from tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
copy to tests/SystemMemoryTest/host/Android.mk
index 5d7a6f7..a516e38 100644
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
+++ b/tests/SystemMemoryTest/host/Android.mk
@@ -1,4 +1,3 @@
-#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,7 +11,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := system-memory-test
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := tradefed
+LOCAL_COMPATIBILITY_SUITE := general-tests
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tests/SystemMemoryTest/host/AndroidTest.xml b/tests/SystemMemoryTest/host/AndroidTest.xml
new file mode 100644
index 0000000..6d2c95f
--- /dev/null
+++ b/tests/SystemMemoryTest/host/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs the system memory tests">
+ <option name="test-suite-tag" value="system-memory-test" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="SystemMemoryTestDevice.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.tests.sysmem.host.MemoryTest" />
+ </test>
+</configuration>
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
new file mode 100644
index 0000000..579d972
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+/**
+ * Critical user journeys with which to exercise the system, driven from the
+ * host.
+ */
+public class Cujs {
+ private ITestDevice device;
+
+ public Cujs(ITestDevice device) {
+ this.device = device;
+ }
+
+ /**
+ * Runs the critical user journeys.
+ */
+ public void run() throws DeviceNotAvailableException {
+ // Invoke the Device Cujs instrumentation to run the cujs.
+ // TODO: Consider exercising the system in other interesting ways as
+ // well.
+ String command = "am instrument -w com.android.tests.sysmem.device/.Cujs";
+ device.executeShellCommand(command);
+ }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
new file mode 100644
index 0000000..bbec065
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+import com.android.tradefed.testtype.IDeviceTest;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MemoryTest implements IDeviceTest {
+
+ @Rule public TestMetrics testMetrics = new TestMetrics();
+ @Rule public TestLogData testLogs = new TestLogData();
+
+ private ITestDevice testDevice;
+ private int iterations = 0; // Number of times cujs have been run.
+ private Metrics metrics;
+ private Cujs cujs;
+
+ @Override
+ public void setDevice(ITestDevice device) {
+ testDevice = device;
+ metrics = new Metrics(device, testMetrics, testLogs);
+ cujs = new Cujs(device);
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return testDevice;
+ }
+
+ // Invoke a single iteration of running the cujs.
+ private void runCujs() throws DeviceNotAvailableException {
+ cujs.run();
+ iterations++;
+ }
+
+ // Sample desired memory.
+ private void sample()
+ throws DeviceNotAvailableException, IOException, Metrics.MetricsException {
+ metrics.sample(String.format("%03d", iterations));
+ }
+
+ @Test
+ public void run() throws Exception {
+ sample(); // Sample before running cujs
+ runCujs();
+ sample(); // Sample after first iteration of cujs
+
+ // Run cujs in a loop to highlight memory leaks.
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 5; j++) {
+ runCujs();
+ }
+ sample();
+ }
+ }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
new file mode 100644
index 0000000..7de092a
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+/**
+ * Utilities for sampling and reporting memory metrics.
+ */
+class Metrics {
+
+ private ITestDevice device;
+ private TestMetrics metrics;
+ private TestLogData logs;
+
+ /**
+ * Exception thrown in case of error sampling metrics.
+ */
+ public static class MetricsException extends Exception {
+ public MetricsException(String msg) {
+ super(msg);
+ }
+
+ MetricsException(String msg, Exception cause) {
+ super(msg, cause);
+ }
+ }
+
+ /**
+ * Constructs a metrics instance that will output high level metrics and
+ * more detailed breakdowns using the given <code>metrics</code> and
+ * <code>logs</code> objects.
+ *
+ * @param device the device to sample metrics from
+ * @param metrics where to log the high level metrics when taking a sample
+ * @param logs where to log detailed breakdowns when taking a sample
+ */
+ public Metrics(ITestDevice device, TestMetrics metrics, TestLogData logs) {
+ this.device = device;
+ this.metrics = metrics;
+ this.logs = logs;
+ }
+
+ /**
+ * Returns the pid for the process with the given name.
+ */
+ private int getPidForProcess(String name)
+ throws DeviceNotAvailableException, IOException, MetricsException {
+ String psout = device.executeShellCommand("ps -A -o PID,CMD");
+ Scanner sc = new Scanner(psout);
+ try {
+ // ps output is of the form:
+ // PID CMD
+ // 1 init
+ // 2 kthreadd
+ // ...
+ // 9693 ps
+ sc.nextLine();
+ while (sc.hasNextLine()) {
+ int pid = sc.nextInt();
+ String cmd = sc.next();
+
+ if (name.equals(cmd)) {
+ return pid;
+ }
+ }
+ } catch (InputMismatchException e) {
+ throw new MetricsException("unexpected ps output format: " + psout, e);
+ }
+
+ throw new MetricsException("failed to get pid for process " + name);
+ }
+
+ /**
+ * Samples the current memory use on the system. Outputs high level test
+ * metrics and detailed breakdowns to the TestMetrics and TestLogData
+ * objects provided when constructing this Metrics instance. The metrics
+ * and log names are prefixed with the given label.
+ *
+ * @param label prefix to use for metrics and logs output for this sample.
+ */
+ void sample(String label) throws DeviceNotAvailableException, IOException, MetricsException {
+ // adb root access is required to get showmap
+ device.enableAdbRoot();
+
+ int pid = getPidForProcess("system_server");
+
+ // Read showmap for system server and add it as a test log
+ String showmap = device.executeShellCommand("showmap " + pid);
+ String showmapLabel = label + ".system_server.showmap";
+ File file = File.createTempFile(showmapLabel, "txt");
+ PrintStream ps = new PrintStream(file);
+ ps.print(showmap);
+ try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
+ logs.addTestLog(showmapLabel, LogDataType.TEXT, dataStream);
+ }
+
+ // Extract VSS, PSS and RSS from the showmap and output them as metrics.
+ // The last lines of the showmap output looks something like:
+ // virtual shared shared private private
+ // size RSS PSS clean dirty clean dirty swap swapPSS # object
+ //-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
+ // 928480 113016 24860 87348 7916 3632 14120 1968 1968 1900 TOTAL
+ try {
+ int pos = showmap.lastIndexOf("----");
+ Scanner sc = new Scanner(showmap.substring(pos));
+ sc.next();
+ long vss = sc.nextLong();
+ long rss = sc.nextLong();
+ long pss = sc.nextLong();
+
+ metrics.addTestMetric(String.format("%s.system_server.vss", label), Long.toString(vss));
+ metrics.addTestMetric(String.format("%s.system_server.rss", label), Long.toString(rss));
+ metrics.addTestMetric(String.format("%s.system_server.pss", label), Long.toString(pss));
+ } catch (InputMismatchException e) {
+ throw new MetricsException("unexpected showmap format", e);
+ }
+
+ // TODO: Experiment with other additional metrics.
+
+ // TODO: Consider launching an instrumentation to collect metrics from
+ // within the device itself.
+ }
+}
diff --git a/tests/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/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 8c1fa9a..36b5578 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -690,10 +690,6 @@
return 0;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(CompileContext);
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 4b82eef..d57eaa1 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -311,10 +311,6 @@
return 0u;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
bool verbose_ = false;
std::string package_;
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 7875a2b..262f4fc4e 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -65,10 +65,6 @@
return 0;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
std::string empty_;
StdErrDiagnostics diagnostics_;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 717e757..8b1f672 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -292,10 +292,6 @@
return 0;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
StdErrDiagnostics diagnostics_;
bool verbose_ = false;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 1d508d9..c94b847 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -137,14 +137,6 @@
min_sdk_version_ = minSdk;
}
- bool IsAutoNamespace() override {
- return auto_namespace_;
- }
-
- void SetAutoNamespace(bool val) {
- auto_namespace_ = val;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(LinkContext);
@@ -156,7 +148,6 @@
SymbolTable symbols_;
bool verbose_ = false;
int min_sdk_version_ = 0;
- bool auto_namespace_ = false;
};
// A custom delegate that generates compatible pre-O IDs for use with feature splits.
@@ -2042,15 +2033,6 @@
options_.output_format = OutputFormat::kProto;
}
- if (options_.auto_namespace_static_lib) {
- if (!static_lib_) {
- context.GetDiagnostics()->Error(
- DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib");
- return 1;
- }
- context.SetAutoNamespace(true);
- }
-
if (package_id_) {
if (context.GetPackageType() != PackageType::kApp) {
context.GetDiagnostics()->Error(
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 434475e..fb8796f 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -64,7 +64,6 @@
// Static lib options.
bool no_static_lib_packages = false;
- bool auto_namespace_static_lib = false;
// AndroidManifest.xml massaging options.
ManifestFixerOptions manifest_fixer_options;
@@ -188,10 +187,6 @@
AddOptionalSwitch("--no-static-lib-packages",
"Merge all library resources under the app's package.",
&options_.no_static_lib_packages);
- AddOptionalSwitch("--auto-namespace-static-lib",
- "Automatically namespace resource references when building a static\n"
- "library.",
- &options_.auto_namespace_static_lib);
AddOptionalSwitch("--non-final-ids",
"Generates R.java without the final modifier. This is implied when\n"
"--static-lib is specified.",
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index b4cba8c..47288ec 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -104,10 +104,6 @@
return sdk_version_;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk
deleted file mode 100644
index 91716b9..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestAutoNamespace_LibOne
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_AAPT_NAMESPACES := true
-LOCAL_AAPT_FLAGS := --auto-namespace-static-lib
-# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac.
-LOCAL_JAR_EXCLUDE_FILES := none
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml
deleted file mode 100644
index f585840..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest package="com.example.android.aapt2.autonamespace.staticlib.one" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml
deleted file mode 100644
index 3e57b0f..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <!-- An attribute from StaticLibOne -->
- <attr name="StaticLibOne_attr" format="string" />
-
- <string name="Foo">Foo</string>
-
- <declare-styleable name="Widget">
- <attr name="StaticLibOne_attr" />
- <attr name="android:text" />
- </declare-styleable>
-</resources>
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java
deleted file mode 100644
index 886d48c..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.android.aapt2.autonamespace.staticlib.one;
-
-public class StaticLibOne { public static int FooId = R.string.Foo; }
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk
deleted file mode 100644
index c85496d..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestAutoNamespace_LibTwo
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestAutoNamespace_LibOne
-LOCAL_AAPT_NAMESPACES := true
-LOCAL_AAPT_FLAGS := --auto-namespace-static-lib
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml
deleted file mode 100644
index 8d3c506..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest package="com.example.android.aapt2.autonamespace.staticlib.two" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml
deleted file mode 100644
index fb20220..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<View xmlns:custom="http://schemas.android.com/apk/res-auto"
- custom:StaticLibOne_attr="@string/FooBar" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml
deleted file mode 100644
index c532387..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <string name="FooBar">@string/Foo</string>
-</resources>
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java
deleted file mode 100644
index 323f53a..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.android.aapt2.autonamespace.staticlib.two;
-
-public class StaticLibTwo {
- // IDs from StaticLibOne
- public static int FooId = com.example.android.aapt2.autonamespace.staticlib.one.R.string.Foo;
-
- // IDs from StaticLibTwo
- public static int FooBarId = R.string.FooBar;
- public static int LayoutId = R.layout.layout_two;
-}
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 28e71cc..3a5d585 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -80,7 +80,7 @@
// Find the attribute in the symbol table and check if it is visible from this callsite.
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
- transformed_reference, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);
+ transformed_reference, callsite_, symbols_, &err_str);
if (symbol) {
// Assign our style key the correct ID. The ID may not exist.
entry.key.id = symbol->id;
@@ -202,18 +202,12 @@
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
const CallSite& callsite,
- SymbolTable* symbols,
- bool auto_namespace) {
+ SymbolTable* symbols) {
if (reference.name) {
const ResourceName& name = reference.name.value();
if (name.package.empty()) {
// Use the callsite's package name if no package name was defined.
- const SymbolTable::Symbol* local_symbol =
- symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
- if (!auto_namespace || local_symbol) {
- return local_symbol;
- }
- return symbols->FindByNameInAnyPackage(name);
+ return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
}
return symbols->FindByName(name);
} else if (reference.id) {
@@ -226,9 +220,8 @@
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error) {
- const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols, auto_namespace);
+ const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
if (!symbol) {
if (out_error) *out_error = "not found";
return nullptr;
@@ -242,10 +235,10 @@
}
const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
- const Reference& reference, const CallSite& callsite, SymbolTable* symbols, bool auto_namespace,
+ const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveSymbolCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);
+ ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
if (!symbol) {
return nullptr;
}
@@ -260,10 +253,9 @@
Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveAttributeCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);
+ ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
if (!symbol) {
return {};
}
@@ -341,8 +333,8 @@
xml::ResolvePackage(decls, &transformed_reference);
std::string err_str;
- const SymbolTable::Symbol* s = ResolveSymbolCheckVisibility(
- transformed_reference, callsite, symbols, context->IsAutoNamespace(), &err_str);
+ const SymbolTable::Symbol* s =
+ ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
if (s) {
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 7887915..b0b4945 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -36,12 +36,10 @@
ReferenceLinker() = default;
// Performs name mangling and looks up the resource in the symbol table. Uses the callsite's
- // package if the reference has no package name defined (implicit), or if auto_namespace is
- // set try looking in all avaliable packages for a symbol of that name.
+ // package if the reference has no package name defined (implicit).
// Returns nullptr if the symbol was not found.
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
- const CallSite& callsite, SymbolTable* symbols,
- bool auto_namespace);
+ const CallSite& callsite, SymbolTable* symbols);
// Performs name mangling and looks up the resource in the symbol table. If the symbol is not
// visible by the reference at the callsite, nullptr is returned.
@@ -49,7 +47,6 @@
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error);
// Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
@@ -57,14 +54,13 @@
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error);
// Resolves the attribute reference and returns an xml::AaptAttribute if successful.
// If resolution fails, outError holds the error message.
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
- SymbolTable* symbols, bool auto_namespace,
+ SymbolTable* symbols,
std::string* out_error);
// Writes the resource name to the DiagMessage, using the
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 0b7b1ce..be38b96 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -267,7 +267,7 @@
std::string error;
const CallSite call_site{"com.app.test"};
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
- *test::BuildReference("com.app.test:string/foo"), call_site, &table, false, &error);
+ *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
ASSERT_THAT(symbol, NotNull());
EXPECT_TRUE(error.empty());
}
@@ -285,13 +285,13 @@
std::string error;
const CallSite call_site{"com.app.ext"};
- EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(*test::BuildReference("com.app.test:attr/foo"),
- call_site, &table, false, &error));
+ EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
+ *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
EXPECT_FALSE(error.empty());
error = "";
ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
- *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, false, &error));
+ *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
EXPECT_TRUE(error.empty());
}
@@ -303,74 +303,19 @@
.AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
.Build());
- const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(
- *test::BuildReference("string/foo"), CallSite{"com.app.test"}, &table, false);
+ const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
+ CallSite{"com.app.test"}, &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
- &table, false);
+ &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
- CallSite{"com.app.bad"}, &table, false),
+ CallSite{"com.app.bad"}, &table),
IsNull());
}
-TEST(ReferenceLinkerTest, AutomaticNamespace) {
- NameMangler mangler(NameManglerPolicy{"com.example.thislib"});
- SymbolTable table(&mangler);
- table.AppendSource(
- test::StaticSymbolSourceBuilder()
- .AddSymbol("com.example.thislib:string/thislib_string", ResourceId(0x7f010006))
- .AddSymbol("com.example.thislib:string/explicit_override_string", ResourceId(0x7f010007))
- .Build());
- // Lib2 is higher priority than lib1
- table.AppendSource(
- test::StaticSymbolSourceBuilder()
- .AddSymbol("com.example.lib2:string/lib2_string", ResourceId(0x7f010003))
- .AddSymbol("com.example.lib2:string/explicit_override_string", ResourceId(0x7f010004))
- .AddSymbol("com.example.lib2:string/implicit_override_string", ResourceId(0x7f010005))
- .Build());
- table.AppendSource(
- test::StaticSymbolSourceBuilder()
- .AddSymbol("com.example.lib1:string/explicit_override_string", ResourceId(0x7f010001))
- .AddSymbol("com.example.lib1:string/implicit_override_string", ResourceId(0x7f010002))
- .Build());
-
- // Sanity test: Local references are still fine.
- const SymbolTable::Symbol* s =
- ReferenceLinker::ResolveSymbol(*test::BuildReference("string/thislib_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010006)));
-
- // Local references are fine, even if clash with remote ones.
- s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/explicit_override_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010007)));
-
- // An unqualified reference to lib2 is rewritten
- s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/lib2_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010003)));
-
- // Qualified references are left alone.
- s = ReferenceLinker::ResolveSymbol(
- *test::BuildReference("com.example.lib2:string/explicit_override_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010004)));
-
- // Implicit overrides respect priority ordering.
- s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/implicit_override_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010005)));
-
- //
-}
} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 420a127..160ff92 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -97,8 +97,8 @@
attr_ref.private_reference = maybe_package.value().private_namespace;
std::string err_str;
- attr.compiled_attribute = ReferenceLinker::CompileXmlAttribute(
- attr_ref, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);
+ attr.compiled_attribute =
+ ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
if (!attr.compiled_attribute) {
DiagMessage error_msg(source);
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index d321f8f..ef99355 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -18,7 +18,6 @@
#include "test/Test.h"
-using ::testing::Eq;
using ::testing::IsNull;
using ::testing::NotNull;
@@ -71,29 +70,12 @@
.Build())
.AddPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
test::AttributeBuilder().Build())
- .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003))
.Build())
.Build();
-
- auto_namespace_context_ =
- test::ContextBuilder()
- .SetCompilationPackage("com.app.test")
- .SetNameManglerPolicy(NameManglerPolicy{"com.app.test", {"com.android.support"}})
- .SetAutoNamespace(true)
- .AddSymbolSource(
- test::StaticSymbolSourceBuilder()
- .AddPublicSymbol("android:attr/text", ResourceId(0x01010003),
- test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_STRING)
- .Build())
- .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003))
- .Build())
- .Build();
}
protected:
std::unique_ptr<IAaptContext> context_;
- std::unique_ptr<IAaptContext> auto_namespace_context_;
};
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
@@ -213,31 +195,6 @@
EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
}
-TEST_F(XmlReferenceLinkerTest, LinkAutoNamespaceResReference) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
- <View
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:text="@string/lib_string" />)");
-
- XmlReferenceLinker linker;
- // Should not link with auto-namespace support disabled.
- ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
- // Should link with auto-namespace enabled.
- ASSERT_TRUE(linker.Consume(auto_namespace_context_.get(), doc.get()));
-
- xml::Element* view_el = doc->root.get();
- ASSERT_THAT(view_el, NotNull());
-
- xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
- ASSERT_THAT(xml_attr, NotNull());
- ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010003)), xml_attr->compiled_attribute.value().id);
- Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
- ASSERT_THAT(ref, NotNull());
- ASSERT_TRUE(ref->name);
- EXPECT_EQ(make_value(ResourceId(0x7f020003)), ref->id);
-}
-
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id">
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index a931343..e92c121 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -99,10 +99,6 @@
util::make_unique<SourcePathDiagnostics>(Source{source}, context_->GetDiagnostics());
}
- bool IsAutoNamespace() override {
- return context_->IsAutoNamespace();
- }
-
private:
IAaptContext* context_;
std::unique_ptr<SourcePathDiagnostics> source_diag_;
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index a3a7719..30dad802 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -50,7 +50,6 @@
virtual NameMangler* GetNameMangler() = 0;
virtual bool IsVerbose() = 0;
virtual int GetMinSdkVersion() = 0;
- virtual bool IsAutoNamespace() = 0;
};
struct IResourceTableConsumer {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index ef2e448..fc4c9b5 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -114,16 +114,6 @@
return shared_symbol.get();
}
-const SymbolTable::Symbol* SymbolTable::FindByNameInAnyPackage(const ResourceName& name) {
- for (auto& source : sources_) {
- std::string package = source->GetPackageForSymbol(name);
- if (!package.empty()) {
- return FindByName(ResourceName(package, name.type, name.entry));
- }
- }
- return {};
-}
-
const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) {
if (const std::shared_ptr<Symbol>& s = id_cache_.get(id)) {
return s.get();
@@ -221,25 +211,6 @@
return symbol;
}
-std::string ResourceTableSymbolSource::GetPackageForSymbol(const ResourceName& name) {
- for (auto& package : table_->packages) {
- ResourceTableType* type = package->FindType(name.type);
- if (type == nullptr) {
- continue;
- }
- ResourceEntry* entry = type->FindEntry(name.entry);
- if (entry == nullptr) {
- continue;
- }
- return package->name;
- }
- if (name.type == ResourceType::kAttr) {
- // Recurse and try looking up a private attribute.
- return GetPackageForSymbol(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
- }
- return {};
-}
-
bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
int32_t cookie = 0;
return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie);
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index c798cbb..51a2e37 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -89,13 +89,6 @@
// results are stored in a cache which may evict entries on subsequent calls.
const Symbol* FindByName(const ResourceName& name);
- // Finds the symbol from any package, for use as part of automatic conversion to namespaces.
- // This returns the symbol from the highest priority package,
- // which mimics the behavior of the resource merger and overlays.
- // NOTE: Never hold on to the result between calls to FindByXXX. The
- // results are stored in a cache which may evict entries on subsequent calls.
- const Symbol* FindByNameInAnyPackage(const ResourceName& name);
-
// NOTE: Never hold on to the result between calls to FindByXXX. The
// results are stored in a cache which may evict entries on subsequent calls.
const Symbol* FindById(const ResourceId& id);
@@ -160,11 +153,6 @@
virtual std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) = 0;
- // Finds the name of a symbol from any package,
- // for use as part of automatic conversion to namespaces.
- // This returns the symbol from the highest priority package,
- // which mimics the behavior of the resource merger and overlays.
- virtual std::string GetPackageForSymbol(const ResourceName& name) = 0;
virtual std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) = 0;
// Default implementation tries the name if it exists, else the ID.
@@ -189,7 +177,6 @@
std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) override;
- std::string GetPackageForSymbol(const ResourceName& name) override;
std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
return {};
}
@@ -210,9 +197,6 @@
std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) override;
- std::string GetPackageForSymbol(const ResourceName& name) override {
- return {};
- }
std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override;
std::unique_ptr<SymbolTable::Symbol> FindByReference(
const Reference& ref) override;
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index df40b26..1f59d70 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -120,39 +120,4 @@
EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull());
}
-TEST(SymbolTableTest, FindByNameInAnyPackage) {
- // This represents lib3 --depends-on--> lib2 --depends-on--> lib1
-
- NameMangler mangler(NameManglerPolicy{"com.example.lib3"});
- SymbolTable symbol_table(&mangler);
- // Lib2 has higher precedence than lib1, as it is closer to the current library (lib3)
- // in the dependency graph.
-
- symbol_table.AppendSource(test::StaticSymbolSourceBuilder()
- .AddPublicSymbol("com.example.lib1:string/foo", ResourceId())
- .AddSymbol("com.example.lib1:attr/foo", ResourceId(),
- test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_FLAGS)
- .AddItem("one", 0x01)
- .AddItem("two", 0x02)
- .Build())
- .Build());
- symbol_table.PrependSource(test::StaticSymbolSourceBuilder()
- .AddPublicSymbol("com.example.lib2:string/foo", ResourceId())
- .Build());
-
- // Sanity test
- EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("string/foo")), IsNull());
-
- // Test public symbol resolution
- const SymbolTable::Symbol* const found_string =
- symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("string/foo"));
- ASSERT_THAT(found_string, NotNull());
-
- // Test attr resolution
- const SymbolTable::Symbol* const found_attr =
- symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("attr/foo"));
- ASSERT_THAT(found_attr, NotNull());
-}
-
} // namespace aapt
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index a07d79f..0564db0 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -81,10 +81,6 @@
return min_sdk_version_;
}
- bool IsAutoNamespace() override {
- return auto_namespace_;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(Context);
@@ -97,7 +93,6 @@
NameMangler name_mangler_;
SymbolTable symbols_;
int min_sdk_version_;
- bool auto_namespace_;
};
class ContextBuilder {
@@ -132,11 +127,6 @@
return *this;
}
- ContextBuilder& SetAutoNamespace(bool auto_namespace) {
- context_->auto_namespace_ = auto_namespace;
- return *this;
- }
-
std::unique_ptr<Context> Build() { return std::move(context_); }
private:
@@ -182,15 +172,6 @@
return nullptr;
}
- std::string GetPackageForSymbol(const ResourceName& name) override {
- for (auto const& imap : name_map_) {
- if (imap.first.type == name.type && imap.first.entry == name.entry) {
- return imap.first.package;
- }
- }
- return "";
- }
-
std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
auto iter = id_map_.find(id);
if (iter != id_map_.end()) {
diff --git a/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 6963ed0..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;
@@ -1585,13 +1568,12 @@
* Return the record of {@link WifiActivityEnergyInfo} object that
* has the activity and energy info. This can be used to ascertain what
* the controller has been up to, since the last sample.
- * @param updateType Type of info, cached vs refreshed.
*
* @return a record with {@link WifiActivityEnergyInfo} or null if
* report is unavailable or unsupported
* @hide
*/
- public WifiActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
+ public WifiActivityEnergyInfo getControllerActivityEnergyInfo() {
if (mService == null) return null;
try {
synchronized(this) {
@@ -2461,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(() -> {
@@ -2470,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);
@@ -3103,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) {
@@ -3719,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 928a1da..045291b 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -721,6 +722,17 @@
}
/**
+ * Enable/Disable wifi scanning.
+ *
+ * {@hide}
+ */
+ @RequiresPermission(Manifest.permission.NETWORK_STACK)
+ public void setScanningEnabled(boolean enable) {
+ validateChannel();
+ mAsyncChannel.sendMessage(enable ? CMD_ENABLE : CMD_DISABLE);
+ }
+
+ /**
* Register a listener that will receive results from all single scans
* Either the onSuccess/onFailure will be called once when the listener is registered. After
* (assuming onSuccess was called) all subsequent single scan results will be delivered to the
@@ -916,6 +928,7 @@
* scans should also not share this object.
* {@hide}
*/
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
PnoScanListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
@@ -930,9 +943,9 @@
* Stop an ongoing wifi PNO scan
* @param listener specifies which scan to cancel; must be same object as passed in {@link
* #startPnoScan}
- * TODO(rpius): Check if we can remove pnoSettings param in stop.
* {@hide}
*/
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void stopPnoScan(ScanListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
int key = removeListener(listener);
@@ -1164,6 +1177,10 @@
public static final int CMD_DEREGISTER_SCAN_LISTENER = BASE + 28;
/** @hide */
public static final int CMD_GET_SINGLE_SCAN_RESULTS = BASE + 29;
+ /** @hide */
+ public static final int CMD_ENABLE = BASE + 30;
+ /** @hide */
+ public static final int CMD_DISABLE = BASE + 31;
private Context mContext;
private IWifiScanner mService;
diff --git a/wifi/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);
+ }
}