Merge "Add telephony commands about SMS apps"
diff --git a/Android.bp b/Android.bp
index cb7f7a2..415eff3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -368,6 +368,7 @@
"core/java/com/android/internal/app/IAppOpsService.aidl",
"core/java/com/android/internal/app/IBatteryStats.aidl",
"core/java/com/android/internal/app/ISoundTriggerService.aidl",
+ "core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl",
"core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
"core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
"core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
@@ -1084,53 +1085,63 @@
"-federate SupportLib https://developer.android.com " +
"-federationapi SupportLib $(location current/support-api.txt) "
-doc_defaults {
- name: "api-stubs-default",
- srcs: [
- ":opt-telephony-srcs",
- ":opt-net-voip-srcs",
- ":openjdk_javadoc_files",
- ":non_openjdk_javadoc_files",
- ":android_icu4j_src_files_for_docs",
- ],
- srcs_lib: "framework",
- srcs_lib_whitelist_dirs: frameworks_base_subdirs,
- srcs_lib_whitelist_pkgs: packages_to_document,
- libs: [
- "core-oj",
- "core-libart",
- "conscrypt",
- "bouncycastle",
- "okhttp",
- "ext",
- "framework",
- "voip-common",
- "android.test.mock.impl",
- ],
- local_sourcepaths: frameworks_base_subdirs,
- html_dirs: [
- "docs/html",
- ],
- knowntags: [
- "docs/knowntags.txt",
- ":known-oj-tags",
- ],
- custom_template: "droiddoc-templates-sdk",
- hdf: [
- "dac true",
- "sdk.codename O",
- "sdk.preview.version 1",
- "sdk.version 7.0",
- "sdk.rel.id 1",
- "sdk.preview 0",
- ],
- resourcesdir: "docs/html/reference/images",
- resourcesoutdir: "reference/android/images",
- installable: false,
-}
+framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
+ "-overview $(location core/java/overview.html) " +
+ // Federate Support Library references against local API file.
+ "-federate SupportLib https://developer.android.com " +
+ "-federationapi SupportLib $(location current/support-api.txt) "
-doc_defaults {
- name: "framework-docs-default",
+framework_docs_only_libs = [
+ "conscrypt",
+ "bouncycastle",
+ "voip-common",
+ "android.test.mock",
+ "android-support-annotations",
+ "android-support-compat",
+ "android-support-core-ui",
+ "android-support-core-utils",
+ "android-support-customtabs",
+ "android-support-design",
+ "android-support-dynamic-animation",
+ "android-support-exifinterface",
+ "android-support-fragment",
+ "android-support-media-compat",
+ "android-support-percent",
+ "android-support-recommendation",
+ "android-support-transition",
+ "android-support-tv-provider",
+ "android-support-v7-cardview",
+ "android-support-v7-gridlayout",
+ "android-support-v7-mediarouter",
+ "android-support-v7-palette",
+ "android-support-v7-preference",
+ "android-support-v13",
+ "android-support-v14-preference",
+ "android-support-v17-leanback",
+ "android-support-v17-preference-leanback",
+ "android-support-wear",
+ "android-support-vectordrawable",
+ "android-support-animatedvectordrawable",
+ "android-support-v7-appcompat",
+ "android-support-v7-recyclerview",
+ "android-support-emoji",
+ "android-support-emoji-appcompat",
+ "android-support-emoji-bundled",
+ "android-support-v8-renderscript",
+ "android-support-multidex",
+ "android-support-multidex-instrumentation",
+]
+
+metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
+ "--hide-package com.android.okhttp " +
+ "--hide-package com.android.org.conscrypt --hide-package com.android.server " +
+ "--hide RequiresPermission " +
+ "--hide MissingPermission --hide BroadcastBehavior " +
+ "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
+ "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
+
+stubs_defaults {
+ name: "framework-doc-stubs-default",
srcs: [
"test-base/src/**/*.java",
":opt-telephony-srcs",
@@ -1144,47 +1155,25 @@
srcs_lib: "framework",
srcs_lib_whitelist_dirs: frameworks_base_subdirs,
srcs_lib_whitelist_pkgs: packages_to_document,
- libs: [
- "conscrypt",
- "bouncycastle",
- "voip-common",
- "android.test.mock",
- "android-support-annotations",
- "android-support-compat",
- "android-support-core-ui",
- "android-support-core-utils",
- "android-support-customtabs",
- "android-support-design",
- "android-support-dynamic-animation",
- "android-support-exifinterface",
- "android-support-fragment",
- "android-support-media-compat",
- "android-support-percent",
- "android-support-recommendation",
- "android-support-transition",
- "android-support-tv-provider",
- "android-support-v7-cardview",
- "android-support-v7-gridlayout",
- "android-support-v7-mediarouter",
- "android-support-v7-palette",
- "android-support-v7-preference",
- "android-support-v13",
- "android-support-v14-preference",
- "android-support-v17-leanback",
- "android-support-v17-preference-leanback",
- "android-support-wear",
- "android-support-vectordrawable",
- "android-support-animatedvectordrawable",
- "android-support-v7-appcompat",
- "android-support-v7-recyclerview",
- "android-support-emoji",
- "android-support-emoji-appcompat",
- "android-support-emoji-bundled",
- "android-support-v8-renderscript",
- "android-support-multidex",
- "android-support-multidex-instrumentation",
- ],
+ libs: framework_docs_only_libs,
local_sourcepaths: frameworks_base_subdirs,
+ create_doc_stubs: true,
+ annotations_enabled: true,
+ api_levels_annotations_enabled: true,
+ api_levels_annotations_dirs: [
+ "sdk-dir",
+ "api-versions-jars-dir",
+ ],
+ previous_api: ":last-released-public-api",
+ merge_annotations_dirs: [
+ "metalava-manual",
+ "ojluni-annotated-sdk-stubs",
+ ],
+}
+
+doc_defaults {
+ name: "framework-docs-default",
+ libs: framework_docs_only_libs,
html_dirs: [
"docs/html",
],
@@ -1205,22 +1194,12 @@
],
arg_files: [
"core/res/AndroidManifest.xml",
- ":api-version-xml",
"core/java/overview.html",
":current-support-api",
- "api/current.txt",
],
create_stubs: false,
}
-metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
- "--hide-package com.android.okhttp " +
- "--hide-package com.android.org.conscrypt --hide-package com.android.server " +
- "--hide RequiresPermission " +
- "--hide MissingPermission --hide BroadcastBehavior " +
- "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
- "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
-
stubs_defaults {
name: "metalava-api-stubs-default",
srcs: [
@@ -1250,25 +1229,54 @@
previous_api: ":last-released-public-api",
merge_annotations_dirs: [
"metalava-manual",
- "ojluni-annotated-stubs",
+ "ojluni-annotated-sdk-stubs",
],
+ api_levels_annotations_enabled: true,
+ api_levels_annotations_dirs: [
+ "sdk-dir",
+ "api-versions-jars-dir",
+ ],
+}
+
+droidstubs {
+ name: "framework-doc-stubs",
+ defaults: ["framework-doc-stubs-default"],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ args: metalava_framework_docs_args,
+}
+
+droidstubs {
+ name: "framework-doc-system-stubs",
+ defaults: ["framework-doc-stubs-default"],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi ",
}
droiddoc {
name: "doc-comment-check-docs",
defaults: ["framework-docs-default"],
- args: framework_docs_args + " -referenceonly -parsecomments",
+ srcs: [
+ ":framework-doc-stubs",
+ ],
+ args: framework_docs_only_args + " -referenceonly -parsecomments",
installable: false,
}
droiddoc {
name: "offline-sdk-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
hdf: [
"android.whichdoc offline",
],
proofread_file: "offline-sdk-docs-proofrerad.txt",
- args: framework_docs_args + " -offlinemode -title \"Android SDK\"",
+ args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
write_sdk_values: true,
static_doc_index_redirect: "docs/docs-preview-index.html",
}
@@ -1276,11 +1284,14 @@
droiddoc {
name: "offline-sdk-referenceonly-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
hdf: [
"android.whichdoc offline",
],
proofread_file: "offline-sdk-referenceonly-docs-proofrerad.txt",
- args: framework_docs_args + " -offlinemode -title \"Android SDK\" -referenceonly",
+ args: framework_docs_only_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",
@@ -1289,13 +1300,15 @@
droiddoc {
name: "offline-system-sdk-referenceonly-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-system-stubs",
+ ],
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",
+ args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" +
+ " -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",
@@ -1304,12 +1317,15 @@
droiddoc {
name: "online-sdk-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
hdf: [
"android.whichdoc online",
"android.hasSamples true",
],
proofread_file: "online-sdk-docs-proofrerad.txt",
- args: framework_docs_args +
+ args: framework_docs_only_args +
" -toroot / -samplegroup Admin " +
" -samplegroup Background " +
" -samplegroup Connectivity " +
@@ -1330,14 +1346,16 @@
droiddoc {
name: "online-system-api-sdk-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-system-stubs",
+ ],
hdf: [
"android.whichdoc online",
"android.hasSamples true",
],
proofread_file: "online-system-api-sdk-docs-proofrerad.txt",
- args: framework_docs_args +
+ args: framework_docs_only_args +
" -referenceonly " +
- " -showAnnotation android.annotation.SystemApi " +
" -title \"Android SDK - Including system APIs.\" " +
" -hide 101 " +
" -hide 104 " +
@@ -1363,12 +1381,15 @@
droiddoc {
name: "ds-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
hdf: [
"android.whichdoc online",
"android.hasSamples true",
],
proofread_file: "ds-docs-proofrerad.txt",
- args: framework_docs_args +
+ args: framework_docs_only_args +
" -toroot / -samplegroup Admin " +
" -samplegroup Background " +
" -samplegroup Connectivity " +
@@ -1389,11 +1410,14 @@
droiddoc {
name: "ds-static-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
hdf: [
"android.whichdoc online",
],
proofread_file: "ds-static-docs-proofrerad.txt",
- args: framework_docs_args +
+ args: framework_docs_only_args +
" -staticonly " +
" -toroot / " +
" -devsite " +
@@ -1403,11 +1427,14 @@
droiddoc {
name: "ds-ref-navtree-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
hdf: [
"android.whichdoc online",
],
proofread_file: "ds-ref-navtree-docs-proofrerad.txt",
- args: framework_docs_args +
+ args: framework_docs_only_args +
" -toroot / " +
" -atLinksNavtree " +
" -navtreeonly ",
@@ -1416,12 +1443,15 @@
droiddoc {
name: "online-sdk-dev-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
hdf: [
"android.whichdoc online",
"android.hasSamples true",
],
proofread_file: "online-sdk-dev-docs-proofrerad.txt",
- args: framework_docs_args +
+ args: framework_docs_only_args +
" -toroot / -samplegroup Admin " +
" -samplegroup Background " +
" -samplegroup Connectivity " +
@@ -1442,13 +1472,16 @@
droiddoc {
name: "hidden-docs",
defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
proofread_file: "hidden-docs-proofrerad.txt",
- args: framework_docs_args +
+ args: framework_docs_only_args +
" -referenceonly " +
" -title \"Android SDK - Including hidden APIs.\"",
}
-droiddoc {
+droidstubs {
name: "hwbinder-stubs-docs",
srcs: [
"core/java/android/os/HidlSupport.java",
@@ -1466,10 +1499,15 @@
"core/java/android/os/RemoteException.java",
"core/java/android/util/AndroidException.java",
],
- custom_template: "droiddoc-templates-sdk",
installable: false,
no_framework_libs: true,
- args: "-showAnnotation android.annotation.SystemApi -nodocs -stubsourceonly",
+ annotations_enabled: true,
+ previous_api: ":last-released-public-api",
+ merge_annotations_dirs: [
+ "metalava-manual",
+ "ojluni-annotated-sdk-stubs",
+ ],
+ args: " --show-annotation android.annotation.SystemApi",
}
java_library_static {
@@ -1496,23 +1534,17 @@
}
-droiddoc {
+droidstubs {
name: "hiddenapi-mappings",
- defaults: ["api-stubs-default"],
+ defaults: ["metalava-api-stubs-default"],
arg_files: [
"core/res/AndroidManifest.xml",
- ":api-version-xml",
- "core/java/overview.html",
- ":current-support-api",
- "api/current.txt",
],
dex_mapping_filename: "dex-mapping.txt",
- args: framework_docs_args +
- " -referenceonly" +
- " -nodocs" +
- " -showUnannotated" +
- " -showAnnotation android.annotation.SystemApi" +
- " -showAnnotation android.annotation.TestApi",
+ args: metalava_framework_docs_args +
+ " --show-unannotated " +
+ " --show-annotation android.annotation.SystemApi " +
+ " --show-annotation android.annotation.TestApi "
}
filegroup {
@@ -1559,6 +1591,7 @@
removed_api_file: "api/removed.txt",
},
},
+ jdiff_enabled: true,
}
droidstubs {
@@ -1583,6 +1616,7 @@
removed_api_file: "api/system-removed.txt",
},
},
+ jdiff_enabled: true,
}
droidstubs {
diff --git a/Android.mk b/Android.mk
index 5c4c237..d333074 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,265 +53,16 @@
cat $^ | sort -u > $@.tmp
$(call commit-change-for-toc,$@)
-# the documentation
-# ============================================================
-
-# TODO: deal with com/google/android/googleapps
-packages_to_document := \
- android \
- javax/microedition/khronos \
- org/apache/http/conn \
- org/apache/http/params \
-
-# include definition of libcore_to_document
-include libcore/Docs.mk
-
-non_base_dirs := \
- ../opt/telephony/src/java/android/telephony \
- ../opt/telephony/src/java/android/telephony/gsm \
- ../opt/net/voip/src/java/android/net/rtp \
- ../opt/net/voip/src/java/android/net/sip \
-
-# Find all files in specific directories (relative to frameworks/base)
-# to document and check apis
-files_to_check_apis := \
- $(call find-other-java-files, \
- $(non_base_dirs) \
- )
-
-# Find all files in specific packages that were used to compile
-# framework.jar to document and check apis
-files_to_check_apis += \
- $(addprefix ../../,\
- $(filter \
- $(foreach dir,$(FRAMEWORKS_BASE_JAVA_SRC_DIRS),\
- $(foreach package,$(packages_to_document),\
- $(dir)/$(package)/%.java)),\
- $(SOONG_FRAMEWORK_SRCS)))
-
-# Find all generated files that were used to compile framework.jar
-files_to_check_apis_generated := \
- $(filter $(OUT_DIR)/%,\
- $(SOONG_FRAMEWORK_SRCS))
-
-# These are relative to frameworks/base
-# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
-files_to_document := \
- $(files_to_check_apis) \
- $(call find-other-java-files,\
- test-base/src \
- test-mock/src \
- test-runner/src)
-
-# These are relative to frameworks/base
-html_dirs := \
- $(FRAMEWORKS_BASE_SUBDIRS) \
- $(non_base_dirs) \
-
-# Common sources for doc check and api check
-common_src_files := \
- $(call find-other-html-files, $(html_dirs)) \
- $(addprefix ../../, $(libcore_to_document)) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_SRC_FILES := \
- $(files_to_document) \
- $(common_src_files) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_API_CHECK_SRC_FILES := \
- $(files_to_check_apis) \
- $(common_src_files) \
-
# This is used by ide.mk as the list of source files that are
# always included.
INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
-framework_docs_LOCAL_DROIDDOC_SOURCE_PATH := \
- $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
-
-framework_docs_LOCAL_SRCJARS := $(SOONG_FRAMEWORK_SRCJARS)
-
-framework_docs_LOCAL_GENERATED_SOURCES := \
- $(libcore_to_document_generated) \
- $(files_to_check_apis_generated) \
-
-framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
- core-oj \
- core-libart \
- conscrypt \
- bouncycastle \
- okhttp \
- ext \
- framework \
- voip-common \
-
-# Platform docs can refer to Support Library APIs, but we don't actually build
-# them as part of the docs target, so we need to include them on the classpath.
-framework_docs_LOCAL_JAVA_LIBRARIES := \
- $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) \
- $(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES)
-
-framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
-# The since flag (-since N.xml API_LEVEL) is used to add API Level information
-# to the reference documentation. Must be in order of oldest to newest.
-#
-# Conscrypt (com.android.org.conscrypt) is an implementation detail and should
-# not be referenced in the documentation.
-framework_docs_LOCAL_DROIDDOC_OPTIONS := \
- -android \
- -knowntags ./frameworks/base/docs/knowntags.txt \
- -knowntags ./libcore/known_oj_tags.txt \
- -manifest ./frameworks/base/core/res/AndroidManifest.xml \
- -hidePackage com.android.internal \
- -hidePackage com.android.internal.util \
- -hidePackage com.android.okhttp \
- -hidePackage com.android.org.conscrypt \
- -hidePackage com.android.server
-
-# Convert an sdk level to a "since" argument.
-since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.$(2)) $(1)
-
-finalized_xml_sdks := $(call numerically_sort,\
- $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml)))
-finalized_txt_sdks := $(call numerically_sort,\
- $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt)))
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_xml_sdks),$(call since-arg,$(sdk),xml))
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_txt_sdks),$(call since-arg,$(sdk),txt))
-ifneq ($(PLATFORM_VERSION_CODENAME),REL)
- framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -since ./frameworks/base/api/current.txt $(PLATFORM_VERSION_CODENAME)
-endif
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
- -overview $(LOCAL_PATH)/core/java/overview.html
-
-framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
- $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
-
-framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
- $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-
-framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
- frameworks/base/docs/knowntags.txt \
- $(libcore_to_document_generated)
-
-samples_dir := development/samples/browseable
-
-# Whitelist of valid groups, used for default TOC grouping. Each sample must
-# belong to one (and only one) group. Assign samples to groups by setting
-# a sample.group var to one of these groups in the sample's _index.jd.
-sample_groups := -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
-
-## SDK version identifiers used in the published docs
- # major[.minor] version for current SDK. (full releases only)
-framework_docs_SDK_VERSION:=7.0
- # release version (ie "Release x") (full releases only)
-framework_docs_SDK_REL_ID:=1
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -hdf dac true \
- -hdf sdk.codename O \
- -hdf sdk.preview.version 1 \
- -hdf sdk.version $(framework_docs_SDK_VERSION) \
- -hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
- -hdf sdk.preview 0 \
- -resourcesdir $(LOCAL_PATH)/docs/html/reference/images/ \
- -resourcesoutdir reference/android/images/
-
-# Federate Support Library references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -federate SupportLib https://developer.android.com \
- -federationapi SupportLib prebuilts/sdk/current/support-api.txt
-
-# Federate AndroidX references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -federate AndroidX https://developer.android.com \
- -federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
-
-# Get the highest numbered api txt for the given api level.
-# $(1): the api level (e.g. public, system)
-define highest_sdk_txt
-$(HISTORICAL_SDK_VERSIONS_ROOT)/$(lastword $(call numerically_sort, \
- $(patsubst \
- $(HISTORICAL_SDK_VERSIONS_ROOT)/%,\
- %,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/$(1)/api/android.txt)\
- ) \
-))
-endef
-
-# ==== Public API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
- $(INTERNAL_PLATFORM_API_FILE)
-
-LOCAL_MODULE := offline-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,public))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
-# ==== System API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
- $(INTERNAL_PLATFORM_SYSTEM_API_FILE)
-
-LOCAL_MODULE := offline-system-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,system))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTEM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt)
# 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
diff --git a/api/current.txt b/api/current.txt
index ea21c2d..3beb491 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -2905,6 +2905,7 @@
field public static final deprecated int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
field public static final int FLAG_REQUEST_FINGERPRINT_GESTURES = 512; // 0x200
+ field public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 1024; // 0x400
field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
field public int eventTypes;
@@ -5580,14 +5581,14 @@
ctor public Notification.WearableExtender(android.app.Notification);
method public android.app.Notification.WearableExtender addAction(android.app.Notification.Action);
method public android.app.Notification.WearableExtender addActions(java.util.List<android.app.Notification.Action>);
- method public android.app.Notification.WearableExtender addPage(android.app.Notification);
- method public android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
+ method public deprecated android.app.Notification.WearableExtender addPage(android.app.Notification);
+ method public deprecated android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
method public android.app.Notification.WearableExtender clearActions();
- method public android.app.Notification.WearableExtender clearPages();
+ method public deprecated android.app.Notification.WearableExtender clearPages();
method public android.app.Notification.WearableExtender clone();
method public android.app.Notification.Builder extend(android.app.Notification.Builder);
method public java.util.List<android.app.Notification.Action> getActions();
- method public android.graphics.Bitmap getBackground();
+ method public deprecated android.graphics.Bitmap getBackground();
method public java.lang.String getBridgeTag();
method public int getContentAction();
method public deprecated int getContentIcon();
@@ -5596,17 +5597,17 @@
method public deprecated int getCustomContentHeight();
method public deprecated int getCustomSizePreset();
method public java.lang.String getDismissalId();
- method public android.app.PendingIntent getDisplayIntent();
+ method public deprecated android.app.PendingIntent getDisplayIntent();
method public deprecated int getGravity();
- method public boolean getHintAmbientBigPicture();
+ method public deprecated boolean getHintAmbientBigPicture();
method public deprecated boolean getHintAvoidBackgroundClipping();
method public boolean getHintContentIntentLaunchesActivity();
method public deprecated boolean getHintHideIcon();
method public deprecated int getHintScreenTimeout();
method public deprecated boolean getHintShowBackgroundOnly();
- method public java.util.List<android.app.Notification> getPages();
+ method public deprecated java.util.List<android.app.Notification> getPages();
method public boolean getStartScrollBottom();
- method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+ method public deprecated android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String);
method public android.app.Notification.WearableExtender setContentAction(int);
method public deprecated android.app.Notification.WearableExtender setContentIcon(int);
@@ -5615,23 +5616,23 @@
method public deprecated android.app.Notification.WearableExtender setCustomContentHeight(int);
method public deprecated android.app.Notification.WearableExtender setCustomSizePreset(int);
method public android.app.Notification.WearableExtender setDismissalId(java.lang.String);
- method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
+ method public deprecated android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
method public deprecated android.app.Notification.WearableExtender setGravity(int);
- method public android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean);
+ method public deprecated android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean);
method public deprecated android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public deprecated android.app.Notification.WearableExtender setHintHideIcon(boolean);
method public deprecated android.app.Notification.WearableExtender setHintScreenTimeout(int);
method public deprecated android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
- field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
- field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
- field public static final int SIZE_DEFAULT = 0; // 0x0
- field public static final int SIZE_FULL_SCREEN = 5; // 0x5
- field public static final int SIZE_LARGE = 4; // 0x4
- field public static final int SIZE_MEDIUM = 3; // 0x3
- field public static final int SIZE_SMALL = 2; // 0x2
- field public static final int SIZE_XSMALL = 1; // 0x1
+ field public static final deprecated int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+ field public static final deprecated int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+ field public static final deprecated int SIZE_DEFAULT = 0; // 0x0
+ field public static final deprecated int SIZE_FULL_SCREEN = 5; // 0x5
+ field public static final deprecated int SIZE_LARGE = 4; // 0x4
+ field public static final deprecated int SIZE_MEDIUM = 3; // 0x3
+ field public static final deprecated int SIZE_SMALL = 2; // 0x2
+ field public static final deprecated int SIZE_XSMALL = 1; // 0x1
field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
}
@@ -7733,7 +7734,9 @@
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
+ method public android.bluetooth.BluetoothServerSocket listenUsingInsecureL2capChannel() throws java.io.IOException;
method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
+ method public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
method public boolean setName(java.lang.String);
method public boolean startDiscovery();
@@ -8101,7 +8104,9 @@
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler);
method public boolean createBond();
+ method public android.bluetooth.BluetoothSocket createInsecureL2capChannel(int) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
+ method public android.bluetooth.BluetoothSocket createL2capChannel(int) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public int describeContents();
method public boolean fetchUuidsWithSdp();
@@ -8517,6 +8522,7 @@
method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
method public void close() throws java.io.IOException;
+ method public int getPsm();
}
public final class BluetoothSocket implements java.io.Closeable {
@@ -14009,11 +14015,15 @@
method public android.graphics.Paint.Style getStyle();
method public android.graphics.Paint.Align getTextAlign();
method public void getTextBounds(java.lang.String, int, int, android.graphics.Rect);
+ method public void getTextBounds(java.lang.CharSequence, int, int, android.graphics.Rect);
method public void getTextBounds(char[], int, int, android.graphics.Rect);
method public java.util.Locale getTextLocale();
method public android.os.LocaleList getTextLocales();
method public void getTextPath(char[], int, int, float, float, android.graphics.Path);
method public void getTextPath(java.lang.String, int, int, float, float, android.graphics.Path);
+ method public float getTextRunAdvances(char[], int, int, int, int, boolean, float[], int);
+ method public int getTextRunCursor(char[], int, int, boolean, int, int);
+ method public int getTextRunCursor(java.lang.CharSequence, int, int, boolean, int, int);
method public float getTextScaleX();
method public float getTextSize();
method public float getTextSkewX();
@@ -14080,6 +14090,11 @@
method public void setWordSpacing(float);
method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
+ field public static final int CURSOR_AFTER = 0; // 0x0
+ field public static final int CURSOR_AT = 4; // 0x4
+ field public static final int CURSOR_AT_OR_AFTER = 1; // 0x1
+ field public static final int CURSOR_AT_OR_BEFORE = 3; // 0x3
+ field public static final int CURSOR_BEFORE = 2; // 0x2
field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
field public static final int DITHER_FLAG = 4; // 0x4
field public static final int EMBEDDED_BITMAP_TEXT_FLAG = 1024; // 0x400
@@ -18675,6 +18690,37 @@
field public static final int WORD_NUMBER_LIMIT = 200; // 0xc8
}
+ public abstract class CaseMap {
+ method public static android.icu.text.CaseMap.Fold fold();
+ method public abstract android.icu.text.CaseMap omitUnchangedText();
+ method public static android.icu.text.CaseMap.Lower toLower();
+ method public static android.icu.text.CaseMap.Title toTitle();
+ method public static android.icu.text.CaseMap.Upper toUpper();
+ }
+
+ public static final class CaseMap.Fold extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Fold omitUnchangedText();
+ method public android.icu.text.CaseMap.Fold turkic();
+ }
+
+ public static final class CaseMap.Lower extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Lower omitUnchangedText();
+ }
+
+ public static final class CaseMap.Title extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.util.Locale, android.icu.text.BreakIterator, java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Title noBreakAdjustment();
+ method public android.icu.text.CaseMap.Title noLowercase();
+ method public android.icu.text.CaseMap.Title omitUnchangedText();
+ }
+
+ public static final class CaseMap.Upper extends android.icu.text.CaseMap {
+ method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+ method public android.icu.text.CaseMap.Upper omitUnchangedText();
+ }
+
public final class CollationElementIterator {
method public int getMaxExpansion(int);
method public int getOffset();
@@ -19340,6 +19386,30 @@
enum_constant public static final android.icu.text.DisplayContext.Type SUBSTITUTE_HANDLING;
}
+ public final class Edits {
+ ctor public Edits();
+ method public void addReplace(int, int);
+ method public void addUnchanged(int);
+ method public android.icu.text.Edits.Iterator getCoarseChangesIterator();
+ method public android.icu.text.Edits.Iterator getCoarseIterator();
+ method public android.icu.text.Edits.Iterator getFineChangesIterator();
+ method public android.icu.text.Edits.Iterator getFineIterator();
+ method public boolean hasChanges();
+ method public int lengthDelta();
+ method public void reset();
+ }
+
+ public static final class Edits.Iterator {
+ method public int destinationIndex();
+ method public boolean findSourceIndex(int);
+ method public boolean hasChange();
+ method public int newLength();
+ method public boolean next();
+ method public int oldLength();
+ method public int replacementIndex();
+ method public int sourceIndex();
+ }
+
public abstract class IDNA {
method public static android.icu.text.IDNA getUTS46Instance(int);
method public abstract java.lang.StringBuilder labelToASCII(java.lang.CharSequence, java.lang.StringBuilder, android.icu.text.IDNA.Info);
@@ -36594,9 +36664,9 @@
field public static final java.lang.String IS_PRIVATE = "isprivate";
field public static final java.lang.String LATITUDE = "latitude";
field public static final java.lang.String LONGITUDE = "longitude";
- field public static final java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
+ field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
field public static final java.lang.String ORIENTATION = "orientation";
- field public static final java.lang.String PICASA_ID = "picasa_id";
+ field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
}
public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
@@ -36699,7 +36769,7 @@
field public static final java.lang.String LANGUAGE = "language";
field public static final java.lang.String LATITUDE = "latitude";
field public static final java.lang.String LONGITUDE = "longitude";
- field public static final java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
+ field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
field public static final java.lang.String RESOLUTION = "resolution";
field public static final java.lang.String TAGS = "tags";
}
@@ -39858,6 +39928,7 @@
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
method public android.os.IBinder onBind(android.content.Intent);
+ method public java.util.Set<java.lang.String> onGetSupportedVoiceActions(java.util.Set<java.lang.String>);
method public void onLaunchVoiceAssistFromKeyguard();
method public void onReady();
method public void onShutdown();
@@ -42716,6 +42787,7 @@
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int);
+ method public static int getSlotIndex(int);
method public static int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isNetworkRoaming(int);
@@ -42733,7 +42805,9 @@
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+ field public static final int INVALID_SIM_SLOT_INDEX = -2; // 0xfffffffe
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
+ field public static final int SIM_NOT_INSERTED = -3; // 0xfffffffd
}
public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
@@ -49696,6 +49770,7 @@
method public boolean isScrollable();
method public boolean isSelected();
method public boolean isShowingHintText();
+ method public boolean isTextEntryKey();
method public boolean isVisibleToUser();
method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
@@ -49757,6 +49832,7 @@
method public void setSource(android.view.View);
method public void setSource(android.view.View, int);
method public void setText(java.lang.CharSequence);
+ method public void setTextEntryKey(boolean);
method public void setTextSelection(int, int);
method public void setTooltipText(java.lang.CharSequence);
method public void setTraversalAfter(android.view.View);
diff --git a/api/system-current.txt b/api/system-current.txt
index d29b216..8f7606a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -91,6 +91,7 @@
field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
+ field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
@@ -155,6 +156,7 @@
field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
+ field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
@@ -217,6 +219,14 @@
field public static final int config_sendPackageName = 17891328; // 0x1110000
}
+ public static final class R.dimen {
+ field public static final int config_restricted_icon_size = 17104903; // 0x1050007
+ }
+
+ public static final class R.drawable {
+ field public static final int ic_info = 17301684; // 0x10800b4
+ }
+
public static final class R.raw {
field public static final int loaderror = 17825792; // 0x1100000
field public static final int nodomain = 17825793; // 0x1100001
@@ -355,11 +365,6 @@
field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
}
- public final class AutomaticZenRule implements android.os.Parcelable {
- ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long);
- ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long, android.service.notification.ZenPolicy);
- }
-
public class BroadcastOptions {
method public static android.app.BroadcastOptions makeBasic();
method public void setDontSendToRestrictedApps(boolean);
@@ -1113,6 +1118,7 @@
public class PackageItemInfo {
method public deprecated java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager, float, int);
+ method public static void setForceSafeLabels(boolean);
field public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
field public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
field public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
@@ -4193,6 +4199,7 @@
method public final void attachBaseContext(android.content.Context);
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract java.util.List<android.content.pm.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+ method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.permissionpresenterservice.RuntimePermissionPresenterService";
}
@@ -5398,7 +5405,6 @@
}
public class TelephonyManager {
- method public deprecated void answerRingingCall();
method public deprecated void call(java.lang.String, java.lang.String);
method public int checkCarrierPrivilegesForPackage(java.lang.String);
method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
@@ -5406,7 +5412,6 @@
method public boolean disableDataConnectivity();
method public boolean enableDataConnectivity();
method public void enableVideoCalling(boolean);
- method public deprecated boolean endCall();
method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -6588,6 +6593,10 @@
package android.view {
+ public abstract class Window {
+ method public void addPrivateFlags(int);
+ }
+
public abstract interface WindowManager implements android.view.ViewManager {
method public abstract android.graphics.Region getCurrentImeTouchRegion();
}
@@ -6595,6 +6604,7 @@
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
method public final long getUserActivityTimeout();
method public final void setUserActivityTimeout(long);
+ field public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
}
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index b88c760..9012c33 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -143,3 +143,12 @@
}
+package android.telephony {
+
+ public class TelephonyManager {
+ method public deprecated void answerRingingCall();
+ method public deprecated boolean endCall();
+ }
+
+}
+
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 36e51b9..12fb4a3 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -470,7 +470,8 @@
onExecute(provider);
} finally {
if (provider != null) {
- activityManager.removeContentProviderExternal(providerName, token);
+ activityManager.removeContentProviderExternalAsUser(
+ providerName, token, mUserId);
}
}
} catch (Exception e) {
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 87799b3..cd48af9 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -407,7 +407,19 @@
WorkerThreadSection::~WorkerThreadSection() {}
+void sigpipe_handler(int signum) {
+ if (signum == SIGPIPE) {
+ ALOGE("Wrote to a broken pipe\n");
+ } else {
+ ALOGE("Received unexpected signal: %d\n", signum);
+ }
+}
+
static void* worker_thread_func(void* cookie) {
+ // Don't crash the service if we write to a closed pipe (which can happen if
+ // dumping times out).
+ signal(SIGPIPE, sigpipe_handler);
+
WorkerThreadData* data = (WorkerThreadData*)cookie;
status_t err = data->section->BlockingCall(data->pipe.writeFd().get());
@@ -486,6 +498,7 @@
}
}
}
+
write_section_stats(requests->sectionStats(this->id), buffer);
if (timedOut || buffer.timedOut()) {
ALOGW("[%s] timed out", this->name.string());
@@ -773,7 +786,10 @@
}
}
gLastLogsRetrieved[mLogID] = lastTimestamp;
- proto.flush(pipeWriteFd);
+ if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+ ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+ return EPIPE;
+ }
return NO_ERROR;
}
@@ -875,7 +891,7 @@
break;
}
if (cStatus != NO_ERROR) {
- ALOGE("TombstoneSection '%s' child had an issue: %s\n", this->name.string(), strerror(-cStatus));
+ ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
}
auto dump = std::make_unique<char[]>(buffer.size());
@@ -894,7 +910,13 @@
dumpPipe.readFd().reset();
}
- proto.flush(pipeWriteFd);
+ if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+ ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+ if (err != NO_ERROR) {
+ return EPIPE;
+ }
+ }
+
return err;
}
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 302b4ef..86d956f 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -171,7 +171,7 @@
*/
class TombstoneSection : public WorkerThreadSection {
public:
- TombstoneSection(int id, const char* type, int64_t timeoutMs = 30000 /* 30 seconds */);
+ TombstoneSection(int id, const char* type, int64_t timeoutMs = 120000 /* 2 minutes */);
virtual ~TombstoneSection();
virtual status_t BlockingCall(int pipeWriteFd) const;
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index b11e843..d334f96 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -198,8 +198,7 @@
sp<GraphicBuffer> outBuffer;
status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
- 0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation,
- &outBuffer);
+ 0 /* reqHeight */, false, captureOrientation, &outBuffer);
if (result != NO_ERROR) {
close(fd);
return 1;
@@ -207,7 +206,14 @@
result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
- if (base == NULL) {
+ if (base == nullptr || result != NO_ERROR) {
+ String8 reason;
+ if (base == nullptr) {
+ reason = "Failed to write to buffer";
+ } else {
+ reason.appendFormat("Error Code: %d", result);
+ }
+ fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
close(fd);
return 1;
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5e87ef6..27c5a17 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -67,7 +67,9 @@
src/subscriber/SubscriberReporter.cpp \
src/HashableDimensionKey.cpp \
src/guardrail/StatsdStats.cpp \
- src/socket/StatsSocketListener.cpp
+ src/socket/StatsSocketListener.cpp \
+ src/shell/ShellSubscriber.cpp \
+ src/shell/shell_config.proto
# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
statsd_common_src += \
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 7b6d29b..fc1a61c 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -147,6 +147,9 @@
case STRING:
str_value = from.str_value;
break;
+ case STORAGE:
+ storage_value = from.storage_value;
+ break;
default:
break;
}
@@ -164,6 +167,8 @@
return std::to_string(double_value) + "[D]";
case STRING:
return str_value + "[S]";
+ case STORAGE:
+ return "bytes of size " + std::to_string(storage_value.size()) + "[ST]";
default:
return "[UNKNOWN]";
}
@@ -183,6 +188,8 @@
return double_value == that.double_value;
case STRING:
return str_value == that.str_value;
+ case STORAGE:
+ return storage_value == that.storage_value;
default:
return false;
}
@@ -201,6 +208,8 @@
return double_value != that.double_value;
case STRING:
return str_value != that.str_value;
+ case STORAGE:
+ return storage_value != that.storage_value;
default:
return false;
}
@@ -220,6 +229,8 @@
return double_value < that.double_value;
case STRING:
return str_value < that.str_value;
+ case STORAGE:
+ return storage_value < that.storage_value;
default:
return false;
}
@@ -239,6 +250,8 @@
return double_value > that.double_value;
case STRING:
return str_value > that.str_value;
+ case STORAGE:
+ return storage_value > that.storage_value;
default:
return false;
}
@@ -258,6 +271,8 @@
return double_value >= that.double_value;
case STRING:
return str_value >= that.str_value;
+ case STORAGE:
+ return storage_value >= that.storage_value;
default:
return false;
}
@@ -274,6 +289,11 @@
return v;
}
+ if (type == STORAGE) {
+ ALOGE("Can't operate on storage value type");
+ return v;
+ }
+
switch (type) {
case INT:
v.setInt(int_value - that.int_value);
@@ -311,6 +331,9 @@
case STRING:
str_value = that.str_value;
break;
+ case STORAGE:
+ storage_value = that.storage_value;
+ break;
default:
break;
}
@@ -326,6 +349,10 @@
ALOGE("Can't operate on string value type");
return *this;
}
+ if (type == STORAGE) {
+ ALOGE("Can't operate on storage value type");
+ return *this;
+ }
switch (type) {
case INT:
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index b1b885e..77163f9 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -32,7 +32,7 @@
const int32_t kClearLastBitDeco = 0x7f;
const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
-enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE };
int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
@@ -293,6 +293,11 @@
type = STRING;
}
+ Value(const std::vector<uint8_t>& v) {
+ storage_value = v;
+ type = STORAGE;
+ }
+
void setInt(int32_t v) {
int_value = v;
type = INT;
@@ -320,6 +325,7 @@
double double_value;
};
std::string str_value;
+ std::vector<uint8_t> storage_value;
Type type;
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 8e02f9c..f4c70be 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -257,12 +257,18 @@
return it->second->byteSize();
}
-void StatsLogProcessor::dumpStates(FILE* out, bool verbose) {
+void StatsLogProcessor::dumpStates(int out, bool verbose) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- fprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
- for (auto metricsManager : mMetricsManagers) {
- metricsManager.second->dumpStates(out, verbose);
+ FILE* fout = fdopen(out, "w");
+ if (fout == NULL) {
+ return;
}
+ fprintf(fout, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
+ for (auto metricsManager : mMetricsManagers) {
+ metricsManager.second->dumpStates(fout, verbose);
+ }
+
+ fclose(fout);
}
/*
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index df80b8e..4091d95 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -83,7 +83,7 @@
return mUidMap;
}
- void dumpStates(FILE* out, bool verbose);
+ void dumpStates(int outFd, bool verbose);
void informPullAlarmFired(const int64_t timestampNs);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 9a79345..2ef1169 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -34,13 +34,14 @@
#include <dirent.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <private/android_filesystem_config.h>
-#include <utils/Looper.h>
-#include <utils/String16.h>
#include <statslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
#include <unistd.h>
+#include <utils/Looper.h>
+#include <utils/String16.h>
+#include <chrono>
using namespace android;
@@ -214,30 +215,8 @@
sp<IResultReceiver> resultReceiver =
IResultReceiver::asInterface(data.readStrongBinder());
- FILE* fin = fdopen(in, "r");
- FILE* fout = fdopen(out, "w");
- FILE* ferr = fdopen(err, "w");
-
- if (fin == NULL || fout == NULL || ferr == NULL) {
- resultReceiver->send(NO_MEMORY);
- } else {
- err = command(fin, fout, ferr, args);
- resultReceiver->send(err);
- }
-
- if (fin != NULL) {
- fflush(fin);
- fclose(fin);
- }
- if (fout != NULL) {
- fflush(fout);
- fclose(fout);
- }
- if (fout != NULL) {
- fflush(ferr);
- fclose(ferr);
- }
-
+ err = command(in, out, err, args, resultReceiver);
+ resultReceiver->send(err);
return NO_ERROR;
}
default: { return BnStatsManager::onTransact(code, data, reply, flags); }
@@ -251,10 +230,6 @@
if (!checkCallingPermission(String16(kPermissionDump))) {
return PERMISSION_DENIED;
}
- FILE* out = fdopen(fd, "w");
- if (out == NULL) {
- return NO_MEMORY; // the fd is already open
- }
bool verbose = false;
bool proto = false;
@@ -265,21 +240,20 @@
proto = true;
}
- dump_impl(out, verbose, proto);
+ dump_impl(fd, verbose, proto);
- fclose(out);
return NO_ERROR;
}
/**
* Write debugging data about statsd in text or proto format.
*/
-void StatsService::dump_impl(FILE* out, bool verbose, bool proto) {
+void StatsService::dump_impl(int out, bool verbose, bool proto) {
if (proto) {
vector<uint8_t> data;
StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats.
for (size_t i = 0; i < data.size(); i ++) {
- fprintf(out, "%c", data[i]);
+ dprintf(out, "%c", data[i]);
}
} else {
StatsdStats::getInstance().dumpStats(out);
@@ -290,7 +264,8 @@
/**
* Implementation of the adb shell cmd stats command.
*/
-status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
+ sp<IResultReceiver> resultReceiver) {
uid_t uid = IPCThreadState::self()->getCallingUid();
if (uid != AID_ROOT && uid != AID_SHELL) {
return PERMISSION_DENIED;
@@ -342,97 +317,106 @@
if (!args[0].compare(String8("print-logs"))) {
return cmd_print_logs(out, args);
}
+ if (!args[0].compare(String8("data-subscribe"))) {
+ if (mShellSubscriber == nullptr) {
+ mShellSubscriber = new ShellSubscriber(mUidMap);
+ }
+ mShellSubscriber->startNewSubscription(in, out, resultReceiver);
+ return NO_ERROR;
+ }
}
print_cmd_help(out);
return NO_ERROR;
}
-void StatsService::print_cmd_help(FILE* out) {
- fprintf(out,
+void StatsService::print_cmd_help(int out) {
+ dprintf(out,
"usage: adb shell cmd stats print-stats-log [tag_required] "
"[timestamp_nsec_optional]\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats meminfo\n");
- fprintf(out, "\n");
- fprintf(out, " Prints the malloc debug information. You need to run the following first: \n");
- fprintf(out, " # adb shell stop\n");
- fprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n");
- fprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n");
- fprintf(out, " # adb shell start\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n");
- fprintf(out, "\n");
- fprintf(out, " Prints the UID, app name, version mapping.\n");
- fprintf(out, " PKG Optional package name to print the uids of the package\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
- fprintf(out, "\n");
- fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats write-to-disk \n");
- fprintf(out, "\n");
- fprintf(out, " Flushes all data on memory to disk.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n");
- fprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n");
- fprintf(out, " UID The uid to use. It is only possible to pass a UID\n");
- fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
- fprintf(out, " uid is used.\n");
- fprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n");
- fprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
- fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
- fprintf(out, "\n");
- fprintf(out, " Adds, updates or removes a configuration. The proto should be in\n");
- fprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n");
- fprintf(out, " provided, then all configs will be removed from memory and disk.\n");
- fprintf(out, "\n");
- fprintf(out, " UID The uid to use. It is only possible to pass the UID\n");
- fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
- fprintf(out, " uid is used.\n");
- fprintf(out, " NAME The per-uid name to use\n");
- fprintf(out, "\n");
- 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 [--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");
- fprintf(out, " calling uid is used.\n");
- fprintf(out, " NAME The name of the configuration\n");
- fprintf(out, " --proto Print proto binary.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
- fprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\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");
- fprintf(out, " calling uid is used.\n");
- fprintf(out, " NAME The name of the configuration\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats print-stats\n");
- fprintf(out, " Prints some basic stats.\n");
- fprintf(out, " --proto Print proto binary instead of string format.\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats clear-puller-cache\n");
- fprintf(out, " Clear cached puller data.\n");
- fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats print-logs\n");
- fprintf(out, " Only works on eng build\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats meminfo\n");
+ dprintf(out, "\n");
+ dprintf(out, " Prints the malloc debug information. You need to run the following first: \n");
+ dprintf(out, " # adb shell stop\n");
+ dprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n");
+ dprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n");
+ dprintf(out, " # adb shell start\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n");
+ dprintf(out, "\n");
+ dprintf(out, " Prints the UID, app name, version mapping.\n");
+ dprintf(out, " PKG Optional package name to print the uids of the package\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
+ dprintf(out, "\n");
+ dprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats write-to-disk \n");
+ dprintf(out, "\n");
+ dprintf(out, " Flushes all data on memory to disk.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n");
+ dprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n");
+ dprintf(out, " UID The uid to use. It is only possible to pass a UID\n");
+ dprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
+ dprintf(out, " uid is used.\n");
+ dprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n");
+ dprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
+ dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
+ dprintf(out, "\n");
+ dprintf(out, " Adds, updates or removes a configuration. The proto should be in\n");
+ dprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n");
+ dprintf(out, " provided, then all configs will be removed from memory and disk.\n");
+ dprintf(out, "\n");
+ dprintf(out, " UID The uid to use. It is only possible to pass the UID\n");
+ dprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
+ dprintf(out, " uid is used.\n");
+ dprintf(out, " NAME The per-uid name to use\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n");
+ dprintf(out, "\n be removed from memory and disk!\n");
+ dprintf(out, "\n");
+ dprintf(out,
+ "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] "
+ "[--proto]\n");
+ dprintf(out, " Dump all metric data for a configuration.\n");
+ dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
+ dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
+ dprintf(out, " calling uid is used.\n");
+ dprintf(out, " NAME The name of the configuration\n");
+ dprintf(out, " --proto Print proto binary.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
+ dprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n");
+ dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
+ dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
+ dprintf(out, " calling uid is used.\n");
+ dprintf(out, " NAME The name of the configuration\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats print-stats\n");
+ dprintf(out, " Prints some basic stats.\n");
+ dprintf(out, " --proto Print proto binary instead of string format.\n");
+ dprintf(out, "\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats clear-puller-cache\n");
+ dprintf(out, " Clear cached puller data.\n");
+ dprintf(out, "\n");
+ dprintf(out, "usage: adb shell cmd stats print-logs\n");
+ dprintf(out, " Only works on eng build\n");
}
-status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
+status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
string name;
bool good = false;
int uid;
@@ -456,9 +440,9 @@
}
}
} else {
- fprintf(out,
+ dprintf(out,
"The metrics can only be dumped for other UIDs on eng or userdebug "
- "builds.\n");
+ "builds.\n");
}
}
if (!good) {
@@ -481,7 +465,7 @@
return NO_ERROR;
}
-status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) {
const int argCount = args.size();
if (argCount >= 2) {
if (args[1] == "update" || args[1] == "remove") {
@@ -508,7 +492,7 @@
}
}
} else {
- fprintf(err,
+ dprintf(err,
"The config can only be set for other UIDs on eng or userdebug "
"builds.\n");
}
@@ -526,21 +510,21 @@
char* endp;
int64_t configID = strtoll(name.c_str(), &endp, 10);
if (endp == name.c_str() || *endp != '\0') {
- fprintf(err, "Error parsing config ID.\n");
+ dprintf(err, "Error parsing config ID.\n");
return UNKNOWN_ERROR;
}
// Read stream into buffer.
string buffer;
- if (!android::base::ReadFdToString(fileno(in), &buffer)) {
- fprintf(err, "Error reading stream for StatsConfig.\n");
+ if (!android::base::ReadFdToString(in, &buffer)) {
+ dprintf(err, "Error reading stream for StatsConfig.\n");
return UNKNOWN_ERROR;
}
// Parse buffer.
StatsdConfig config;
if (!config.ParseFromString(buffer)) {
- fprintf(err, "Error parsing proto stream for StatsConfig.\n");
+ dprintf(err, "Error parsing proto stream for StatsConfig.\n");
return UNKNOWN_ERROR;
}
@@ -562,7 +546,7 @@
return UNKNOWN_ERROR;
}
-status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String8>& args) {
+status_t StatsService::cmd_dump_report(int out, int err, const Vector<String8>& args) {
if (mProcessor != nullptr) {
int argCount = args.size();
bool good = false;
@@ -597,7 +581,7 @@
}
}
} else {
- fprintf(out,
+ dprintf(out,
"The metrics can only be dumped for other UIDs on eng or userdebug "
"builds.\n");
}
@@ -608,11 +592,11 @@
includeCurrentBucket, ADB_DUMP, &data);
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
- fprintf(out, "%c", data[i]);
+ dprintf(out, "%c", data[i]);
}
} else {
- fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
- fprintf(out, "See the StatsLogReport in logcat...\n");
+ dprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
+ dprintf(out, "See the StatsLogReport in logcat...\n");
}
return android::OK;
} else {
@@ -621,12 +605,12 @@
return UNKNOWN_ERROR;
}
} else {
- fprintf(out, "Log processor does not exist...\n");
+ dprintf(out, "Log processor does not exist...\n");
return UNKNOWN_ERROR;
}
}
-status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_stats(int out, const Vector<String8>& args) {
int argCount = args.size();
bool proto = false;
if (!std::strcmp("--proto", args[argCount-1].c_str())) {
@@ -638,13 +622,13 @@
vector<uint8_t> data;
statsdStats.dumpStats(&data, false); // does not reset statsdStats.
for (size_t i = 0; i < data.size(); i ++) {
- fprintf(out, "%c", data[i]);
+ dprintf(out, "%c", data[i]);
}
} else {
vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
for (const ConfigKey& key : configs) {
- fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
+ dprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
mProcessor->GetMetricsSize(key));
}
statsdStats.dumpStats(out);
@@ -652,29 +636,29 @@
return NO_ERROR;
}
-status_t StatsService::cmd_print_uid_map(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_uid_map(int out, const Vector<String8>& args) {
if (args.size() > 1) {
string pkg;
pkg.assign(args[1].c_str(), args[1].size());
auto uids = mUidMap->getAppUid(pkg);
- fprintf(out, "%s -> [ ", pkg.c_str());
+ dprintf(out, "%s -> [ ", pkg.c_str());
for (const auto& uid : uids) {
- fprintf(out, "%d ", uid);
+ dprintf(out, "%d ", uid);
}
- fprintf(out, "]\n");
+ dprintf(out, "]\n");
} else {
mUidMap->printUidMap(out);
}
return NO_ERROR;
}
-status_t StatsService::cmd_write_data_to_disk(FILE* out) {
- fprintf(out, "Writing data to disk\n");
+status_t StatsService::cmd_write_data_to_disk(int out) {
+ dprintf(out, "Writing data to disk\n");
mProcessor->WriteDataToDisk(ADB_DUMP);
return NO_ERROR;
}
-status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& args) {
bool good = false;
int32_t uid;
int32_t label;
@@ -695,13 +679,13 @@
state = atoi(args[3].c_str());
good = true;
} else {
- fprintf(out,
+ dprintf(out,
"Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
- "on eng or userdebug builds.\n");
+ "on eng or userdebug builds.\n");
}
}
if (good) {
- fprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
+ dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state);
} else {
print_cmd_help(out);
@@ -710,46 +694,46 @@
return NO_ERROR;
}
-status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
- fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
+ dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
- fprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
+ dprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
return NO_ERROR;
}
return UNKNOWN_ERROR;
}
-status_t StatsService::cmd_remove_all_configs(FILE* out) {
- fprintf(out, "Removing all configs...\n");
+status_t StatsService::cmd_remove_all_configs(int out) {
+ dprintf(out, "Removing all configs...\n");
VLOG("StatsService::cmd_remove_all_configs was called");
mConfigManager->RemoveAllConfigs();
StorageManager::deleteAllFiles(STATS_SERVICE_DIR);
return NO_ERROR;
}
-status_t StatsService::cmd_dump_memory_info(FILE* out) {
- fprintf(out, "meminfo not available.\n");
+status_t StatsService::cmd_dump_memory_info(int out) {
+ dprintf(out, "meminfo not available.\n");
return NO_ERROR;
}
-status_t StatsService::cmd_clear_puller_cache(FILE* out) {
+status_t StatsService::cmd_clear_puller_cache(int out) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
ipc->getCallingPid(), ipc->getCallingUid());
if (checkCallingPermission(String16(kPermissionDump))) {
int cleared = mPullerManager->ForceClearPullerCache();
- fprintf(out, "Puller removed %d cached data!\n", cleared);
+ dprintf(out, "Puller removed %d cached data!\n", cleared);
return NO_ERROR;
} else {
return PERMISSION_DENIED;
}
}
-status_t StatsService::cmd_print_logs(FILE* out, const Vector<String8>& args) {
+status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
ipc->getCallingUid());
@@ -885,6 +869,9 @@
void StatsService::OnLogEvent(LogEvent* event) {
mProcessor->OnLogEvent(event);
+ if (mShellSubscriber != nullptr) {
+ mShellSubscriber->onLogEvent(*event);
+ }
}
Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 613f509..0618927 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -24,6 +24,7 @@
#include "external/StatsPullerManager.h"
#include "logd/LogListener.h"
#include "packages/UidMap.h"
+#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
#include <android/os/BnStatsManager.h>
@@ -54,7 +55,8 @@
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+ virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args,
+ sp<IResultReceiver> resultReceiver);
virtual Status systemRunning();
virtual Status statsCompanionReady();
@@ -162,73 +164,73 @@
/**
* Text or proto output of dumpsys.
*/
- void dump_impl(FILE* out, bool verbose, bool proto);
+ void dump_impl(int outFd, bool verbose, bool proto);
/**
* Print usage information for the commands
*/
- void print_cmd_help(FILE* out);
+ void print_cmd_help(int out);
/**
* Trigger a broadcast.
*/
- status_t cmd_trigger_broadcast(FILE* out, Vector<String8>& args);
+ status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args);
/**
* Handle the config sub-command.
*/
- status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+ status_t cmd_config(int inFd, int outFd, int err, Vector<String8>& args);
/**
* Prints some basic stats to std out.
*/
- status_t cmd_print_stats(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_stats(int outFd, const Vector<String8>& args);
/**
* Print the event log.
*/
- status_t cmd_dump_report(FILE* out, FILE* err, const Vector<String8>& args);
+ status_t cmd_dump_report(int outFd, int err, const Vector<String8>& args);
/**
* Print the mapping of uids to package names.
*/
- status_t cmd_print_uid_map(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_uid_map(int outFd, const Vector<String8>& args);
/**
* Flush the data to disk.
*/
- status_t cmd_write_data_to_disk(FILE* out);
+ status_t cmd_write_data_to_disk(int outFd);
/**
* Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling
* StatsLog.write(APP_BREADCRUMB_REPORTED).
*/
- status_t cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args);
+ status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args);
/**
* Print contents of a pulled metrics source.
*/
- status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args);
/**
* Removes all configs stored on disk and on memory.
*/
- status_t cmd_remove_all_configs(FILE* out);
+ status_t cmd_remove_all_configs(int outFd);
/*
* Dump memory usage by statsd.
*/
- status_t cmd_dump_memory_info(FILE* out);
+ status_t cmd_dump_memory_info(int outFd);
/*
* Clear all puller cached data
*/
- status_t cmd_clear_puller_cache(FILE* out);
+ status_t cmd_clear_puller_cache(int outFd);
/**
* Print all stats logs received to logcat.
*/
- status_t cmd_print_logs(FILE* out, const Vector<String8>& args);
+ status_t cmd_print_logs(int outFd, const Vector<String8>& args);
/**
* Adds a configuration after checking permissions and obtaining UID from binder call.
@@ -275,6 +277,8 @@
*/
bool mEngBuild;
+ sp<ShellSubscriber> mShellSubscriber;
+
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2c07431..c6c10ec 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -132,6 +132,10 @@
KeyValuePairsAtom key_value_pairs_atom = 83;
VibratorStateChanged vibrator_state_changed = 84;
DeferredJobStatsReported deferred_job_stats_reported = 85;
+ ThermalThrottlingStateChanged thermal_throttling = 86;
+ FingerprintAcquired fingerprint_acquired = 87;
+ FingerprintAuthenticated fingerprint_authenticated = 88;
+ FingerprintErrorOccurred fingerprint_error_occurred = 89;
}
// Pulled events will start at field 10000.
@@ -166,7 +170,9 @@
DirectoryUsage directory_usage = 10026;
AppSize app_size = 10027;
CategorySize category_size = 10028;
- android.service.procstats.ProcessStatsSectionProto proc_stats = 10029;
+ BatteryVoltage battery_voltage = 10030;
+ NumFingerprints num_fingerprints = 10031;
+ ProcStats proc_stats = 10029;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -229,6 +235,26 @@
*/
/**
+ * Logs when the Thermal service HAL notifies the throttling start/stop events.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/stats/StatsCompanionService.java
+ */
+message ThermalThrottlingStateChanged {
+ optional android.os.TemperatureTypeEnum sensor_type = 1;
+
+ enum State {
+ UNKNOWN = 0;
+ START = 1;
+ STOP = 2;
+ }
+
+ optional State state = 2;
+
+ optional float temperature = 3;
+}
+
+/**
* Logs when the screen state changes.
*
* Logged from:
@@ -410,9 +436,9 @@
* Logs reporting of a ble scan finding results.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
*/
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+// TODO: Consider also tracking per-scanner-id.
message BleScanResultReceived {
repeated AttributionNode attribution_node = 1;
@@ -1810,6 +1836,60 @@
optional android.os.statsd.EventType event_id = 2;
}
+/**
+ * Logs when a fingerprint acquire event occurs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintAcquired {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // If this acquire is for a crypto fingerprint.
+ // e.g. Secure purchases, unlock password storage.
+ optional bool is_crypto = 2;
+}
+
+/**
+ * Logs when a fingerprint authentication event occurs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintAuthenticated {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // If this authentication is for a crypto fingerprint.
+ // e.g. Secure purchases, unlock password storage.
+ optional bool is_crypto = 2;
+ // Whether or not this authentication was successful.
+ optional bool is_authenticated = 3;
+}
+
+/**
+ * Logs when a fingerprint error occurs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintErrorOccurred {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // If this error is for a crypto fingerprint.
+ // e.g. Secure purchases, unlock password storage.
+ optional bool is_crypto = 2;
+
+ enum Error {
+ UNKNOWN = 0;
+ LOCKOUT = 1;
+ PERMANENT_LOCKOUT = 2;
+ }
+ // The type of error.
+ optional Error error = 3;
+}
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -2155,6 +2235,16 @@
}
/**
+ * Pulls battery voltage.
+ * Pulled from:
+ * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message BatteryVoltage {
+ // The voltage of the battery, in millivolts.
+ optional int32 voltage_mV = 1;
+}
+
+/**
* Pulls the temperature of various parts of the device.
* The units are tenths of a degree Celsius. Eg: 30.3C is reported as 303.
*
@@ -2175,8 +2265,8 @@
/**
* Pulls the statistics of calls to Binder.
*
- * Binder stats are cumulative from boot unless somebody reset the data using
- * > adb shell dumpsys binder_calls_stats --reset
+ * Binder stats will be reset every time the data is pulled. It means it can only be pulled by one
+ * config on the device.
*
* Next tag: 14
*/
@@ -2240,8 +2330,16 @@
optional int64 exception_count = 2;
}
+/**
+ * Pulls the statistics of message dispatching on HandlerThreads.
+ *
+ * Looper stats will be reset every time the data is pulled. It means it can only be pulled by one
+ * config on the device.
+ *
+ * Next tag: 11
+ */
message LooperStats {
- // Currently not collected and always set to 0.
+ // The uid that made a call to the System Server and caused the message to be enqueued.
optional int32 uid = 1 [(is_uid) = true];
// Fully qualified class name of the handler target class.
@@ -2283,8 +2381,11 @@
// Total CPU usage of all processed message.
// Average can be computed using recorded_total_cpu_micros /
// recorded_message_count. Total can be computed using
- // recorded_total_cpu_micros / recorded_message_count * call_count.
+ // recorded_total_cpu_micros / recorded_message_count * message_count.
optional int64 recorded_total_cpu_micros = 9;
+
+ // True if the screen was interactive PowerManager#isInteractive at the end of the call.
+ optional bool screen_interactive = 10;
}
/**
@@ -2365,3 +2466,23 @@
// Uses System.currentTimeMillis(), which is wall clock time.
optional int64 cache_time_millis = 3;
}
+
+/**
+ * Pulls the number of fingerprints for each user.
+ *
+ * Pulled from StatsCompanionService, which queries FingerprintManager.
+ */
+message NumFingerprints {
+ // The associated user. Eg: 0 for owners, 10+ for others.
+ // Defined in android/os/UserHandle.java
+ optional int32 user = 1;
+ // Number of fingerprints registered to that user.
+ optional int32 num_fingerprints = 2;
+}
+
+/**
+ * Pulled from ProcessStatsService.java
+ */
+message ProcStats {
+ optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1;
+}
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
index ae97d7a..ae2cf74 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -67,6 +67,7 @@
data->clear();
bool result_success = true;
+ // Get the data from the Health HAL (hardware/interfaces/health/1.0/types.hal).
Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
if (r != Result::SUCCESS) {
result_success = false;
@@ -84,6 +85,12 @@
ptr->write(v.legacy.batteryFullCharge);
ptr->init();
data->push_back(ptr);
+ } else if (mTagId == android::util::BATTERY_VOLTAGE) {
+ auto ptr = make_shared<LogEvent>(android::util::BATTERY_VOLTAGE,
+ wallClockTimestampNs, elapsedTimestampNs);
+ ptr->write(v.legacy.batteryVoltage);
+ ptr->init();
+ data->push_back(ptr);
} else {
ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index d953f50..6d7bba0 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -36,8 +36,6 @@
namespace os {
namespace statsd {
-const int kLogMsgHeaderSize = 28;
-
// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
// let StatsCompanionService handle that and send the data back.
StatsCompanionServicePuller::StatsCompanionServicePuller(int tagId) : StatsPuller(tagId) {
@@ -56,20 +54,12 @@
vector<StatsLogEventWrapper> returned_value;
Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
if (!status.isOk()) {
- ALOGW("error pulling for %d", mTagId);
+ ALOGW("StatsCompanionServicePuller::pull failed to pull for %d", mTagId);
return false;
}
data->clear();
- int32_t timestampSec = getWallClockSec();
for (const StatsLogEventWrapper& it : returned_value) {
- log_msg tmp;
- tmp.entry_v1.len = it.bytes.size();
- // Manually set the header size to 28 bytes to match the pushed log events.
- tmp.entry.hdr_size = kLogMsgHeaderSize;
- tmp.entry_v1.sec = timestampSec;
- // And set the received bytes starting after the 28 bytes reserved for header.
- std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
- data->push_back(make_shared<LogEvent>(tmp));
+ data->push_back(make_shared<LogEvent>(it));
}
VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
return true;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index dbc13dc..5a0172b 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -161,6 +161,12 @@
{},
1 * NS_PER_SEC,
new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+ // battery_voltage
+ {android::util::BATTERY_VOLTAGE,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
{{4, 5, 6, 7, 8},
@@ -168,7 +174,7 @@
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
// temperature
- {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}},
+ {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
// binder_calls
{android::util::BINDER_CALLS,
{{4, 5, 6, 8, 12},
@@ -184,33 +190,30 @@
// looper_stats
{android::util::LOOPER_STATS,
{{5, 6, 7, 8, 9},
- {2, 3, 4},
+ {2, 3, 4, 10},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
// Disk Stats
{android::util::DISK_STATS,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
// Directory usage
{android::util::DIRECTORY_USAGE,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
// Size of app's code, data, and cache
{android::util::APP_SIZE,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
// Size of specific categories of files. Eg. Music.
{android::util::CATEGORY_SIZE,
{{},
{},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+ // Number of fingerprints registered to each user.
+ {android::util::NUM_FINGERPRINTS,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 33f3917..bf0bfec 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -400,36 +400,35 @@
return string(timeBuffer);
}
-void StatsdStats::dumpStats(FILE* out) const {
+void StatsdStats::dumpStats(int out) const {
lock_guard<std::mutex> lock(mLock);
time_t t = mStartTimeSec;
struct tm* tm = localtime(&t);
char timeBuffer[80];
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm);
- fprintf(out, "Stats collection start second: %s\n", timeBuffer);
- fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
+ dprintf(out, "Stats collection start second: %s\n", timeBuffer);
+ dprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
for (const auto& configStats : mIceBox) {
- fprintf(out,
+ dprintf(out,
"Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, "
"#matcher=%d, #alert=%d, valid=%d\n",
configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
configStats->deletion_time_sec, configStats->reset_time_sec,
- configStats->metric_count,
- configStats->condition_count, configStats->matcher_count, configStats->alert_count,
- configStats->is_valid);
+ configStats->metric_count, configStats->condition_count, configStats->matcher_count,
+ configStats->alert_count, configStats->is_valid);
for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
- fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
+ dprintf(out, "\tbroadcast time: %d\n", broadcastTime);
}
for (const auto& dataDropTime : configStats->data_drop_time_sec) {
- fprintf(out, "\tdata drop time: %d\n", dataDropTime);
+ dprintf(out, "\tdata drop time: %d\n", dataDropTime);
}
}
- fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
+ dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
for (auto& pair : mConfigStats) {
auto& configStats = pair.second;
- fprintf(out,
+ dprintf(out,
"Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
"#matcher=%d, #alert=%d, valid=%d\n",
configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
@@ -437,81 +436,81 @@
configStats->condition_count, configStats->matcher_count, configStats->alert_count,
configStats->is_valid);
for (const auto& annotation : configStats->annotations) {
- fprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first,
+ dprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first,
annotation.second);
}
for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
- fprintf(out, "\tbroadcast time: %s(%lld)\n",
- buildTimeString(broadcastTime).c_str(), (long long)broadcastTime);
+ dprintf(out, "\tbroadcast time: %s(%lld)\n", buildTimeString(broadcastTime).c_str(),
+ (long long)broadcastTime);
}
for (const auto& dataDropTime : configStats->data_drop_time_sec) {
- fprintf(out, "\tdata drop time: %s(%lld)\n",
- buildTimeString(dataDropTime).c_str(), (long long)dataDropTime);
+ dprintf(out, "\tdata drop time: %s(%lld)\n", buildTimeString(dataDropTime).c_str(),
+ (long long)dataDropTime);
}
for (const auto& dump : configStats->dump_report_stats) {
- fprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
+ dprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
buildTimeString(dump.first).c_str(), (long long)dump.first,
(long long)dump.second);
}
for (const auto& stats : pair.second->matcher_stats) {
- fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second);
+ dprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second);
}
for (const auto& stats : pair.second->condition_stats) {
- fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
+ dprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
stats.second);
}
for (const auto& stats : pair.second->condition_stats) {
- fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
+ dprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
stats.second);
}
for (const auto& stats : pair.second->alert_stats) {
- fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second);
+ dprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second);
}
}
- fprintf(out, "********Disk Usage stats***********\n");
+ dprintf(out, "********Disk Usage stats***********\n");
StorageManager::printStats(out);
- fprintf(out, "********Pushed Atom stats***********\n");
+ dprintf(out, "********Pushed Atom stats***********\n");
const size_t atomCounts = mPushedAtomStats.size();
for (size_t i = 2; i < atomCounts; i++) {
if (mPushedAtomStats[i] > 0) {
- fprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+ dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
}
}
- fprintf(out, "********Pulled Atom stats***********\n");
+ dprintf(out, "********Pulled Atom stats***********\n");
for (const auto& pair : mPulledAtomStats) {
- fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
+ dprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
(long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec);
}
if (mAnomalyAlarmRegisteredStats > 0) {
- fprintf(out, "********AnomalyAlarmStats stats***********\n");
- fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
+ dprintf(out, "********AnomalyAlarmStats stats***********\n");
+ dprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
}
if (mPeriodicAlarmRegisteredStats > 0) {
- fprintf(out, "********SubscriberAlarmStats stats***********\n");
- fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
+ dprintf(out, "********SubscriberAlarmStats stats***********\n");
+ dprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
}
- fprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n",
+ dprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n",
mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps,
mUidMapStats.dropped_changes);
for (const auto& restart : mSystemServerRestartSec) {
- fprintf(out, "System server restarts at %s(%lld)\n",
- buildTimeString(restart).c_str(), (long long)restart);
+ dprintf(out, "System server restarts at %s(%lld)\n", buildTimeString(restart).c_str(),
+ (long long)restart);
}
for (const auto& loss : mLogLossStats) {
- fprintf(out, "Log loss: %lld (wall clock sec) - %d (count)\n", (long long)loss.first,
+ dprintf(out, "Log loss: %lld (wall clock sec) - %d (count)\n", (long long)loss.first,
loss.second);
}
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index b5156da..a8188c8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -156,7 +156,7 @@
* Report a config has been removed.
*/
void noteConfigRemoved(const ConfigKey& key);
- /**
+ /**
* Report a config has been reset when ttl expires.
*/
void noteConfigReset(const ConfigKey& key);
@@ -202,7 +202,6 @@
*/
void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
-
/**
* Report the max size of output tuple of dimension in condition across dimensions in what.
*
@@ -272,8 +271,8 @@
void notePullFromCache(int pullAtomId);
/*
- * Records when system server restarts.
- */
+ * Records when system server restarts.
+ */
void noteSystemServerRestart(int32_t timeSec);
/**
@@ -296,9 +295,9 @@
void dumpStats(std::vector<uint8_t>* buffer, bool reset);
/**
- * Output statsd stats in human readable format to [out] file.
+ * Output statsd stats in human readable format to [out] file descriptor.
*/
- void dumpStats(FILE* out) const;
+ void dumpStats(int outFd) const;
typedef struct {
long totalPull;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index cf04ee3..f9f1b38 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,6 +41,44 @@
}
}
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+ mTagId = statsLogEventWrapper.getTagId();
+ mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
+ mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
+ mLogUid = 0;
+ for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
+ Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+ switch (statsLogEventWrapper.getElements()[i].type) {
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
+ mValues.push_back(
+ FieldValue(field, Value(statsLogEventWrapper.getElements()[i].int_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::LONG:
+ mValues.push_back(
+ FieldValue(field, Value(statsLogEventWrapper.getElements()[i].long_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::FLOAT:
+ mValues.push_back(FieldValue(
+ field, Value(statsLogEventWrapper.getElements()[i].float_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::DOUBLE:
+ mValues.push_back(FieldValue(
+ field, Value(statsLogEventWrapper.getElements()[i].double_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STRING:
+ mValues.push_back(
+ FieldValue(field, Value(statsLogEventWrapper.getElements()[i].str_value)));
+ break;
+ case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STORAGE:
+ mValues.push_back(FieldValue(
+ field, Value(statsLogEventWrapper.getElements()[i].storage_value)));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
mLogdTimestampNs = wallClockTimestampNs;
mTagId = tagId;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 2ee6bdf..9ef0bf4 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,6 +18,7 @@
#include "FieldValue.h"
+#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
#include <log/log_event_list.h>
#include <log/log_read.h>
@@ -61,6 +62,8 @@
*/
explicit LogEvent(log_msg& msg);
+ explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+
/**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
*/
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index be94725..4325f0f 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -386,12 +386,12 @@
StatsdStats::getInstance().setUidMapChanges(mChanges.size());
}
-void UidMap::printUidMap(FILE* out) const {
+void UidMap::printUidMap(int out) const {
lock_guard<mutex> lock(mMutex);
for (const auto& kv : mMap) {
if (!kv.second.deleted) {
- fprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode,
+ dprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode,
kv.first.first);
}
}
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 91f2030..4598369 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -103,7 +103,7 @@
// Helper for debugging contents of this uid map. Can be triggered with:
// adb shell cmd stats print-uid-map
- void printUidMap(FILE* out) const;
+ void printUidMap(int outFd) const;
// Commands for indicating to the map that a producer should be notified if an app is updated.
// This allows the metric producer to distinguish when the same uid or app represents a
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
new file mode 100644
index 0000000..3cd49d7
--- /dev/null
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "ShellSubscriber.h"
+
+#include "matchers/matcher_util.h"
+
+#include <android-base/file.h>
+
+using android::util::ProtoOutputStream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver) {
+ VLOG("start new shell subscription");
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mResultReceiver != nullptr) {
+ VLOG("Only one shell subscriber is allowed.");
+ return;
+ }
+ mInput = in;
+ mOutput = out;
+ mResultReceiver = resultReceiver;
+ IInterface::asBinder(mResultReceiver)->linkToDeath(this);
+ }
+
+ // Spawn another thread to read the config updates from the input file descriptor
+ std::thread reader([in, this] { readConfig(in); });
+ reader.detach();
+
+ std::unique_lock<std::mutex> lk(mMutex);
+
+ mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+ if (reader.joinable()) {
+ reader.join();
+ }
+}
+
+void ShellSubscriber::updateConfig(const ShellSubscription& config) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPushedMatchers.clear();
+ for (const auto& pushed : config.pushed()) {
+ mPushedMatchers.push_back(pushed);
+ VLOG("adding matcher for atom %d", pushed.atom_id());
+ }
+}
+
+void ShellSubscriber::readConfig(int in) {
+ if (in <= 0) {
+ return;
+ }
+
+ while (1) {
+ size_t bufferSize = 0;
+ int result = 0;
+ if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) {
+ VLOG("Done reading");
+ break;
+ } else if (result < 0 || result != sizeof(bufferSize)) {
+ ALOGE("Error reading config size");
+ break;
+ }
+
+ vector<uint8_t> buffer(bufferSize);
+ if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) {
+ ShellSubscription config;
+ if (config.ParseFromArray(buffer.data(), bufferSize)) {
+ updateConfig(config);
+ } else {
+ ALOGE("error parsing the config");
+ break;
+ }
+ } else {
+ VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize);
+ break;
+ }
+ }
+}
+
+void ShellSubscriber::cleanUpLocked() {
+ // The file descriptors will be closed by binder.
+ mInput = 0;
+ mOutput = 0;
+ mResultReceiver = nullptr;
+ mPushedMatchers.clear();
+ VLOG("done clean up");
+}
+
+void ShellSubscriber::onLogEvent(const LogEvent& event) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (mOutput <= 0) {
+ return;
+ }
+
+ for (const auto& matcher : mPushedMatchers) {
+ if (matchesSimple(*mUidMap, matcher, event)) {
+ // First write the payload size.
+ size_t bufferSize = mProto.size();
+ write(mOutput, &bufferSize, sizeof(bufferSize));
+
+ // Then write the payload.
+ event.ToProto(mProto);
+ mProto.flush(mOutput);
+ mProto.clear();
+ break;
+ }
+ }
+}
+
+void ShellSubscriber::binderDied(const wp<IBinder>& who) {
+ {
+ VLOG("Shell exits");
+ std::lock_guard<std::mutex> lock(mMutex);
+ cleanUpLocked();
+ }
+ mShellDied.notify_all();
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
new file mode 100644
index 0000000..0ace35f
--- /dev/null
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "logd/LogEvent.h"
+
+#include <android/util/ProtoOutputStream.h>
+#include <binder/IResultReceiver.h>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Handles atoms subscription via shell cmd.
+ *
+ * A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client
+ * communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms.
+ * The atoms are sent back to the client in real time, as opposed to
+ * keeping the data in memory. Shell clients do not subscribe aggregated metrics, as they are
+ * responsible for doing the aggregation after receiving the atom events.
+ *
+ * Shell client pass ShellSubscription in the proto binary format. Client can update the
+ * subscription by sending a new subscription. The new subscription would replace the old one.
+ * Input data stream format is:
+ *
+ * |size_t|subscription proto|size_t|subscription proto|....
+ *
+ * statsd sends the events back in Atom proto binary format. Each Atom message is preceded
+ * with sizeof(size_t) bytes indicating the size of the proto message payload.
+ *
+ * The stream would be in the following format:
+ * |size_t|atom1 proto|size_t|atom2 proto|....
+ *
+ * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
+ * until it exits.
+ */
+class ShellSubscriber : public virtual IBinder::DeathRecipient {
+public:
+ ShellSubscriber(sp<UidMap> uidMap) : mUidMap(uidMap){};
+
+ /**
+ * Start a new subscription.
+ */
+ void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver);
+
+ void binderDied(const wp<IBinder>& who);
+
+ void onLogEvent(const LogEvent& event);
+
+private:
+ void readConfig(int in);
+
+ void updateConfig(const ShellSubscription& config);
+
+ void cleanUpLocked();
+
+ sp<UidMap> mUidMap;
+
+ // bool mWritten = false;
+
+ android::util::ProtoOutputStream mProto;
+
+ mutable std::mutex mMutex;
+
+ std::condition_variable mShellDied; // semaphore for waiting until shell exits.
+
+ int mInput; // The input file descriptor
+
+ int mOutput; // The output file descriptor
+
+ sp<IResultReceiver> mResultReceiver;
+
+ std::vector<SimpleAtomMatcher> mPushedMatchers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
new file mode 100644
index 0000000..516693d
--- /dev/null
+++ b/cmds/statsd/src/shell/shell_config.proto
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+
+option java_package = "com.android.os";
+option java_outer_classname = "ShellConfig";
+
+import "frameworks/base/cmds/statsd/src/statsd_config.proto";
+
+message PulledAtomSubscription {
+ optional int32 atom_id = 1;
+
+ /* gap between two pulls in milliseconds */
+ optional int32 freq_millis = 2;
+}
+
+message ShellSubscription {
+ repeated SimpleAtomMatcher pushed = 1;
+ repeated PulledAtomSubscription pulled = 2;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a0ab3e4..805e583 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -322,6 +322,11 @@
case STRING:
protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
break;
+ case STORAGE:
+ protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+ (const char*)dim.mValue.storage_value.data(),
+ dim.mValue.storage_value.size());
+ break;
default:
break;
}
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 3ebc8a4..2f19a02 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -392,13 +392,13 @@
}
}
-void StorageManager::printStats(FILE* out) {
- printDirStats(out, STATS_SERVICE_DIR);
- printDirStats(out, STATS_DATA_DIR);
+void StorageManager::printStats(int outFd) {
+ printDirStats(outFd, STATS_SERVICE_DIR);
+ printDirStats(outFd, STATS_DATA_DIR);
}
-void StorageManager::printDirStats(FILE* out, const char* path) {
- fprintf(out, "Printing stats of %s\n", path);
+void StorageManager::printDirStats(int outFd, const char* path) {
+ dprintf(outFd, "Printing stats of %s\n", path);
unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", path);
@@ -418,25 +418,22 @@
int64_t timestamp = result[0];
int64_t uid = result[1];
int64_t configID = result[2];
- fprintf(out, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld",
- fileCount + 1,
- (long long)timestamp,
- (int)uid,
- (long long)configID);
+ dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld", fileCount + 1,
+ (long long)timestamp, (int)uid, (long long)configID);
string file_name = getFilePath(path, timestamp, uid, configID);
ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
if (file.is_open()) {
file.seekg(0, ios::end);
int fileSize = file.tellg();
file.close();
- fprintf(out, ", File Size: %d bytes", fileSize);
+ dprintf(outFd, ", File Size: %d bytes", fileSize);
totalFileSize += fileSize;
}
- fprintf(out, "\n");
+ dprintf(outFd, "\n");
fileCount++;
}
- fprintf(out, "\tTotal number of files: %d, Total size of files: %d bytes.\n",
- fileCount, totalFileSize);
+ dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
+ totalFileSize);
}
} // namespace statsd
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 4840f3c..8fbc89e 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -100,13 +100,13 @@
/**
* Prints disk usage statistics related to statsd.
*/
- static void printStats(FILE* out);
+ static void printStats(int out);
private:
/**
* Prints disk usage statistics about a directory related to statsd.
*/
- static void printDirStats(FILE* out, const char* path);
+ static void printDirStats(int out, const char* path);
};
} // namespace statsd
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 950a258..455e4bb 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -84,7 +84,8 @@
cursor.close();
}
if (provider != null) {
- activityManager.removeContentProviderExternal(providerName, token);
+ activityManager.removeContentProviderExternalAsUser(providerName, token,
+ UserHandle.USER_SYSTEM);
}
}
} catch (RemoteException e) {
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index b683138..be9ccec 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -11067,6 +11067,7 @@
HPLjava/lang/Long;->valueOf(Ljava/lang/String;I)Ljava/lang/Long;
HPLjava/lang/Math;->addExact(JJ)J
HPLjava/lang/Math;->scalb(FI)F
+HPLjava/lang/Object;->wait()V
HPLjava/lang/StackTraceElement;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
HPLjava/lang/String;->copyValueOf([C)Ljava/lang/String;
HPLjava/lang/String;->join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 5d37c5f..0472461 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2134,6 +2134,7 @@
Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V
Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V
Lcom/android/internal/telephony/SmsMessageBase;-><init>()V
+Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String;
Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
Lcom/android/internal/view/BaseIWindow;-><init>()V
@@ -2222,17 +2223,11 @@
Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V
Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostname()Ljava/lang/String;
-Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostnameOrIP()Ljava/lang/String;
-Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I
-Lcom/android/org/conscrypt/ConscryptSocketBase;->setHandshakeTimeout(I)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->setHostname(Ljava/lang/String;)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->setSoWriteTimeout(I)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->socket:Ljava/net/Socket;
Lcom/android/org/conscrypt/OpenSSLKey;-><init>(J)V
Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey;
Lcom/android/org/conscrypt/OpenSSLKey;->getNativeRef()Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;
Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey()Ljava/security/PublicKey;
+Lcom/android/org/conscrypt/OpenSSLProvider;-><init>()V
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
@@ -2254,6 +2249,108 @@
Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateChain(Ljava/security/cert/X509Certificate;)Ljava/util/List;
Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V
Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
+Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/MmsException;-><init>()V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
+Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
+Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
+Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
+Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
+Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
+Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
+Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
+Lcom/google/android/mms/pdu/PduBody;-><init>()V
+Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
+Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
+Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
+Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
+Lcom/google/android/mms/pdu/PduComposer;->make()[B
+Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPart;-><init>()V
+Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPart;->getCharset()I
+Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
+Lcom/google/android/mms/pdu/PduPart;->getData()[B
+Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
+Lcom/google/android/mms/pdu/PduPart;->getName()[B
+Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
+Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
+Lcom/google/android/mms/pdu/PduPart;->setData([B)V
+Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
+Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
+Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
+Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
+Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
+Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
+Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
+Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;-><init>()V
+Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
+Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
+Lcom/google/android/mms/util/PduCache;->purgeAll()V
+Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
@@ -2560,6 +2657,7 @@
Ljava/net/SocketException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
+Ljava/net/SocksSocketImpl;-><init>()V
Ljava/net/URI;->fragment:Ljava/lang/String;
Ljava/net/URI;->host:Ljava/lang/String;
Ljava/net/URI;->port:I
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 50e97c5..63c583f 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1436,6 +1436,12 @@
android.hardware.usb.UsbDeviceConnection
android.hardware.usb.UsbManager
android.hardware.usb.UsbRequest
+android.hidl.base.V1_0.DebugInfo
+android.hidl.base.V1_0.IBase
+android.hidl.manager.V1_0.IServiceManager
+android.hidl.manager.V1_0.IServiceManager$Proxy
+android.hidl.manager.V1_0.IServiceNotification
+android.hidl.manager.V1_0.IServiceNotification$Stub
android.icu.impl.BMPSet
android.icu.impl.CacheBase
android.icu.impl.CacheValue
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 446e6cc..f2ad268 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Region;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -73,7 +74,7 @@
* follows the established service life cycle. Starting an accessibility service is triggered
* exclusively by the user explicitly turning the service on in device settings. After the system
* binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can
- * be overriden by clients that want to perform post binding setup.
+ * be overridden by clients that want to perform post binding setup.
* </p>
* <p>
* An accessibility service stops either when the user turns it off in device settings or when
@@ -446,7 +447,7 @@
@UnsupportedAppUsage
private AccessibilityServiceInfo mInfo;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private IBinder mWindowToken;
private WindowManager mWindowManager;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 759443d..aa0275a 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -314,6 +314,12 @@
*/
public static final int FLAG_REQUEST_FINGERPRINT_GESTURES = 0x00000200;
+ /**
+ * This flag requests that accessibility shortcut warning dialog has spoken feedback when
+ * dialog is shown.
+ */
+ public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 0x00000400;
+
/** {@hide} */
public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
@@ -414,6 +420,7 @@
* @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS
* @see #FLAG_ENABLE_ACCESSIBILITY_VOLUME
* @see #FLAG_REQUEST_ACCESSIBILITY_BUTTON
+ * @see #FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK
*/
public int flags;
@@ -1050,6 +1057,8 @@
return "FLAG_REQUEST_ACCESSIBILITY_BUTTON";
case FLAG_REQUEST_FINGERPRINT_GESTURES:
return "FLAG_REQUEST_FINGERPRINT_GESTURES";
+ case FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK:
+ return "FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK";
default:
return null;
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7b86646..2ee266d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -545,12 +545,12 @@
* <a name="SavingPersistentState"></a>
* <h3>Saving Persistent State</h3>
*
- * <p>There are generally two kinds of persistent state than an activity
+ * <p>There are generally two kinds of persistent state that an activity
* will deal with: shared document-like data (typically stored in a SQLite
* database using a {@linkplain android.content.ContentProvider content provider})
* and internal state such as user preferences.</p>
*
- * <p>For content provider data, we suggest that activities use a
+ * <p>For content provider data, we suggest that activities use an
* "edit in place" user model. That is, any edits a user makes are effectively
* made immediately without requiring an additional confirmation step.
* Supporting this model is generally a simple matter of following two rules:</p>
@@ -847,7 +847,7 @@
/*package*/ ActionBar mActionBar = null;
private boolean mEnableDefaultActionBarUp;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private VoiceInteractor mVoiceInteractor;
@UnsupportedAppUsage
@@ -1383,6 +1383,7 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
+ enableAutofillCompatibilityIfNeeded();
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
@@ -3523,7 +3524,7 @@
* {@link android.view.Window#FEATURE_OPTIONS_PANEL} panel,
* so that subclasses of Activity don't need to deal with feature codes.
*/
- public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) {
if (featureId == Window.FEATURE_OPTIONS_PANEL) {
boolean show = onCreateOptionsMenu(menu);
show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
@@ -3541,8 +3542,8 @@
* panel, so that subclasses of
* Activity don't need to deal with feature codes.
*/
- public boolean onPreparePanel(int featureId, View view, Menu menu) {
- if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
+ public boolean onPreparePanel(int featureId, @Nullable View view, @NonNull Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
boolean goforit = onPrepareOptionsMenu(menu);
goforit |= mFragments.dispatchPrepareOptionsMenu(menu);
return goforit;
@@ -3555,7 +3556,8 @@
*
* @return The default implementation returns true.
*/
- public boolean onMenuOpened(int featureId, Menu menu) {
+ @Override
+ public boolean onMenuOpened(int featureId, @NonNull Menu menu) {
if (featureId == Window.FEATURE_ACTION_BAR) {
initWindowDecorActionBar();
if (mActionBar != null) {
@@ -3576,7 +3578,7 @@
* panel, so that subclasses of
* Activity don't need to deal with feature codes.
*/
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ public boolean onMenuItemSelected(int featureId, @NonNull MenuItem item) {
CharSequence titleCondensed = item.getTitleCondensed();
switch (featureId) {
@@ -3626,7 +3628,7 @@
* For context menus ({@link Window#FEATURE_CONTEXT_MENU}), the
* {@link #onContextMenuClosed(Menu)} will be called.
*/
- public void onPanelClosed(int featureId, Menu menu) {
+ public void onPanelClosed(int featureId, @NonNull Menu menu) {
switch (featureId) {
case Window.FEATURE_OPTIONS_PANEL:
mFragments.dispatchOptionsMenuClosed(menu);
@@ -3734,7 +3736,7 @@
*
* @see #onCreateOptionsMenu
*/
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (mParent != null) {
return mParent.onOptionsItemSelected(item);
}
@@ -3958,7 +3960,7 @@
* @return boolean Return false to allow normal context menu processing to
* proceed, true to consume it here.
*/
- public boolean onContextItemSelected(MenuItem item) {
+ public boolean onContextItemSelected(@NonNull MenuItem item) {
if (mParent != null) {
return mParent.onContextItemSelected(item);
}
@@ -3972,7 +3974,7 @@
*
* @param menu The context menu that is being closed.
*/
- public void onContextMenuClosed(Menu menu) {
+ public void onContextMenuClosed(@NonNull Menu menu) {
if (mParent != null) {
mParent.onContextMenuClosed(menu);
}
@@ -6385,11 +6387,13 @@
* closed for you after you return.
* @param args additional arguments to the dump request.
*/
- public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ public void dump(@NonNull String prefix, @Nullable FileDescriptor fd,
+ @NonNull PrintWriter writer, @Nullable String[] args) {
dumpInner(prefix, fd, writer, args);
}
- void dumpInner(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
+ @NonNull PrintWriter writer, @Nullable String[] args) {
if (args != null && args.length > 0 && args[0].equals("--autofill")) {
dumpAutofillManager(prefix, writer);
return;
@@ -7097,7 +7101,7 @@
// ------------------ Internal API ------------------
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final void setParent(Activity parent) {
mParent = parent;
}
@@ -7162,7 +7166,6 @@
mWindow.setColorMode(info.colorMode);
setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
- enableAutofillCompatibilityIfNeeded();
}
private void enableAutofillCompatibilityIfNeeded() {
@@ -7208,7 +7211,7 @@
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
}
- final void performNewIntent(Intent intent) {
+ final void performNewIntent(@NonNull Intent intent) {
mCanEnterPictureInPicture = true;
onNewIntent(intent);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8354235..055a91e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -776,7 +776,7 @@
/** @hide */
public int getPackageScreenCompatMode(String packageName) {
try {
- return getService().getPackageScreenCompatMode(packageName);
+ return getTaskService().getPackageScreenCompatMode(packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -785,7 +785,7 @@
/** @hide */
public void setPackageScreenCompatMode(String packageName, int mode) {
try {
- getService().setPackageScreenCompatMode(packageName, mode);
+ getTaskService().setPackageScreenCompatMode(packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -794,7 +794,7 @@
/** @hide */
public boolean getPackageAskScreenCompat(String packageName) {
try {
- return getService().getPackageAskScreenCompat(packageName);
+ return getTaskService().getPackageAskScreenCompat(packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -803,7 +803,7 @@
/** @hide */
public void setPackageAskScreenCompat(String packageName, boolean ask) {
try {
- getService().setPackageAskScreenCompat(packageName, ask);
+ getTaskService().setPackageAskScreenCompat(packageName, ask);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1221,7 +1221,7 @@
* @return The background color.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getBackgroundColor() {
return mColorBackground;
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 87366db..be1f2db 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -183,12 +184,6 @@
/** Trims memory usage in the system by removing/stopping unused application processes. */
public abstract void trimApplications();
- /** Returns the screen compatibility mode for the given application. */
- public abstract int getPackageScreenCompatMode(ApplicationInfo ai);
-
- /** Sets the screen compatibility mode for the given application. */
- public abstract void setPackageScreenCompatMode(ApplicationInfo ai, int mode);
-
/** Closes all system dialogs. */
public abstract void closeSystemDialogs(String reason);
@@ -201,6 +196,11 @@
public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
public abstract void updateOomAdj();
+ public abstract void updateCpuStats();
+ public abstract void updateUsageStats(
+ ComponentName activity, int uid, int userId, boolean resumed);
+ public abstract void updateForegroundTimeIfOnBattery(
+ String packageName, int uid, long cpuTimeDiff);
public abstract void sendForegroundProfileChanged(int userId);
/**
@@ -226,4 +226,10 @@
/** Gets the task id for a given activity. */
public abstract int getTaskIdForActivity(@NonNull IBinder token, boolean onlyRoot);
+
+ public abstract void setBooting(boolean booting);
+ public abstract boolean isBooting();
+ public abstract void setBooted(boolean booted);
+ public abstract boolean isBooted();
+ public abstract void finishBooting();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0e884d6..31e9a2d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -340,7 +340,7 @@
// An executor that performs multi-step transactions.
private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final ResourcesManager mResourcesManager;
private static final class ProviderKey {
@@ -374,7 +374,7 @@
@UnsupportedAppUsage
final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
= new ArrayMap<IBinder, ProviderRefCount>();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
= new ArrayMap<IBinder, ProviderClientRecord>();
@UnsupportedAppUsage
@@ -394,7 +394,7 @@
final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
static volatile Handler sMainThreadHandler; // set once in main()
Bundle mCoreSettings = null;
@@ -456,7 +456,7 @@
private int mLifecycleState = PRE_ON_CREATE;
@VisibleForTesting
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public ActivityClientRecord() {
this.isForward = false;
init();
@@ -704,7 +704,7 @@
@UnsupportedAppUsage
boolean persistent;
Configuration config;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
CompatibilityInfo compatInfo;
String buildSerial;
@@ -2088,7 +2088,7 @@
return mH;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
int flags) {
return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
@@ -2177,7 +2177,7 @@
return getPackageInfo(ai, compatInfo, null, false, true, false);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
@@ -2850,7 +2850,7 @@
return aInfo;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
Activity.NonConfigurationInstances lastNonConfigurationInstances) {
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index dbc8c5d..cf40e06 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -397,7 +397,7 @@
* @param listener The {@link DialogInterface.OnClickListener} to use.
* @deprecated Use
* {@link #setButton(int, CharSequence, android.content.DialogInterface.OnClickListener)}
- * with {@link DialogInterface#BUTTON_POSITIVE}
+ * with {@link DialogInterface#BUTTON_NEUTRAL}
*/
@Deprecated
public void setButton3(CharSequence text, final OnClickListener listener) {
diff --git a/core/java/android/app/ContentProviderHolder.java b/core/java/android/app/ContentProviderHolder.java
index 1098990..2a13c71 100644
--- a/core/java/android/app/ContentProviderHolder.java
+++ b/core/java/android/app/ContentProviderHolder.java
@@ -20,6 +20,7 @@
import android.content.ContentProviderNative;
import android.content.IContentProvider;
import android.content.pm.ProviderInfo;
+import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,7 +36,7 @@
@UnsupportedAppUsage
public IContentProvider provider;
public IBinder connection;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean noReleaseNeeded;
@UnsupportedAppUsage
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bbaaee8..77f6395 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -164,7 +164,7 @@
* Map from preference name to generated path.
*/
@GuardedBy("ContextImpl.class")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private ArrayMap<String, File> mSharedPrefsPaths;
@UnsupportedAppUsage
@@ -183,7 +183,7 @@
@UnsupportedAppUsage
private final String mBasePackageName;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mOpPackageName;
private final @NonNull ResourcesManager mResourcesManager;
@@ -191,7 +191,7 @@
private @NonNull Resources mResources;
private @Nullable Display mDisplay; // may be null if default display
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mFlags;
@UnsupportedAppUsage
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 37a05f0..9d82ffa 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -239,7 +239,7 @@
* @param year the selected year
* @param month the selected month (0-11 for compatibility with
* {@link Calendar#MONTH})
- * @param dayOfMonth th selected day of the month (1-31, depending on
+ * @param dayOfMonth the selected day of the month (1-31, depending on
* month)
*/
void onDateSet(DatePicker view, int year, int month, int dayOfMonth);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index b7bac52..6bcfb2e 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -34,6 +34,7 @@
import android.content.res.ResourceId;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -133,7 +134,7 @@
private final Handler mHandler = new Handler();
private static final int DISMISS = 0x43;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static final int CANCEL = 0x44;
private static final int SHOW = 0x45;
@@ -936,8 +937,8 @@
* @see Activity#onPreparePanel(int, View, Menu)
*/
@Override
- public boolean onPreparePanel(int featureId, View view, Menu menu) {
- if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
+ public boolean onPreparePanel(int featureId, @Nullable View view, @NonNull Menu menu) {
+ if (featureId == Window.FEATURE_OPTIONS_PANEL) {
return onPrepareOptionsMenu(menu) && menu.hasVisibleItems();
}
return true;
@@ -947,7 +948,7 @@
* @see Activity#onMenuOpened(int, Menu)
*/
@Override
- public boolean onMenuOpened(int featureId, Menu menu) {
+ public boolean onMenuOpened(int featureId, @NonNull Menu menu) {
if (featureId == Window.FEATURE_ACTION_BAR) {
mActionBar.dispatchMenuVisibilityChanged(true);
}
@@ -958,7 +959,7 @@
* @see Activity#onMenuItemSelected(int, MenuItem)
*/
@Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ public boolean onMenuItemSelected(int featureId, @NonNull MenuItem item) {
return false;
}
@@ -966,7 +967,7 @@
* @see Activity#onPanelClosed(int, Menu)
*/
@Override
- public void onPanelClosed(int featureId, Menu menu) {
+ public void onPanelClosed(int featureId, @NonNull Menu menu) {
if (featureId == Window.FEATURE_ACTION_BAR) {
mActionBar.dispatchMenuVisibilityChanged(false);
}
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 35999a2..1622c06 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -879,7 +879,7 @@
* @return this object
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public Query orderBy(String column, int direction) {
if (direction != ORDER_ASCENDING && direction != ORDER_DESCENDING) {
throw new IllegalArgumentException("Invalid direction: " + direction);
@@ -1011,7 +1011,7 @@
}
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void setAccessFilename(boolean accessFilename) {
mAccessFilename = accessFilename;
}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 9f4157f..49917b4 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -264,7 +264,7 @@
*/
@Deprecated
public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static final ArrayMap<String, Class<?>> sClassMap =
new ArrayMap<String, Class<?>>();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 16360b3..c0c8b79 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -248,10 +248,7 @@
boolean runGc, in String path, in ParcelFileDescriptor fd,
in RemoteCallback finishCallback);
boolean isUserRunning(int userid, int flags);
- int getPackageScreenCompatMode(in String packageName);
void setPackageScreenCompatMode(in String packageName, int mode);
- boolean getPackageAskScreenCompat(in String packageName);
- void setPackageAskScreenCompat(in String packageName, boolean ask);
boolean switchUser(int userid);
boolean removeTask(int taskId);
void registerProcessObserver(in IProcessObserver observer);
@@ -263,7 +260,9 @@
void killAllBackgroundProcesses();
ContentProviderHolder getContentProviderExternal(in String name, int userId,
in IBinder token, String tag);
+ /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
void removeContentProviderExternal(in String name, in IBinder token);
+ void removeContentProviderExternalAsUser(in String name, in IBinder token, int userId);
// Get memory information about the calling process.
void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
boolean killProcessesBelowForeground(in String reason);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 46664c6..abd1cca 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -422,4 +422,9 @@
void resumeAppSwitches();
void setActivityController(in IActivityController watcher, boolean imAMonkey);
void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake);
+
+ int getPackageScreenCompatMode(in String packageName);
+ void setPackageScreenCompatMode(in String packageName, int mode);
+ boolean getPackageAskScreenCompat(in String packageName);
+ void setPackageAskScreenCompat(in String packageName, boolean ask);
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 3085fe5..b720df8 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -29,6 +29,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -415,7 +416,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean isDeviceLocked(int userId) {
try {
return mTrustManager.isDeviceLocked(userId);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ddd9441..b827d01 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -111,7 +111,7 @@
private String mDataDir;
@UnsupportedAppUsage
private String mLibDir;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private File mDataDirFile;
private File mDeviceProtectedDataDirFile;
private File mCredentialProtectedDataDirFile;
@@ -141,7 +141,7 @@
= new ArrayMap<>();
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
= new ArrayMap<>();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
@@ -1358,7 +1358,7 @@
final IIntentReceiver.Stub mIIntentReceiver;
@UnsupportedAppUsage
final BroadcastReceiver mReceiver;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final Context mContext;
final Handler mActivityThread;
final Instrumentation mInstrumentation;
@@ -1605,7 +1605,7 @@
private final ServiceDispatcher.InnerConnection mIServiceConnection;
@UnsupportedAppUsage
private final ServiceConnection mConnection;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final Context mContext;
private final Handler mActivityThread;
private final ServiceConnectionLeaked mLocation;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9dca061..3638bc4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1417,7 +1417,7 @@
public static final int SEMANTIC_ACTION_CALL = 10;
private final Bundle mExtras;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Icon mIcon;
private final RemoteInput[] mRemoteInputs;
private boolean mAllowGeneratedReplies = true;
@@ -3499,7 +3499,7 @@
/**
* Set the small icon, which will be used to represent the notification in the
- * status bar and content view (unless overriden there by a
+ * status bar and content view (unless overridden there by a
* {@link #setLargeIcon(Bitmap) large icon}).
*
* @param icon An Icon object to use.
@@ -8253,7 +8253,10 @@
* <p>For custom display notifications created using {@link #setDisplayIntent},
* the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
* on their content.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_DEFAULT = 0;
/**
@@ -8261,7 +8264,10 @@
* with an extra small size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_XSMALL = 1;
/**
@@ -8269,7 +8275,10 @@
* with a small size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_SMALL = 2;
/**
@@ -8277,7 +8286,10 @@
* with a medium size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_MEDIUM = 3;
/**
@@ -8285,7 +8297,10 @@
* with a large size.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_LARGE = 4;
/**
@@ -8293,20 +8308,29 @@
* full screen.
* <p>This value is only applicable for custom display notifications created using
* {@link #setDisplayIntent}.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public static final int SIZE_FULL_SCREEN = 5;
/**
* Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
* short amount of time when this notification is displayed on the screen. This
* is the default value.
+ *
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public static final int SCREEN_TIMEOUT_SHORT = 0;
/**
* Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
* for a longer amount of time when this notification is displayed on the screen.
+ *
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public static final int SCREEN_TIMEOUT_LONG = -1;
/** Notification extra which contains wearable extensions */
@@ -8556,7 +8580,9 @@
* @param intent the {@link PendingIntent} for an activity
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getDisplayIntent
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public WearableExtender setDisplayIntent(PendingIntent intent) {
mDisplayIntent = intent;
return this;
@@ -8565,7 +8591,10 @@
/**
* Get the intent to launch inside of an activity view when displaying this
* notification. This {@code PendingIntent} should be for an activity.
+ *
+ * @deprecated Display intents are no longer supported.
*/
+ @Deprecated
public PendingIntent getDisplayIntent() {
return mDisplayIntent;
}
@@ -8579,7 +8608,9 @@
* @param page the notification to add as another page
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getPages
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public WearableExtender addPage(Notification page) {
mPages.add(page);
return this;
@@ -8594,7 +8625,9 @@
* @param pages a list of notifications
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getPages
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public WearableExtender addPages(List<Notification> pages) {
mPages.addAll(pages);
return this;
@@ -8604,7 +8637,9 @@
* Clear all additional pages present on this builder.
* @return this object for method chaining.
* @see #addPage
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public WearableExtender clearPages() {
mPages.clear();
return this;
@@ -8616,7 +8651,9 @@
* subsequent pages. This field can be used to separate a notification into multiple
* sections.
* @return the pages for this notification
+ * @deprecated Multiple content pages are no longer supported.
*/
+ @Deprecated
public List<Notification> getPages() {
return mPages;
}
@@ -8629,7 +8666,9 @@
* @param background the background bitmap
* @return this object for method chaining
* @see android.app.Notification.WearableExtender#getBackground
+ * @deprecated Background images are no longer supported.
*/
+ @Deprecated
public WearableExtender setBackground(Bitmap background) {
mBackground = background;
return this;
@@ -8642,7 +8681,9 @@
*
* @return the background image
* @see android.app.Notification.WearableExtender#setBackground
+ * @deprecated Background images are no longer supported.
*/
+ @Deprecated
public Bitmap getBackground() {
return mBackground;
}
@@ -8688,15 +8729,11 @@
}
/**
- * Set an action from this notification's actions to be clickable with the content of
- * this notification. This action will no longer display separately from the
- * notification's content.
+ * Set an action from this notification's actions as the primary action. If the action has a
+ * {@link RemoteInput} associated with it, shortcuts to the options for that input are shown
+ * directly on the notification.
*
- * <p>For notifications with multiple pages, child pages can also have content actions
- * set, although the list of available actions comes from the main notification and not
- * from the child page's notification.
- *
- * @param actionIndex The index of the action to hoist onto the current notification page.
+ * @param actionIndex The index of the primary action.
* If wearable actions were added to the main notification, this index
* will apply to that list, otherwise it will apply to the regular
* actions list.
@@ -8707,13 +8744,8 @@
}
/**
- * Get the index of the notification action, if any, that was specified as being clickable
- * with the content of this notification. This action will no longer display separately
- * from the notification's content.
- *
- * <p>For notifications with multiple pages, child pages can also have content actions
- * set, although the list of available actions comes from the main notification and not
- * from the child page's notification.
+ * Get the index of the notification action, if any, that was specified as the primary
+ * action.
*
* <p>If wearable specific actions were added to the main notification, this index will
* apply to that list, otherwise it will apply to the regular actions list.
@@ -8938,7 +8970,9 @@
* qr codes, as well as other simple black-and-white tickets.
* @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
* @return this object for method chaining
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
return this;
@@ -8950,7 +8984,9 @@
* qr codes, as well as other simple black-and-white tickets.
* @return {@code true} if it should be displayed in ambient, false otherwise
* otherwise. The default value is {@code false} if this was never set.
+ * @deprecated This feature is no longer supported.
*/
+ @Deprecated
public boolean getHintAmbientBigPicture() {
return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index dbb6c3d..c0903b6 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -29,6 +29,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -193,7 +194,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public UiAutomation(Looper looper, IUiAutomationConnection connection) {
if (looper == null) {
throw new IllegalArgumentException("Looper cannot be null!");
@@ -210,7 +211,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void connect() {
connect(0);
}
@@ -282,7 +283,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void disconnect() {
synchronized (mLock) {
if (mIsConnecting) {
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4f172a4..e532ece 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -25,7 +25,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -214,7 +213,7 @@
}
/**
- * Sets the night mode.
+ * Sets the system-wide night mode.
* <p>
* The mode can be one of:
* <ul>
@@ -231,6 +230,12 @@
* are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
* or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
* device. Starting in API 23, changes to night mode are always effective.
+ * <p>
+ * Changes to night mode take effect globally and will result in a configuration change
+ * (and potentially an Activity lifecycle event) being applied to all running apps.
+ * Developers interested in an app-local implementation of night mode should consider using
+ * {@link androidx.appcompat.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the
+ * -night qualifier locally.
*
* @param mode the night mode to set
* @see #getNightMode()
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 2a263d6..bebe79e 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1288,7 +1288,7 @@
* requires permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int setBitmap(Bitmap fullImage, Rect visibleCropHint,
boolean allowBackup, @SetWallpaperFlags int which, int userId)
throws IOException {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bd7a2dd..fc67c10 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3450,15 +3450,14 @@
* @param flags Bit mask of additional options: currently supported flags are
* {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
* @param reason a string that contains the reason for wiping data, which can be
- * presented to the user.
+ * presented to the user. If the string is null or empty, user won't be notified.
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
* @throws IllegalArgumentException if the input reason string is null or empty.
*/
- public void wipeData(int flags, @NonNull CharSequence reason) {
+ public void wipeData(int flags, CharSequence reason) {
throwIfParentInstance("wipeData");
- Preconditions.checkNotNull(reason, "CharSequence is null");
- wipeDataInternal(flags, reason.toString());
+ wipeDataInternal(flags, reason != null ? reason.toString() : null);
}
/**
@@ -3469,7 +3468,7 @@
* @see #wipeData(int, CharSequence)
* @hide
*/
- private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
+ private void wipeDataInternal(int flags, String wipeReasonForUser) {
if (mService != null) {
try {
mService.wipeDataWithReason(flags, wipeReasonForUser);
diff --git a/core/java/android/app/backup/package.html b/core/java/android/app/backup/package.html
index 8b5e3ba..dd6c254 100644
--- a/core/java/android/app/backup/package.html
+++ b/core/java/android/app/backup/package.html
@@ -11,7 +11,7 @@
<p>All backup and restore operations are controlled by the {@link
android.app.backup.BackupManager}. Each application that would
-like to enable backup and preserve its data on remote strage must implement a
+like to enable backup and preserve its data on remote storage must implement a
backup agent. A backup agent can be built by extending either {@link android.app.backup.BackupAgent}
or {@link android.app.backup.BackupAgentHelper}. The {@link
android.app.backup.BackupAgentHelper} class provides a wrapper around {@link
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 9baa16f..556ffa24 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -36,6 +36,7 @@
import android.net.NetworkSpecifier;
import android.net.Uri;
import android.os.BaseBundle;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -311,7 +312,7 @@
private final long initialBackoffMillis;
private final int backoffPolicy;
private final int priority;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int flags;
/**
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 0994332..dbb00eb 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -25,6 +25,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -235,7 +236,7 @@
@UnsupportedAppUsage
private static final UsageEvents sEmptyResults = new UsageEvents();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final Context mContext;
@UnsupportedAppUsage
private final IUsageStatsManager mService;
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d21f76d..466b9ce 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -571,7 +572,7 @@
* @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
try {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4c655b5..654bfaf 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -81,7 +81,8 @@
* {@link #getBondedDevices()}; start device discovery with
* {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
* listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
+ * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for
* Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
* </p>
* <p>This class is thread safe.</p>
@@ -2967,7 +2968,7 @@
/**
* Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
* assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
- * for incoming connections.
+ * for incoming connections. The supported Bluetooth transport is LE only.
* <p>A remote device connecting to this socket will be authenticated and communication on this
* socket will be encrypted.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
@@ -2977,21 +2978,16 @@
* closed, Bluetooth is turned off, or the application exits unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
* defined and performed by the application.
- * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
+ * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server
* socket from another Android device that is given the PSM value.
*
- * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
* @return an L2CAP CoC BluetoothServerSocket
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
- * @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothServerSocket listenUsingL2capCoc(int transport)
+ public BluetoothServerSocket listenUsingL2capChannel()
throws IOException {
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3005,7 +3001,7 @@
throw new IOException("Error: Unable to assign PSM value");
}
if (DBG) {
- Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
+ Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to "
+ assignedPsm);
}
socket.setChannel(assignedPsm);
@@ -3014,10 +3010,23 @@
}
/**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, listenUsingL2capChannel.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingL2capCoc(int transport)
+ throws IOException {
+ Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel");
+ return listenUsingL2capChannel();
+ }
+
+ /**
* Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
- * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
+ * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
+ * supported Bluetooth transport is LE only.
* <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
- * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
+ * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
* authenticated communication channel is desired.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
* {@link BluetoothServerSocket}.
@@ -3027,21 +3036,16 @@
* unexpectedly.
* <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
* defined and performed by the application.
- * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
- * server socket from another Android device that is given the PSM value.
+ * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server
+ * socket from another Android device that is given the PSM value.
*
- * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
* @return an L2CAP CoC BluetoothServerSocket
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
- * @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+ public BluetoothServerSocket listenUsingInsecureL2capChannel()
throws IOException {
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3055,11 +3059,24 @@
throw new IOException("Error: Unable to assign PSM value");
}
if (DBG) {
- Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
+ Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to "
+ assignedPsm);
}
socket.setChannel(assignedPsm);
return socket;
}
+
+ /**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, listenUsingInsecureL2capChannel.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+ throws IOException {
+ Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, "
+ + "listenUsingInsecureL2capChannel");
+ return listenUsingInsecureL2capChannel();
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 818a749..73e98cd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1963,8 +1963,8 @@
/**
* Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
* be used to start a secure outgoing connection to the remote device with the same dynamic
- * protocol/service multiplexer (PSM) value.
- * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
+ * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+ * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for
* peer-peer Bluetooth applications.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
* <p>Application using this API is responsible for obtaining PSM value from remote device.
@@ -1975,59 +1975,71 @@
* secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
* int)}.
*
- * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
* @param psm dynamic PSM value from remote device
* @return a CoC #BluetoothSocket ready for an outgoing connection
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
- * @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+ public BluetoothSocket createL2capChannel(int psm) throws IOException {
if (!isBluetoothEnabled()) {
- Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
+ Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
throw new IOException();
}
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
- if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
+ if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm);
return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
null);
}
/**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, createL2capChannel.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+ Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel");
+ return createL2capChannel(psm);
+ }
+
+ /**
* Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
* be used to start a secure outgoing connection to the remote device with the same dynamic
- * protocol/service multiplexer (PSM) value.
- * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
- * for peer-peer Bluetooth applications.
+ * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+ * <p>This is designed to be used with {@link
+ * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications.
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
* <p>Application using this API is responsible for obtaining PSM value from remote device.
* <p> The communication channel may not have an authenticated link key, i.e. it may be subject
- * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
+ * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
* authenticated communication channel is possible.
*
- * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
* @param psm dynamic PSM value from remote device
* @return a CoC #BluetoothSocket ready for an outgoing connection
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
+ if (!isBluetoothEnabled()) {
+ Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
+ throw new IOException();
+ }
+ if (DBG) {
+ Log.d(TAG, "createInsecureL2capChannel: psm=" + psm);
+ }
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
+ null);
+ }
+
+ /**
+ * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+ * API name, createInsecureL2capChannel.
* @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
- if (!isBluetoothEnabled()) {
- Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
- throw new IOException();
- }
- if (transport != BluetoothDevice.TRANSPORT_LE) {
- throw new IllegalArgumentException("Unsupported transport: " + transport);
- }
- if (DBG) {
- Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
- }
- return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
- null);
+ Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel");
+ return createInsecureL2capChannel(psm);
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 78248ef..29d5a1c 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -52,7 +53,7 @@
private BluetoothDevice mDevice;
@UnsupportedAppUsage
private boolean mAutoConnect;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mAuthRetryState;
private int mConnState;
private final Object mStateLock = new Object();
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 9777b5c..3c3a01b 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -21,6 +21,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import java.util.List;
@@ -86,7 +87,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int PAN = 5;
/**
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ba4b5a5..5fc344a 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -203,12 +203,11 @@
/**
* Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
* Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
- * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
- * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
+ * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link
+ * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this
* method is called on non-L2CAP server sockets.
*
* @return the assigned PSM or LE_PSM value depending on transport
- * @hide
*/
public int getPsm() {
return mChannel;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 780f896..3a1e2f5 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -667,6 +667,10 @@
* @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
*/
public int getConnectionType() {
+ if (mType == TYPE_L2CAP_LE) {
+ // Treat the LE CoC to be the same type as L2CAP.
+ return TYPE_L2CAP;
+ }
return mType;
}
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index b55fe76..8691ed4 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -21,6 +21,7 @@
import android.app.ActivityThread;
import android.app.IActivityManager;
import android.app.QueuedWork;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -71,22 +72,22 @@
/** @hide */
public static final int TYPE_UNREGISTERED = 2;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final int mType;
@UnsupportedAppUsage
final boolean mOrderedHint;
@UnsupportedAppUsage
final boolean mInitialStickyHint;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final IBinder mToken;
@UnsupportedAppUsage
final int mSendingUser;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final int mFlags;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int mResultCode;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
String mResultData;
@UnsupportedAppUsage
Bundle mResultExtras;
@@ -96,7 +97,7 @@
boolean mFinished;
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public PendingResult(int resultCode, String resultData, Bundle resultExtras, int type,
boolean ordered, boolean sticky, IBinder token, int userId, int flags) {
mResultCode = resultCode;
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 2b7ea66..089cf10 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -25,6 +25,7 @@
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.StrictMode;
@@ -198,7 +199,7 @@
final CharSequence mText;
final String mHtmlText;
final Intent mIntent;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Uri mUri;
/** @hide */
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 3fe17840..8760efe 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -95,9 +95,6 @@
* Sets the current primary clip on the clipboard. This is the clip that
* is involved in normal cut and paste operations.
*
- * <em>If the application is not the default IME or does not have input focus this will have
- * no effect.</em>
- *
* @param clip The clipped data item to set.
* @see #getPrimaryClip()
* @see #clearPrimaryClip()
@@ -115,9 +112,6 @@
/**
* Clears any current primary clip on the clipboard.
*
- * <em>If the application is not the default IME or does not have input focus this will have
- * no effect.</em>
- *
* @see #setPrimaryClip(ClipData)
*/
public void clearPrimaryClip() {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index f5339ef..bd1e6a4 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -37,6 +37,7 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
@@ -163,7 +164,7 @@
* in the test, which is available via {@link #getPathPermissions()}.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public ContentProvider(
Context context,
String readPermission,
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 04be572..d315f49 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -24,6 +24,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
@@ -73,7 +74,7 @@
private final ContentResolver mContentResolver;
@UnsupportedAppUsage
private final IContentProvider mContentProvider;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mPackageName;
private final boolean mStable;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80e79a3..d88f6e3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -327,6 +327,15 @@
public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;
/**
+ * Flag for {@link #bindService}: If binding from something better than perceptible,
+ * still set the adjust below perceptible. This would be used for bound services that can
+ * afford to be evicted when under extreme memory pressure, but should be restarted as soon
+ * as possible.
+ * @hide
+ */
+ public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x0100;
+
+ /**
* @hide Flag for {@link #bindService}: allows binding to a service provided
* by an instant app. Note that the caller may not have access to the instant
* app providing the service which is a violation of the instant app sandbox.
@@ -3158,11 +3167,11 @@
*
* <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
* don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
- * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
- * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
- * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
- * Generally, if you are running as an instant app you should always check whether the result
- * of this method is null.
+ * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+ * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+ * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+ * return <code>null</code>. Generally, if you are running as an instant app you should always
+ * check whether the result of this method is null.
*
* @param name The name of the desired service.
*
@@ -3249,11 +3258,11 @@
*
* <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
* don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
- * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
- * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
- * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
- * Generally, if you are running as an instant app you should always check whether the result
- * of this method is null.
+ * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+ * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+ * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+ * return <code>null</code>. Generally, if you are running as an instant app you should always
+ * check whether the result of this method is null.
*
* @param serviceClass The class of the desired service.
* @return The service or null if the class is not a supported system service.
@@ -3407,7 +3416,7 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.app.NotificationManager} for controlling keyguard.
+ * {@link android.app.KeyguardManager} for controlling keyguard.
*
* @see #getSystemService(String)
* @see android.app.KeyguardManager
@@ -3431,7 +3440,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final String COUNTRY_DETECTOR = "country_detector";
/**
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index d814e67..044ed61 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -21,6 +21,7 @@
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ICancellationSignal;
@@ -80,7 +81,7 @@
Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException;
/* IPC constants */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
static final String descriptor = "android.content.IContentProvider";
@UnsupportedAppUsage
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 212e132..0469a90 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
@@ -124,7 +125,7 @@
*
* <p><strong>Data Authority</strong> matches if any of the given values match
* the Intent's data authority <em>and</em> one of the data schemes in the filter
- * has matched the Intent, <em>or</em> no authories were supplied in the filter.
+ * has matched the Intent, <em>or</em> no authorities were supplied in the filter.
* The Intent authority is determined by calling
* {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
* <em>Note that authority matching here is <b>case sensitive</b>, unlike
@@ -655,7 +656,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final boolean isVerified() {
if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) {
return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY);
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 33395ec..bbdab04 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -25,6 +25,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -403,7 +404,7 @@
private static final String TAG_RESTRICTION = "restriction";
private final Context mContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final IRestrictionsManager mService;
/**
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index fd12d7a..c18b282 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -18,6 +18,7 @@
import android.accounts.Account;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,10 +34,10 @@
@UnsupportedAppUsage
private final Account mAccountToSync;
/** Authority string that corresponds to a ContentProvider. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mAuthority;
/** Bundle containing user info as well as sync settings. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final Bundle mExtras;
/** Don't allow this sync request on metered networks. */
private final boolean mDisallowMetered;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 46877ca..44e652f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -42,6 +42,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -136,7 +137,7 @@
"android.content.pm.extra.PIN_ITEM_REQUEST";
private final Context mContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final ILauncherApps mService;
@UnsupportedAppUsage
private final PackageManager mPm;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 316ace1..8fab7bb 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -32,6 +32,7 @@
import android.content.pm.PackageManager.InstallReason;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Build;
import android.os.FileBridge;
import android.os.Handler;
import android.os.Looper;
@@ -960,6 +961,10 @@
* If the installer is the device owner or the affiliated profile owner, there will be no
* user intervention.
*
+ * @param statusReceiver Called when the state of the session changes. Intents
+ * sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
+ * individual status codes on how to handle them.
+ *
* @throws SecurityException if streams opened through
* {@link #openWrite(String, long, long)} are still open.
*
@@ -986,7 +991,9 @@
* that new properties are added to the session with a new API revision. In this case the
* callers need to be updated.
*
- * @param statusReceiver Callbacks called when the state of the session changes.
+ * @param statusReceiver Called when the state of the session changes. Intents
+ * sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
+ * individual status codes on how to handle them.
*
* @hide
*/
@@ -1091,7 +1098,7 @@
public static final int UID_UNKNOWN = -1;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int mode = MODE_INVALID;
/** {@hide} */
@UnsupportedAppUsage
@@ -1104,13 +1111,13 @@
@UnsupportedAppUsage
public long sizeBytes = -1;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public String appPackageName;
/** {@hide} */
@UnsupportedAppUsage
public Bitmap appIcon;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public String appLabel;
/** {@hide} */
public long appIconLastModified = -1;
@@ -1435,40 +1442,40 @@
public static class SessionInfo implements Parcelable {
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int sessionId;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public String installerPackageName;
/** {@hide} */
@UnsupportedAppUsage
public String resolvedBaseCodePath;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public float progress;
/** {@hide} */
@UnsupportedAppUsage
public boolean sealed;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean active;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int mode;
/** {@hide} */
public @InstallReason int installReason;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public long sizeBytes;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public String appPackageName;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public Bitmap appIcon;
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public CharSequence appLabel;
/** {@hide} */
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 0c70a3d..19af609 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -22,7 +22,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -103,8 +102,14 @@
private static volatile boolean sForceSafeLabels = false;
- /** {@hide} */
- @UnsupportedAppUsage
+ /**
+ * Always use {@link #loadSafeLabel safe labels} when calling {@link #loadLabel}.
+ *
+ * @param forceSafeLabels {@code true} to enforce safe labels
+ *
+ * @hide
+ */
+ @SystemApi
public static void setForceSafeLabels(boolean forceSafeLabels) {
sForceSafeLabels = forceSafeLabels;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1b4878c..a15711f5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1979,6 +1979,8 @@
* </ul>
* A version of 1.1.0 or higher also indicates:
* <ul>
+ * <li>The {@code VK_ANDROID_external_memory_android_hardware_buffer} extension is
+ * supported.</li>
* <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li>
* <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is
* supported.</li>
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 20e1454e..b5b4432 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -136,24 +136,6 @@
public abstract void setVoiceInteractionPackagesProvider(PackagesProvider provider);
/**
- * Sets the SMS packages provider.
- * @param provider The packages provider.
- */
- public abstract void setSmsAppPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the dialer packages provider.
- * @param provider The packages provider.
- */
- public abstract void setDialerAppPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the sim call manager packages provider.
- * @param provider The packages provider.
- */
- public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider);
-
- /**
* Sets the Use Open Wifi packages provider.
* @param provider The packages provider.
*/
@@ -166,26 +148,28 @@
public abstract void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider);
/**
- * Requests granting of the default permissions to the current default SMS app.
- * @param packageName The default SMS package name.
- * @param userId The user for which to grant the permissions.
+ * Called when the package for the default dialer changed
+ *
+ * @param packageName the new dialer package
+ * @param userId user for which the change was made
*/
- public abstract void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId);
+ public void onDefaultDialerAppChanged(String packageName, int userId) {}
/**
- * Requests granting of the default permissions to the current default dialer app.
- * @param packageName The default dialer package name.
- * @param userId The user for which to grant the permissions.
+ * Called when the package for the default SMS handler changed
+ *
+ * @param packageName the new sms package
+ * @param userId user for which the change was made
*/
- public abstract void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId);
+ public void onDefaultSmsAppChanged(String packageName, int userId) {}
/**
- * Requests granting of the default permissions to the current default sim call manager.
- * @param packageName The default sim call manager package name.
- * @param userId The user for which to grant the permissions.
+ * Called when the package for the default sim call manager changed
+ *
+ * @param packageName the new sms package
+ * @param userId user for which the change was made
*/
- public abstract void grantDefaultPermissionsToDefaultSimCallManager(String packageName,
- int userId);
+ public void onDefaultSimCallManagerAppChanged(String packageName, int userId) {}
/**
* Requests granting of the default permissions to the current default Use Open Wifi app.
@@ -446,8 +430,8 @@
*
* @param packageName The package to check for
* @param uid the uid in which the package is running
- * @return {@link USER_TRUSTED} if the user has trusted the package, {@link USER_BLOCKED}
- * if user has blocked requests from the package, {@link USER_DEFAULT} if the user response
+ * @return {@link #USER_TRUSTED} if the user has trusted the package, {@link #USER_BLOCKED}
+ * if user has blocked requests from the package, {@link #USER_DEFAULT} if the user response
* is not yet available
*/
int getPackageTrustedToInstallApps(String packageName, int uid);
@@ -561,7 +545,7 @@
/**
* Returns a list without a change observer.
*
- * {@see #getPackageList(PackageListObserver)}
+ * @see #getPackageList(PackageListObserver)
*/
public @NonNull PackageList getPackageList() {
return getPackageList(null);
@@ -590,7 +574,16 @@
/**
* Returns a package object for the disabled system package name.
*/
- public abstract @Nullable PackageParser.Package getDisabledPackage(@NonNull String packageName);
+ public abstract @Nullable PackageParser.Package getDisabledSystemPackage(
+ @NonNull String packageName);
+
+ /**
+ * Returns the package name for the disabled system package.
+ *
+ * This is equivalent to
+ * {@link #getDisabledSystemPackage(String)}.{@link PackageParser.Package#packageName}
+ */
+ public abstract @Nullable String getDisabledSystemPackageName(@NonNull String packageName);
/**
* Returns whether or not the component is the resolver activity.
@@ -619,7 +612,7 @@
* Access may be limited based upon whether the calling or target applications
* are instant applications.
*
- * @see #canAccessInstantApps(int)
+ * @see #canAccessInstantApps
*/
public abstract boolean filterAppAccess(
@Nullable PackageParser.Package pkg, int callingUid, int userId);
@@ -635,6 +628,9 @@
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
@NonNull String packageName, int flagMask, int flagValues, int userId);
+ /** Returns whether the given package was signed by the platform */
+ public abstract boolean isPlatformSigned(String pkg);
+
/**
* Returns true if it's still safe to restore data backed up from this app's version
* that was signed with restoringFromSigHash.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6d49362..03a3d1f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1902,7 +1902,7 @@
* @throws XmlPullParserException
* @throws IOException
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java
index 2eef165..dccc524 100644
--- a/core/java/android/content/pm/ParceledListSlice.java
+++ b/core/java/android/content/pm/ParceledListSlice.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -72,7 +73,7 @@
}
@SuppressWarnings("unchecked")
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR =
new Parcelable.ClassLoaderCreator<ParceledListSlice>() {
public ParceledListSlice createFromParcel(Parcel in) {
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 546c213..ec2e2fd 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -30,6 +30,7 @@
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1243,7 +1244,7 @@
* @hide
*/
@Nullable
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public Icon getIcon() {
return mIcon;
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 60ac1f0..2d59003 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.drawable.AdaptiveIconDrawable;
+import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -55,7 +56,7 @@
private static final String TAG = "ShortcutManager";
private final Context mContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final IShortcutService mService;
/**
diff --git a/core/java/android/content/res/package.html b/core/java/android/content/res/package.html
index 3d0bac1..3970b16 100644
--- a/core/java/android/content/res/package.html
+++ b/core/java/android/content/res/package.html
@@ -1,7 +1,7 @@
<HTML>
<BODY>
<p>Contains classes for accessing application resources,
-such as raw asset files, colors, drawables, media or other other files
+such as raw asset files, colors, drawables, media, or other files
in the package, plus important device configuration details
(orientation, input types, etc.) that affect how the application may behave.</p>
@@ -9,4 +9,4 @@
href="{@docRoot}guide/topics/resources/index.html">Application Resources</a> guide.</p>
{@more}
</BODY>
-</HTML>
\ No newline at end of file
+</HTML>
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index c6c2aa5..8bcaa45 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -19,6 +19,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
@@ -79,7 +80,7 @@
private final DataSetObservable mDataSetObservable = new DataSetObservable();
private final ContentObservable mContentObservable = new ContentObservable();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Bundle mExtras = Bundle.EMPTY;
/* -------------------------------------------------------- */
diff --git a/core/java/android/database/sqlite/SQLiteCustomFunction.java b/core/java/android/database/sqlite/SQLiteCustomFunction.java
index ec20458..41b78d3 100644
--- a/core/java/android/database/sqlite/SQLiteCustomFunction.java
+++ b/core/java/android/database/sqlite/SQLiteCustomFunction.java
@@ -17,6 +17,7 @@
package android.database.sqlite;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
* Describes a custom SQL function.
@@ -24,7 +25,7 @@
* @hide
*/
public final class SQLiteCustomFunction {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final String name;
@UnsupportedAppUsage
public final int numArgs;
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index b705ebb..4fce2d7 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -34,6 +34,7 @@
import java.util.Arrays;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@@ -50,6 +51,8 @@
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
private Map<String, String> mProjectionMap = null;
+ private List<Pattern> mProjectionGreylist = null;
+
@UnsupportedAppUsage
private String mTables = "";
@UnsupportedAppUsage
@@ -164,6 +167,17 @@
}
/**
+ * Sets a projection greylist of columns that will be allowed through, even
+ * when {@link #setStrict(boolean)} is enabled. This provides a way for
+ * abusive custom columns like {@code COUNT(*)} to continue working.
+ *
+ * @hide
+ */
+ public void setProjectionGreylist(List<Pattern> projectionGreylist) {
+ mProjectionGreylist = projectionGreylist;
+ }
+
+ /**
* 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.
@@ -809,6 +823,24 @@
continue;
}
+ // If greylist is configured, we might be willing to let
+ // this custom column bypass our strict checks.
+ if (mProjectionGreylist != null) {
+ boolean match = false;
+ for (Pattern p : mProjectionGreylist) {
+ if (p.matcher(userColumn).matches()) {
+ match = true;
+ break;
+ }
+ }
+
+ if (match) {
+ Log.w(TAG, "Allowing abusive custom column: " + userColumn);
+ projection[i] = userColumn;
+ continue;
+ }
+ }
+
throw new IllegalArgumentException("Invalid column "
+ projectionIn[i]);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 8e96f56..ac863b2 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -31,6 +31,7 @@
import android.graphics.SurfaceTexture;
import android.media.AudioAttributes;
import android.media.IAudioService;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -165,7 +166,7 @@
private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400;
private static final int CAMERA_MSG_FOCUS_MOVE = 0x800;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private long mNativeContext; // accessed by native methods
private EventHandler mEventHandler;
private ShutterCallback mShutterCallback;
@@ -721,7 +722,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public native final void setPreviewSurface(Surface surface) throws IOException;
/**
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index c17aabb..d2c0e7b 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -20,6 +20,7 @@
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -184,7 +185,7 @@
* Private use only. See {@link #create(int, int, int, int, long)}. May also be
* called from JNI using an already allocated native <code>HardwareBuffer</code>.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private HardwareBuffer(long nativeObject) {
mNativeObject = nativeObject;
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index dbb2527..c604ff1 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -202,31 +202,6 @@
}
/**
- * @param error
- * @param vendorCode
- * @return the error string associated with this error
- */
- default String getErrorString(int error, int vendorCode) {
- throw new UnsupportedOperationException("Stub!");
- }
-
- /**
- * @param acquireInfo
- * @param vendorCode
- * @return the help string associated with this code
- */
- default String getAcquiredString(int acquireInfo, int vendorCode) {
- throw new UnsupportedOperationException("Stub!");
- }
-
- /**
- * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
- */
- default int getType() {
- throw new UnsupportedOperationException("Stub!");
- }
-
- /**
* This call warms up the hardware and starts scanning for valid biometrics. It terminates
* when {@link AuthenticationCallback#onAuthenticationError(int,
* CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index cd16d56..46e66e0 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -376,10 +376,10 @@
/**
* <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can
- * be overriden for physical devices backing a logical multi-camera.</p>
+ * be overridden for physical devices backing a logical multi-camera.</p>
*
* <p>This is a subset of android.request.availableRequestKeys which contains a list
- * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+ * of keys that can be overridden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
* The respective value of such request key can be obtained by calling
* {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
* individual physical device requests must be built via
@@ -394,7 +394,7 @@
*
* <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
*
- * @return List of keys that can be overriden in individual physical device requests.
+ * @return List of keys that can be overridden in individual physical device requests.
* In case the camera device doesn't support such keys the list can be null.
*/
@SuppressWarnings({"unchecked"})
@@ -1922,10 +1922,10 @@
new Key<int[]>("android.request.availableSessionKeys", int[].class);
/**
- * <p>A subset of the available request keys that can be overriden for
+ * <p>A subset of the available request keys that can be overridden for
* physical devices backing a logical multi-camera.</p>
* <p>This is a subset of android.request.availableRequestKeys which contains a list
- * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+ * of keys that can be overridden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
* The respective value of such request key can be obtained by calling
* {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
* individual physical device requests must be built via
diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java
index 9ac89c8..3b8beac 100644
--- a/core/java/android/hardware/camera2/utils/CloseableLock.java
+++ b/core/java/android/hardware/camera2/utils/CloseableLock.java
@@ -290,7 +290,7 @@
/**
* Release a single lock that was acquired.
*
- * <p>Any other other that is blocked and trying to acquire a lock will get a chance
+ * <p>Any other thread that is blocked and trying to acquire a lock will get a chance
* to acquire the lock.</p>
*
* @throws IllegalStateException if no locks were acquired, or if the lock was already closed
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 0f83c8b..66613ea 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -168,10 +168,11 @@
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
- // Though this may not be a hardware issue, it will cause apps to give up or try
- // again later.
+ // Though this may not be a hardware issue, it will cause apps to give up or
+ // try again later.
callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -232,10 +233,11 @@
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
- // Though this may not be a hardware issue, it will cause apps to give up or try
- // again later.
+ // Though this may not be a hardware issue, it will cause apps to give up or
+ // try again later.
callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -315,7 +317,8 @@
Log.w(TAG, "Remote exception in remove: ", e);
if (callback != null) {
callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -513,32 +516,36 @@
}
}
- @Override
- public String getErrorString(int errMsg, int vendorCode) {
+ /**
+ * @hide
+ */
+ public static String getErrorString(Context context, int errMsg, int vendorCode) {
switch (errMsg) {
- case FACE_ERROR_HW_UNAVAILABLE:
- return mContext.getString(
- com.android.internal.R.string.face_error_hw_not_available);
case FACE_ERROR_UNABLE_TO_PROCESS:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.face_error_unable_to_process);
- case FACE_ERROR_TIMEOUT:
- return mContext.getString(com.android.internal.R.string.face_error_timeout);
+ case FACE_ERROR_HW_UNAVAILABLE:
+ return context.getString(
+ com.android.internal.R.string.face_error_hw_not_available);
case FACE_ERROR_NO_SPACE:
- return mContext.getString(com.android.internal.R.string.face_error_no_space);
+ return context.getString(com.android.internal.R.string.face_error_no_space);
+ case FACE_ERROR_TIMEOUT:
+ return context.getString(com.android.internal.R.string.face_error_timeout);
case FACE_ERROR_CANCELED:
- return mContext.getString(com.android.internal.R.string.face_error_canceled);
+ return context.getString(com.android.internal.R.string.face_error_canceled);
case FACE_ERROR_LOCKOUT:
- return mContext.getString(com.android.internal.R.string.face_error_lockout);
+ return context.getString(com.android.internal.R.string.face_error_lockout);
case FACE_ERROR_LOCKOUT_PERMANENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.face_error_lockout_permanent);
+ case FACE_ERROR_USER_CANCELED:
+ return context.getString(com.android.internal.R.string.face_error_user_canceled);
case FACE_ERROR_NOT_ENROLLED:
- return mContext.getString(com.android.internal.R.string.face_error_not_enrolled);
+ return context.getString(com.android.internal.R.string.face_error_not_enrolled);
case FACE_ERROR_HW_NOT_PRESENT:
- return mContext.getString(com.android.internal.R.string.face_error_hw_not_present);
+ return context.getString(com.android.internal.R.string.face_error_hw_not_present);
case FACE_ERROR_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.face_error_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -552,35 +559,34 @@
/**
* @hide
*/
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
+ public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FACE_ACQUIRED_GOOD:
return null;
case FACE_ACQUIRED_INSUFFICIENT:
- return mContext.getString(R.string.face_acquired_insufficient);
+ return context.getString(R.string.face_acquired_insufficient);
case FACE_ACQUIRED_TOO_BRIGHT:
- return mContext.getString(R.string.face_acquired_too_bright);
+ return context.getString(R.string.face_acquired_too_bright);
case FACE_ACQUIRED_TOO_DARK:
- return mContext.getString(R.string.face_acquired_too_dark);
+ return context.getString(R.string.face_acquired_too_dark);
case FACE_ACQUIRED_TOO_CLOSE:
- return mContext.getString(R.string.face_acquired_too_close);
+ return context.getString(R.string.face_acquired_too_close);
case FACE_ACQUIRED_TOO_FAR:
- return mContext.getString(R.string.face_acquired_too_far);
+ return context.getString(R.string.face_acquired_too_far);
case FACE_ACQUIRED_TOO_HIGH:
- return mContext.getString(R.string.face_acquired_too_high);
+ return context.getString(R.string.face_acquired_too_high);
case FACE_ACQUIRED_TOO_LOW:
- return mContext.getString(R.string.face_acquired_too_low);
+ return context.getString(R.string.face_acquired_too_low);
case FACE_ACQUIRED_TOO_RIGHT:
- return mContext.getString(R.string.face_acquired_too_right);
+ return context.getString(R.string.face_acquired_too_right);
case FACE_ACQUIRED_TOO_LEFT:
- return mContext.getString(R.string.face_acquired_too_left);
+ return context.getString(R.string.face_acquired_too_left);
case FACE_ACQUIRED_POOR_GAZE:
- return mContext.getString(R.string.face_acquired_poor_gaze);
+ return context.getString(R.string.face_acquired_poor_gaze);
case FACE_ACQUIRED_NOT_DETECTED:
- return mContext.getString(R.string.face_acquired_not_detected);
+ return context.getString(R.string.face_acquired_not_detected);
case FACE_ACQUIRED_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
R.array.face_acquired_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -592,18 +598,10 @@
}
/**
- * @hide
- */
- @Override
- public int getType() {
- return TYPE_FACE;
- }
-
- /**
* Used so BiometricPrompt can map the face ones onto existing public constants.
* @hide
*/
- public int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
+ public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FACE_ACQUIRED_GOOD:
return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
@@ -898,13 +896,13 @@
? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
}
}
@@ -932,7 +930,7 @@
if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
}
- final String msg = getAcquiredString(acquireInfo, vendorCode);
+ final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
if (msg == null) {
return;
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index dd995c9..a012214 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -34,8 +34,8 @@
// This method invokes the BiometricDialog. The arguments are almost the same as above,
// but should only be called from (BiometricPromptService).
- void authenticateFromService(IBinder token, long sessionId, int userId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ void authenticateFromService(boolean requireConfirmation, IBinder token, long sessionId,
+ int userId, IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
int callingUid, int callingPid, int callingUserId);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b380a2e..a4f3ce1 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -426,7 +426,8 @@
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -476,7 +477,8 @@
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -546,7 +548,8 @@
Slog.w(TAG, "Remote exception in remove: ", e);
if (callback != null) {
callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -568,7 +571,8 @@
Slog.w(TAG, "Remote exception in enumerate: ", e);
if (callback != null) {
callback.onEnumerateError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
}
}
}
@@ -862,7 +866,7 @@
if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
}
- final String msg = getAcquiredString(acquireInfo, vendorCode);
+ final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
if (msg == null) {
return;
}
@@ -882,16 +886,16 @@
? (vendorCode + FINGERPRINT_ERROR_VENDOR_BASE) : errMsgId;
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
} else if (mEnumerateCallback != null) {
mEnumerateCallback.onEnumerateError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
+ getErrorString(mContext, errMsgId, vendorCode));
}
}
@@ -934,38 +938,37 @@
/**
* @hide
*/
- @Override
- public String getErrorString(int errMsg, int vendorCode) {
+ public static String getErrorString(Context context, int errMsg, int vendorCode) {
switch (errMsg) {
case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_unable_to_process);
case FINGERPRINT_ERROR_HW_UNAVAILABLE:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_hw_not_available);
case FINGERPRINT_ERROR_NO_SPACE:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_no_space);
case FINGERPRINT_ERROR_TIMEOUT:
- return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout);
+ return context.getString(com.android.internal.R.string.fingerprint_error_timeout);
case FINGERPRINT_ERROR_CANCELED:
- return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled);
+ return context.getString(com.android.internal.R.string.fingerprint_error_canceled);
case FINGERPRINT_ERROR_LOCKOUT:
- return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout);
+ return context.getString(com.android.internal.R.string.fingerprint_error_lockout);
case FINGERPRINT_ERROR_LOCKOUT_PERMANENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_lockout_permanent);
case FINGERPRINT_ERROR_USER_CANCELED:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_user_canceled);
case FINGERPRINT_ERROR_NO_FINGERPRINTS:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_no_fingerprints);
case FINGERPRINT_ERROR_HW_NOT_PRESENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_error_hw_not_present);
case FINGERPRINT_ERROR_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -979,28 +982,27 @@
/**
* @hide
*/
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
+ public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FINGERPRINT_ACQUIRED_GOOD:
return null;
case FINGERPRINT_ACQUIRED_PARTIAL:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_partial);
case FINGERPRINT_ACQUIRED_INSUFFICIENT:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_insufficient);
case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_imager_dirty);
case FINGERPRINT_ACQUIRED_TOO_SLOW:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_too_slow);
case FINGERPRINT_ACQUIRED_TOO_FAST:
- return mContext.getString(
+ return context.getString(
com.android.internal.R.string.fingerprint_acquired_too_fast);
case FINGERPRINT_ACQUIRED_VENDOR: {
- String[] msgArray = mContext.getResources().getStringArray(
+ String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_acquired_vendor);
if (vendorCode < msgArray.length) {
return msgArray[vendorCode];
@@ -1011,14 +1013,6 @@
return null;
}
- /**
- * @hide
- */
- @Override
- public int getType() {
- return TYPE_FINGERPRINT;
- }
-
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
@Override // binder call
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6ca5f0c..bfb7c58 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.media.AudioAttributes;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -65,7 +66,7 @@
private static InputManager sInstance;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final IInputManager mIm;
// Guarded by mInputDevicesLock
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index ae0855a..4111941 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -32,6 +32,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.gadget.V1_0.GadgetFunction;
+import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -385,7 +386,7 @@
/**
* {@hide}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public UsbManager(Context context, IUsbManager service) {
mContext = context;
mService = service;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 34fa5b6..b948402 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -85,6 +85,7 @@
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -465,7 +466,7 @@
public final void initializeInternal(IBinder token, int displayId,
IInputMethodPrivilegedOperations privilegedOperations) {
mPrivOps.set(privilegedOperations);
- mImm.registerInputMethodPrivOps(token, mPrivOps);
+ InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
updateInputMethodDisplay(displayId);
attachToken(token);
}
@@ -1031,7 +1032,7 @@
if (mToken != null) {
// This is completely optional, but allows us to show more explicit error messages
// when IME developers are doing something unsupported.
- mImm.unregisterInputMethodPrivOps(mToken);
+ InputMethodPrivilegedOperationsRegistry.remove(mToken);
}
}
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index ec5f050..51d33b2 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -25,6 +25,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
@@ -627,7 +628,7 @@
rows.add(row);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final void resize(int newWidth, int newHeight) {
int numRows = rows.size();
for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fd1e5f2..12b6f9e 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -21,6 +21,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -904,7 +905,7 @@
* specifier. See {@link #setNetworkSpecifier}.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public NetworkSpecifier getNetworkSpecifier() {
return mNetworkSpecifier;
}
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 010d72f..9c836c3 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -211,7 +211,7 @@
* Called for every request every time a new NetworkRequest is seen
* and whenever the filterScore or filterNetworkCapabilities change.
*
- * acceptRequest can be overriden to provide complex filter behavior
+ * acceptRequest can be overridden to provide complex filter behavior
* for the incoming requests
*
* For output, this class will call {@link #needNetworkFor} and
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index 9ce2a5b..be2f955 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -35,7 +35,7 @@
public abstract boolean satisfiedBy(NetworkSpecifier other);
/**
- * Optional method which can be overriden by concrete implementations of NetworkSpecifier to
+ * Optional method which can be overridden by concrete implementations of NetworkSpecifier to
* check a self-reported UID. A concrete implementation may contain a UID which would be self-
* reported by the caller (since NetworkSpecifier implementations should be non-mutable). This
* function is called by ConnectivityService and is passed the actual UID of the caller -
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index c545ee2..97fb3fb 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Slog;
@@ -34,7 +35,7 @@
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
public final NetworkCapabilities networkCapabilities;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final Network network;
public final String subscriberId;
public final String networkId;
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 31494d9..abc1cac 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.SystemProperties;
import android.util.Log;
@@ -87,7 +88,7 @@
* requires root access.
*/
public class SSLCertificateSocketFactory extends SSLSocketFactory {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static final String TAG = "SSLCertificateSocketFactory";
@UnsupportedAppUsage
@@ -384,7 +385,7 @@
* @throws IllegalArgumentException if the socket was not created by this factory.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public byte[] getAlpnSelectedProtocol(Socket socket) {
return castToOpenSSLSocket(socket).getAlpnSelectedProtocol();
}
@@ -410,7 +411,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void setChannelIdPrivateKey(PrivateKey privateKey) {
mChannelIdPrivateKey = privateKey;
}
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 71199c9..030066e 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -19,6 +19,7 @@
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
/**
* High level manager used to obtain an instance of an {@link NfcAdapter}.
@@ -45,7 +46,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public NfcManager(Context context) {
NfcAdapter adapter;
context = context.getApplicationContext();
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index 02819a6..080e058 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -38,7 +38,7 @@
* <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks.
* <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks.
* <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks.
- * <li>MIFARE Classic 4k} are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
+ * <li>MIFARE Classic 4k are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
* and the last 8 sectors contain 16 blocks.
* </ul>
*
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b9d9007..f947b5e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -773,6 +773,7 @@
if (tracingEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
}
+ ThreadLocalWorkSourceUid.set(Binder.getCallingUid());
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
if (observer != null) {
@@ -793,6 +794,7 @@
}
res = true;
} finally {
+ ThreadLocalWorkSourceUid.clear();
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index e03af9d..f3a9a50 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -739,6 +739,8 @@
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
+ msg.workSourceUid = ThreadLocalWorkSourceUid.get();
+
if (mAsynchronous) {
msg.setAsynchronous(true);
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index ded3a19..5b8abab 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -205,6 +205,7 @@
token = observer.messageDispatchStarting();
}
try {
+ ThreadLocalWorkSourceUid.set(msg.workSourceUid);
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
@@ -216,6 +217,7 @@
}
throw exception;
} finally {
+ ThreadLocalWorkSourceUid.clear();
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 455d8ed..cd3f301 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,7 +16,6 @@
package android.os;
-import android.os.MessageProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -74,11 +73,25 @@
public Messenger replyTo;
/**
+ * Indicates that the uid is not set;
+ *
+ * @hide Only for use within the system server.
+ */
+ public static final int UID_NONE = -1;
+
+ /**
* Optional field indicating the uid that sent the message. This is
* only valid for messages posted by a {@link Messenger}; otherwise,
* it will be -1.
*/
- public int sendingUid = -1;
+ public int sendingUid = UID_NONE;
+
+ /**
+ * Optional field indicating the uid that caused this message to be enqueued.
+ *
+ * @hide Only for use within the system server.
+ */
+ public int workSourceUid = UID_NONE;
/** If set message is in use.
* This flag is set when the message is enqueued and remains set while it
@@ -151,6 +164,7 @@
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
+ m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
@@ -301,7 +315,8 @@
arg2 = 0;
obj = null;
replyTo = null;
- sendingUid = -1;
+ sendingUid = UID_NONE;
+ workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
@@ -329,6 +344,7 @@
this.obj = o.obj;
this.replyTo = o.replyTo;
this.sendingUid = o.sendingUid;
+ this.workSourceUid = o.workSourceUid;
if (o.data != null) {
this.data = (Bundle) o.data.clone();
@@ -612,6 +628,7 @@
dest.writeBundle(data);
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
+ dest.writeInt(workSourceUid);
}
private void readFromParcel(Parcel source) {
@@ -625,5 +642,6 @@
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
+ workSourceUid = source.readInt();
}
}
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index b13bcac..051ab75 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -15,8 +15,10 @@
*/
package android.os;
-import java.io.ByteArrayOutputStream;
-import java.nio.charset.StandardCharsets;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Wrapper class for sending data from Android OS to StatsD.
@@ -24,39 +26,28 @@
* @hide
*/
public final class StatsLogEventWrapper implements Parcelable {
- private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
+ static final boolean DEBUG = false;
+ static final String TAG = "StatsLogEventWrapper";
- // Below are constants copied from log/log.h
- private static final int EVENT_TYPE_INT = 0; /* int32_t */
- private static final int EVENT_TYPE_LONG = 1; /* int64_t */
- private static final int EVENT_TYPE_STRING = 2;
- private static final int EVENT_TYPE_LIST = 3;
- private static final int EVENT_TYPE_FLOAT = 4;
+ // Keep in sync with FieldValue.h enums
+ private static final int EVENT_TYPE_UNKNOWN = 0;
+ private static final int EVENT_TYPE_INT = 1; /* int32_t */
+ private static final int EVENT_TYPE_LONG = 2; /* int64_t */
+ private static final int EVENT_TYPE_FLOAT = 3;
+ private static final int EVENT_TYPE_DOUBLE = 4;
+ private static final int EVENT_TYPE_STRING = 5;
+ private static final int EVENT_TYPE_STORAGE = 6;
- // Keep this in sync with system/core/logcat/event.logtags
- private static final int STATS_BUFFER_TAG_ID = 1937006964;
- /**
- * Creates a log_event that is binary-encoded as implemented in
- * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
- * for pushed and pulled data. The write* methods must be called in the same order as their
- * field number. There is no checking that the correct number of write* methods is called.
- * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
- * may be unnecessary.
- *
- * @param tag The integer representing the tag for this event.
- * @param fields The number of fields specified in this event.
- */
- public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
- // Write four bytes from tag, starting with least-significant bit.
- // For pulled data, this tag number is not really used. We use the same tag number as
- // pushed ones to be consistent.
- write4Bytes(STATS_BUFFER_TAG_ID);
- mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
- mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
- // The first element is the elapsed realtime.
- writeLong(elapsedNanos);
- // The second element is the real atom tag number
- writeInt(tag);
+ List<Integer> mTypes = new ArrayList<>();
+ List<Object> mValues = new ArrayList<>();
+ int mTag;
+ long mElapsedTimeNs;
+ long mWallClockTimeNs;
+
+ public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
+ this.mTag = tag;
+ this.mElapsedTimeNs = elapsedTimeNs;
+ this.mWallClockTimeNs = wallClockTimeNs;
}
/**
@@ -79,69 +70,80 @@
}
};
- private void write4Bytes(int val) {
- mStorage.write(val);
- mStorage.write(val >>> 8);
- mStorage.write(val >>> 16);
- mStorage.write(val >>> 24);
- }
-
- private void write8Bytes(long val) {
- write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
- write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
- }
-
- /**
- * Adds 32-bit integer to output.
- */
public void writeInt(int val) {
- mStorage.write(EVENT_TYPE_INT);
- write4Bytes(val);
+ mTypes.add(EVENT_TYPE_INT);
+ mValues.add(val);
}
- /**
- * Adds 64-bit long to output.
- */
public void writeLong(long val) {
- mStorage.write(EVENT_TYPE_LONG);
- write8Bytes(val);
+ mTypes.add(EVENT_TYPE_LONG);
+ mValues.add(val);
}
/**
- * Adds a 4-byte floating point value to output.
- */
- public void writeFloat(float val) {
- int v = Float.floatToIntBits(val);
- mStorage.write(EVENT_TYPE_FLOAT);
- write4Bytes(v);
- }
-
- /**
- * Adds a string to the output.
+ * Write a string value.
*/
public void writeString(String val) {
- mStorage.write(EVENT_TYPE_STRING);
- write4Bytes(val.length());
- byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
- mStorage.write(bytes, 0, bytes.length);
+ mTypes.add(EVENT_TYPE_STRING);
+ // use empty string for null
+ mValues.add(val == null ? "" : val);
+ }
+
+ public void writeFloat(float val) {
+ mTypes.add(EVENT_TYPE_FLOAT);
+ mValues.add(val);
}
/**
- * Adds a boolean by adding either a 1 or 0 to the output.
+ * Write a storage value.
*/
+ public void writeStorage(byte[] val) {
+ mTypes.add(EVENT_TYPE_STORAGE);
+ mValues.add(val);
+ }
+
public void writeBoolean(boolean val) {
- int toWrite = val ? 1 : 0;
- mStorage.write(EVENT_TYPE_INT);
- write4Bytes(toWrite);
+ mTypes.add(EVENT_TYPE_INT);
+ mValues.add(val ? 1 : 0);
}
/**
* Writes the stored fields to a byte array. Will first write a new-line character to denote
* END_LIST before writing contents to byte array.
*/
+
public void writeToParcel(Parcel out, int flags) {
- mStorage.write(10); // new-line character is same as END_LIST
- out.writeByteArray(mStorage.toByteArray());
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Writing " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs + " and "
+ + mTypes.size() + " elements.");
+ }
+ out.writeInt(mTag);
+ out.writeLong(mElapsedTimeNs);
+ out.writeLong(mWallClockTimeNs);
+ out.writeInt(mTypes.size());
+ for (int i = 0; i < mTypes.size(); i++) {
+ out.writeInt(mTypes.get(i));
+ switch (mTypes.get(i)) {
+ case EVENT_TYPE_INT:
+ out.writeInt((int) mValues.get(i));
+ break;
+ case EVENT_TYPE_LONG:
+ out.writeLong((long) mValues.get(i));
+ break;
+ case EVENT_TYPE_FLOAT:
+ out.writeFloat((float) mValues.get(i));
+ break;
+ case EVENT_TYPE_STRING:
+ out.writeString((String) mValues.get(i));
+ break;
+ case EVENT_TYPE_STORAGE:
+ out.writeByteArray((byte[]) mValues.get(i));
+ break;
+ default:
+ break;
+ }
+ }
}
/**
diff --git a/core/java/android/os/ThreadLocalWorkSourceUid.java b/core/java/android/os/ThreadLocalWorkSourceUid.java
new file mode 100644
index 0000000..df1d275
--- /dev/null
+++ b/core/java/android/os/ThreadLocalWorkSourceUid.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide Only for use within system server.
+ */
+public final class ThreadLocalWorkSourceUid {
+ public static final int UID_NONE = Message.UID_NONE;
+ private static final ThreadLocal<Integer> sWorkSourceUid =
+ ThreadLocal.withInitial(() -> UID_NONE);
+
+ /** Returns the original work source uid. */
+ public static int get() {
+ return sWorkSourceUid.get();
+ }
+
+ /** Sets the original work source uid. */
+ public static void set(int uid) {
+ sWorkSourceUid.set(uid);
+ }
+
+ /** Clears the stored work source uid. */
+ public static void clear() {
+ sWorkSourceUid.set(UID_NONE);
+ }
+
+ private ThreadLocalWorkSourceUid() {
+ }
+}
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index f303674..71eda19 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -20,6 +20,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.BatteryStats;
+import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -65,7 +66,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static SystemHealthManager from(Context context) {
return (SystemHealthManager)context.getSystemService(Context.SYSTEM_HEALTH_SERVICE);
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index deff693..5bef7ee 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
@@ -249,7 +250,7 @@
}
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public UserHandle getOwner() {
return mOwner;
}
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index e0bffae..18aea03 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,8 +16,8 @@
package android.permissionpresenterservice;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -74,15 +74,13 @@
public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
/**
- * Revoke the permission {@code permissionName} for app {@code packageName}
+ * Revokes the permission {@code permissionName} for app {@code packageName}
*
* @param packageName The package for which to revoke
* @param permissionName The permission to revoke
- *
- * @hide
*/
- @UnsupportedAppUsage
- public abstract void onRevokeRuntimePermission(String packageName, String permissionName);
+ public abstract void onRevokeRuntimePermission(@NonNull String packageName,
+ @NonNull String permissionName);
@Override
public final IBinder onBind(Intent intent) {
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 98b69a8..2387657 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -27,6 +27,7 @@
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1167,7 +1168,7 @@
* @return True if the Preference handled the key. Returns false by default.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean onKey(View v, int keyCode, KeyEvent event) {
return false;
}
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index c76c8d3..dfee1af 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.XmlResourceParser;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@@ -68,7 +69,7 @@
* Fragment that owns this instance.
*/
@Nullable
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private PreferenceFragment mFragment;
/**
@@ -201,7 +202,7 @@
/**
* Sets the owning preference fragment
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
void setFragment(PreferenceFragment fragment) {
mFragment = fragment;
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index b12d9cf..ee64ca2 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1555,7 +1555,7 @@
*
* <p>Note, that due to internal limitations, if there is already a web link
* intent created for the specified document but with different options,
- * then it may be overriden.
+ * then it may be overridden.
*
* <p>Providers are required to show confirmation UI for all new permissions granted
* for the linked document.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 81b1921..0e782d7 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -771,7 +771,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #queryRoots(String[])
* @see #queryRecentDocuments(String, String[])
@@ -828,7 +828,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #getDocumentType(String)
*/
@@ -903,7 +903,7 @@
/**
* Implementation is provided by the parent class. Throws by default, and
- * cannot be overriden.
+ * cannot be overridden.
*
* @see #createDocument(String, String, String)
*/
@@ -914,7 +914,7 @@
/**
* Implementation is provided by the parent class. Throws by default, and
- * cannot be overriden.
+ * cannot be overridden.
*
* @see #deleteDocument(String)
*/
@@ -925,7 +925,7 @@
/**
* Implementation is provided by the parent class. Throws by default, and
- * cannot be overriden.
+ * cannot be overridden.
*/
@Override
public final int update(
@@ -1165,7 +1165,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #openDocument(String, String, CancellationSignal)
*/
@@ -1176,7 +1176,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #openDocument(String, String, CancellationSignal)
*/
@@ -1188,7 +1188,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #openDocument(String, String, CancellationSignal)
*/
@@ -1202,7 +1202,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #openDocument(String, String, CancellationSignal)
*/
@@ -1216,7 +1216,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #openDocumentThumbnail(String, Point, CancellationSignal)
* @see #openTypedDocument(String, String, Bundle, CancellationSignal)
@@ -1229,7 +1229,7 @@
}
/**
- * Implementation is provided by the parent class. Cannot be overriden.
+ * Implementation is provided by the parent class. Cannot be overridden.
*
* @see #openDocumentThumbnail(String, Point, CancellationSignal)
* @see #openTypedDocument(String, String, Bundle, CancellationSignal)
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 189b7b4..82459b1 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -717,7 +717,11 @@
/**
* The picasa id of the image
* <P>Type: TEXT</P>
+ *
+ * @deprecated this value was only relevant for images hosted on
+ * Picasa, which are no longer supported.
*/
+ @Deprecated
public static final String PICASA_ID = "picasa_id";
/**
@@ -755,7 +759,12 @@
/**
* The mini thumb id.
* <P>Type: INTEGER</P>
+ *
+ * @deprecated all thumbnails should be obtained via
+ * {@link Images.Thumbnails#getThumbnail}, as this
+ * value is no longer supported.
*/
+ @Deprecated
public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
/**
@@ -1947,7 +1956,12 @@
/**
* The mini thumb id.
* <P>Type: INTEGER</P>
+ *
+ * @deprecated all thumbnails should be obtained via
+ * {@link Images.Thumbnails#getThumbnail}, as this
+ * value is no longer supported.
*/
+ @Deprecated
public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
/**
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 138e77b..02a5e6f 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -181,7 +181,7 @@
}
/**
- * Implementation is provided by the parent class. Throws by default, and cannot be overriden.
+ * Implementation is provided by the parent class. Throws by default, and cannot be overridden.
*/
@Override
public final Uri insert(Uri uri, ContentValues values) {
@@ -189,7 +189,7 @@
}
/**
- * Implementation is provided by the parent class. Throws by default, and cannot be overriden.
+ * Implementation is provided by the parent class. Throws by default, and cannot be overridden.
*/
@Override
public final int delete(Uri uri, String selection, String[] selectionArgs) {
@@ -197,7 +197,7 @@
}
/**
- * Implementation is provided by the parent class. Throws by default, and cannot be overriden.
+ * Implementation is provided by the parent class. Throws by default, and cannot be overridden.
*/
@Override
public final int update(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 026195e..aa178fb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -363,6 +363,21 @@
"android.settings.MANAGE_UNKNOWN_APP_SOURCES";
/**
+ * Activity Action: Show the "Open by Default" page in a particular application's details page.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+ * <p>
+ * Input: The Intent's data URI specifies the application package name
+ * to be shown, with the "package" scheme. That is "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS =
+ "com.android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
+
+ /**
* Activity Action: Show trusted credentials settings, opening to the user tab,
* to allow management of installed credentials.
* <p>
@@ -809,21 +824,6 @@
"android.settings.APPLICATION_DETAILS_SETTINGS";
/**
- * Activity Action: Show the "Open by Default" page in a particular application's details page.
- * <p>
- * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
- * <p>
- * Input: The Intent's data URI specifies the application package name
- * to be shown, with the "package" scheme. That is "package:com.my.app".
- * <p>
- * Output: Nothing.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE =
- "android.settings.APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE";
-
- /**
* Activity Action: Show list of applications that have been running
* foreground services (to the user "running in the background").
* <p>
@@ -11537,14 +11537,6 @@
public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
/**
- * Enable faster emergency phone call feature.
- * The value is a boolean (1 or 0).
- * @hide
- */
- public static final String FASTER_EMERGENCY_PHONE_CALL_ENABLED =
- "faster_emergency_phone_call_enabled";
-
- /**
* See RIL_PreferredNetworkType in ril.h
* @hide
*/
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index b1ae7a5..fdbb1c2 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -314,7 +314,8 @@
* is called multiple times passing the same {@code id}, only the last call will be used.
*
* @param id resource id of the child view.
- * @param action action to be performed.
+ * @param action action to be performed. Must be an an implementation provided by the
+ * Android System.
*
* @return this builder
*
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 1695c13..3893f2a 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.os.RemoteException;
+import android.util.Log;
/**
* <p><code>FillCallback</code> handles autofill requests from the {@link AutofillService} into
@@ -28,6 +29,9 @@
* <a href="/guide/topics/text/autofill-services">Build autofill services</a>.
*/
public final class FillCallback {
+
+ private static final String TAG = "FillCallback";
+
private final IFillCallback mCallback;
private final int mRequestId;
private boolean mCalled;
@@ -39,13 +43,20 @@
}
/**
- * Notifies the Android System that an
- * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
- * FillCallback)} was successfully fulfilled by the service.
+ * Notifies the Android System that a fill request
+ * ({@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+ * FillCallback)}) was successfully fulfilled by the service.
*
- * @param response autofill information for that activity, or {@code null} when the activity
- * cannot be autofilled (for example, if it only contains read-only fields). See
- * {@link FillResponse} for examples.
+ * <p>This method should always be called, even if the service doesn't have the heuristics to
+ * fulfill the request (in which case it should be called with {@code null}).
+ *
+ * <p>See the main {@link AutofillService} documentation for more details and examples.
+ *
+ * @param response autofill information for that activity, or {@code null} when the service
+ * cannot autofill the activity.
+ *
+ * @throws IllegalStateException if this method or {@link #onFailure(CharSequence)} was already
+ * called.
*/
public void onSuccess(@Nullable FillResponse response) {
assertNotCalled();
@@ -63,13 +74,28 @@
}
/**
- * Notifies the Android System that an
+ * Notifies the Android System that a fill request (
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
- * FillCallback)} could not be fulfilled by the service.
+ * FillCallback)}) could not be fulfilled by the service (for example, because the user data was
+ * not available yet), so the request could be retried later.
*
- * @param message error message to be displayed to the user.
+ * <p><b>Note: </b>this method should not be used when the service didn't have the heursitics to
+ * fulfill the request; in this case, the service should call {@link #onSuccess(FillResponse)
+ * onSuccess(null)} instead.
+ *
+ * <p><b>Note: </b>on Android versions up to {@link android.os.Build.VERSION_CODES#P}, this
+ * method is not working as intended, and the service should call
+ * {@link #onSuccess(FillResponse) onSuccess(null)} instead.
+ *
+ * @param message error message to be displayed to the user. <b>Note: </b> this message is
+ * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+ * Information, such as username or email address).
+ *
+ * @throws IllegalStateException if this method or {@link #onSuccess(FillResponse)} was already
+ * called.
*/
public void onFailure(@Nullable CharSequence message) {
+ Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
assertNotCalled();
mCalled = true;
try {
diff --git a/core/java/android/service/autofill/OnClickAction.java b/core/java/android/service/autofill/OnClickAction.java
index 7439b00..8597a88 100644
--- a/core/java/android/service/autofill/OnClickAction.java
+++ b/core/java/android/service/autofill/OnClickAction.java
@@ -21,6 +21,9 @@
*
* <p>Typically used to switch the visibility of other views in a
* {@link CustomDescription custom save UI}.
+ *
+ * <p><b>Note:</b> This interface is not meant to be implemented by app developers; only
+ * implementations provided by the Android System can be used in other Autofill APIs.
*/
public interface OnClickAction {
}
diff --git a/core/java/android/service/autofill/Sanitizer.java b/core/java/android/service/autofill/Sanitizer.java
index 38757ac..8a1310d 100644
--- a/core/java/android/service/autofill/Sanitizer.java
+++ b/core/java/android/service/autofill/Sanitizer.java
@@ -21,6 +21,9 @@
* <p>Typically used to avoid displaying the save UI for values that are autofilled but reformatted
* by the app—for example, if the autofill service sends a credit card number
* value as "004815162342108" and the app automatically changes it to "0048 1516 2342 108".
+ *
+ * <p><b>Note:</b> This interface is not meant to be implemented by app developers; only
+ * implementations provided by the Android System can be used in other Autofill APIs.
*/
public interface Sanitizer {
}
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 855981a..0625095 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.content.IntentSender;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -29,6 +30,9 @@
* autofilled.
*/
public final class SaveCallback {
+
+ private static final String TAG = "SaveCallback";
+
private final ISaveCallback mCallback;
private boolean mCalled;
@@ -41,6 +45,9 @@
* Notifies the Android System that an
* {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled
* by the service.
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess(IntentSender)}, or
+ * {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess() {
onSuccessInternal(null);
@@ -58,6 +65,9 @@
*
* @param intentSender intent that will be launched from the context of activity being
* autofilled.
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess()},
+ * or {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess(@NonNull IntentSender intentSender) {
onSuccessInternal(Preconditions.checkNotNull(intentSender));
@@ -86,9 +96,15 @@
* you prefer to show your own message, call {@link #onSuccess()} or
* {@link #onSuccess(IntentSender)} instead.
*
- * @param message error message to be displayed to the user.
+ * @param message error message to be displayed to the user. <b>Note: </b> this message is
+ * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+ * Information, such as username or email address).
+ *
+ * @throws IllegalStateException if this method, {@link #onSuccess()},
+ * or {@link #onSuccess(IntentSender)} was already called.
*/
public void onFailure(CharSequence message) {
+ Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
assertNotCalled();
mCalled = true;
try {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index f571956..dfaf49a 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -235,12 +235,17 @@
*/
public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
+
/**
- * Don't trigger the autofill save UI when the autofill context associated with the response
- * associated with this {@link SaveInfo} is {@link AutofillManager#commit() committed},
- * but keep its {@link FillContext} so it's delivered in a future
- * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) save request} of an
- * activity belonging to the same task.
+ * Postpone the autofill save UI.
+ *
+ * <p>If flag is set, the autofill save UI is not triggered when the
+ * autofill context associated with the response associated with this {@link SaveInfo} is
+ * committed (with {@link AutofillManager#commit()}). Instead, the {@link FillContext}
+ * is delivered in future fill requests (with {@link
+ * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)})
+ * and save request (with {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)})
+ * of an activity belonging to the same task.
*
* <p>This flag should be used when the service detects that the application uses
* multiple screens to implement an autofillable workflow (for example, one screen for the
diff --git a/core/java/android/service/autofill/Transformation.java b/core/java/android/service/autofill/Transformation.java
index aa8bc9b..de43955 100644
--- a/core/java/android/service/autofill/Transformation.java
+++ b/core/java/android/service/autofill/Transformation.java
@@ -20,6 +20,9 @@
* template} at runtime, using the values of fields contained in the screen.
*
* <p>Typically used by {@link CustomDescription} to provide a customized autofill save UI.
+ *
+ * <p><b>Note:</b> This interface is not meant to be implemented by app developers; only
+ * implementations provided by the Android System can be used in other Autofill APIs.
*/
public interface Transformation {
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 15b2aae..38de794 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -626,7 +627,7 @@
* @see #startDozing
* @hide For use by system UI components only.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean canDoze() {
return mCanDoze;
}
@@ -694,7 +695,7 @@
* @see #startDozing
* @hide For use by system UI components only.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void stopDozing() {
if (mDozing) {
mDozing = false;
@@ -716,7 +717,7 @@
* @see #setDozing(boolean)
* @hide For use by system UI components only.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean isDozing() {
return mDozing;
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 26240c5..c1a3c2b 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -283,6 +283,9 @@
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
throw ex.rethrowFromSystemServer();
+ } catch (SecurityException e) {
+ // app cannot catch and recover from this, so do on their behalf
+ Log.w(TAG, "Enqueue adjustment failed; no longer connected", e);
}
}
break;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 3319c9e..d7fe15d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -280,7 +280,7 @@
private final Object mLock = new Object();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Handler mHandler;
/** @hide */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f2fa45d..5012d77 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -146,6 +146,7 @@
private static final String RULE_ATT_CONDITION_ID = "conditionId";
private static final String RULE_ATT_CREATION_TIME = "creationTime";
private static final String RULE_ATT_ENABLER = "enabler";
+ private static final String RULE_ATT_MODIFIED = "modified";
@UnsupportedAppUsage
public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
@@ -633,6 +634,7 @@
Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
+ rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
return rt;
}
@@ -656,6 +658,7 @@
if (rule.condition != null) {
writeConditionXml(rule.condition, out);
}
+ out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified));
}
public static Condition readConditionXml(XmlPullParser parser) {
@@ -1456,6 +1459,7 @@
public long creationTime; // required for automatic
public String enabler; // package name, only used for manual rules.
public ZenPolicy zenPolicy;
+ public boolean modified; // rule has been modified from initial creation
public ZenRule() { }
@@ -1477,6 +1481,7 @@
enabler = source.readString();
}
zenPolicy = source.readParcelable(null);
+ modified = source.readInt() == 1;
}
@Override
@@ -1512,6 +1517,7 @@
dest.writeInt(0);
}
dest.writeParcelable(zenPolicy, 0);
+ dest.writeInt(modified ? 1 : 0);
}
@Override
@@ -1528,6 +1534,7 @@
.append(",creationTime=").append(creationTime)
.append(",enabler=").append(enabler)
.append(",zenPolicy=").append(zenPolicy)
+ .append(",modified=").append(modified)
.append(']').toString();
}
@@ -1551,7 +1558,10 @@
if (component != null) {
component.writeToProto(proto, ZenRuleProto.COMPONENT);
}
- // TODO: write zenPolicy to proto (b/115370281)
+ if (zenPolicy != null) {
+ zenPolicy.writeToProto(proto, ZenRuleProto.ZEN_POLICY);
+ }
+ proto.write(ZenRuleProto.MODIFIED, modified);
proto.end(token);
}
@@ -1604,6 +1614,9 @@
if (!Objects.equals(zenPolicy, to.zenPolicy)) {
d.addLine(item, "zenPolicy", zenPolicy, to.zenPolicy);
}
+ if (modified != to.modified) {
+ d.addLine(item, "modified", modified, to.modified);
+ }
}
@Override
@@ -1620,13 +1633,14 @@
&& Objects.equals(other.component, component)
&& Objects.equals(other.id, id)
&& Objects.equals(other.enabler, enabler)
- && Objects.equals(other.zenPolicy, zenPolicy);
+ && Objects.equals(other.zenPolicy, zenPolicy)
+ && other.modified == modified;
}
@Override
public int hashCode() {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, id, enabler, zenPolicy);
+ component, id, enabler, zenPolicy, modified);
}
public boolean isAutomaticActive() {
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 88825f0..1ccf529 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -21,6 +21,7 @@
import android.app.NotificationChannel;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -852,6 +853,34 @@
}
/**
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(ZenPolicyProto.REMINDERS, getPriorityCategoryReminders());
+ proto.write(ZenPolicyProto.EVENTS, getPriorityCategoryEvents());
+ proto.write(ZenPolicyProto.MESSAGES, getPriorityCategoryMessages());
+ proto.write(ZenPolicyProto.CALLS, getPriorityCategoryCalls());
+ proto.write(ZenPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
+ proto.write(ZenPolicyProto.ALARMS, getPriorityCategoryAlarms());
+ proto.write(ZenPolicyProto.MEDIA, getPriorityCategoryMedia());
+ proto.write(ZenPolicyProto.SYSTEM, getPriorityCategorySystem());
+
+ proto.write(ZenPolicyProto.FULL_SCREEN_INTENT, getVisualEffectFullScreenIntent());
+ proto.write(ZenPolicyProto.LIGHTS, getVisualEffectLights());
+ proto.write(ZenPolicyProto.PEEK, getVisualEffectPeek());
+ proto.write(ZenPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
+ proto.write(ZenPolicyProto.BADGE, getVisualEffectBadge());
+ proto.write(ZenPolicyProto.AMBIENT, getVisualEffectAmbient());
+ proto.write(ZenPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
+
+ proto.write(ZenPolicyProto.PRIORITY_MESSAGES, getPriorityMessageSenders());
+ proto.write(ZenPolicyProto.PRIORITY_CALLS, getPriorityCallSenders());
+ proto.end(token);
+ }
+
+ /**
* Makes deep copy of this ZenPolicy.
* @hide
*/
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index e3d68a6..24819a6 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -16,6 +16,8 @@
package android.service.voice;
+import com.android.internal.app.IVoiceActionCheckCallback;
+
/**
* @hide
*/
@@ -24,4 +26,6 @@
void soundModelsChanged();
void shutdown();
void launchVoiceAssistFromKeyguard();
+ void getActiveServiceSupportedActions(in List<String> voiceActions,
+ in IVoiceActionCheckCallback callback);
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0bbc07e..e105fdf 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,6 +16,8 @@
package android.service.voice;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.UnsupportedAppUsage;
import android.app.Service;
@@ -26,17 +28,22 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
/**
* Top-level service of the current global voice interactor, which is providing
@@ -71,23 +78,43 @@
public static final String SERVICE_META_DATA = "android.voice_interaction";
IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
- @Override public void ready() {
- mHandler.sendEmptyMessage(MSG_READY);
- }
- @Override public void shutdown() {
- mHandler.sendEmptyMessage(MSG_SHUTDOWN);
- }
- @Override public void soundModelsChanged() {
- mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
- }
@Override
- public void launchVoiceAssistFromKeyguard() throws RemoteException {
- mHandler.sendEmptyMessage(MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD);
+ public void ready() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onReady, VoiceInteractionService.this));
+ }
+
+ @Override
+ public void shutdown() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+ }
+
+ @Override
+ public void soundModelsChanged() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onSoundModelsChangedInternal,
+ VoiceInteractionService.this));
+ }
+
+ @Override
+ public void launchVoiceAssistFromKeyguard() {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ VoiceInteractionService::onLaunchVoiceAssistFromKeyguard,
+ VoiceInteractionService.this));
+ }
+
+ @Override
+ public void getActiveServiceSupportedActions(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ Handler.getMain().executeOrSendMessage(
+ PooledLambda.obtainMessage(VoiceInteractionService::onHandleVoiceActionCheck,
+ VoiceInteractionService.this,
+ voiceActions,
+ callback));
}
};
- MyHandler mHandler;
-
IVoiceInteractionManagerService mSystemService;
private final Object mLock = new Object();
@@ -96,33 +123,6 @@
private AlwaysOnHotwordDetector mHotwordDetector;
- static final int MSG_READY = 1;
- static final int MSG_SHUTDOWN = 2;
- static final int MSG_SOUND_MODELS_CHANGED = 3;
- static final int MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD = 4;
-
- class MyHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_READY:
- onReady();
- break;
- case MSG_SHUTDOWN:
- onShutdownInternal();
- break;
- case MSG_SOUND_MODELS_CHANGED:
- onSoundModelsChangedInternal();
- break;
- case MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD:
- onLaunchVoiceAssistFromKeyguard();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
-
/**
* Called when a user has activated an affordance to launch voice assist from the Keyguard.
*
@@ -186,7 +186,7 @@
* be any combination of
* {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
* {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
- * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
+ * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
* to request that the system generate and deliver assist data on the current foreground
* app as part of showing the session UI.
*/
@@ -200,10 +200,22 @@
}
}
- @Override
- public void onCreate() {
- super.onCreate();
- mHandler = new MyHandler();
+ /**
+ * Request to query for what extended voice actions this service supports. This method will
+ * be called when the system checks the supported actions of this
+ * {@link VoiceInteractionService}. Supported actions may be delivered to
+ * {@link VoiceInteractionSession} later to request a session to perform an action.
+ *
+ * <p>Voice actions are defined in support libraries and could vary based on platform context.
+ * For example, car related voice actions will be defined in car support libraries.
+ *
+ * @param voiceActions A set of checked voice actions.
+ * @return Returns a subset of checked voice actions. Additional voice actions in the
+ * returned set will be ignored. Returns null or empty set if no actions are supported.
+ */
+ @Nullable
+ public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) {
+ return null;
}
@Override
@@ -254,6 +266,18 @@
}
}
+ private void onHandleVoiceActionCheck(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ if (callback != null) {
+ try {
+ Set<String> voiceActionsSet = new ArraySet<>(voiceActions);
+ Set<String> resultSet = onGetSupportedVoiceActions(voiceActionsSet);
+ callback.onComplete(resultSet == null ? null : new ArrayList<>(resultSet));
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/**
* Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
* This instance must be retained and used by the client.
@@ -289,12 +313,12 @@
}
/**
- * Checks if a given keyphrase and locale are supported to create an
- * {@link AlwaysOnHotwordDetector}.
- *
- * @return true if the keyphrase and locale combination is supported, false otherwise.
- * @hide
- */
+ * Checks if a given keyphrase and locale are supported to create an
+ * {@link AlwaysOnHotwordDetector}.
+ *
+ * @return true if the keyphrase and locale combination is supported, false otherwise.
+ * @hide
+ */
@UnsupportedAppUsage
public final boolean isKeyphraseAndLocaleSupportedForHotword(String keyphrase, Locale locale) {
if (mKeyphraseEnrollmentInfo == null) {
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index c46c831..c928da1 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -23,6 +23,7 @@
import android.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.os.Build;
import android.text.style.ReplacementSpan;
import android.text.style.UpdateLayout;
import android.text.style.WrapTogetherSpan;
@@ -354,7 +355,7 @@
* @deprecated Use {@link Builder} instead.
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public DynamicLayout(@NonNull CharSequence base, @NonNull CharSequence display,
@NonNull TextPaint paint,
@IntRange(from = 0) int width,
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 6edf845..6c15446 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -58,6 +58,6 @@
/**
* Just like {@link Paint#getTextRunCursor}.
*/
- int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
+ int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
int cursorOpt, Paint p);
}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 33c977b..c89617f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -619,7 +619,7 @@
previousLineBottom = lbottom;
int lbaseline = lbottom - getLineDescent(i);
- if (start >= spanEnd) {
+ if (end >= spanEnd) {
// These should be infrequent, so we'll use this so that
// we don't have to check as often.
spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 9d841e8..c5fabaf 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1551,7 +1551,7 @@
*
* @param contextStart the start index of the context
* @param contextEnd the (non-inclusive) end index of the context
- * @param dir either DIRECTION_RTL or DIRECTION_LTR
+ * @param dir 1 if the run is RTL, otherwise 0
* @param offset the cursor position to move from
* @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
* CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
@@ -1563,21 +1563,28 @@
@Deprecated
public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
int cursorOpt, Paint p) {
+ return getTextRunCursor(contextStart, contextEnd, dir == 1, offset, cursorOpt, p);
+ }
+
+ /** @hide */
+ @Override
+ public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
+ int cursorOpt, Paint p) {
int ret;
int contextLen = contextEnd - contextStart;
if (contextEnd <= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart, contextLen,
- dir, offset, cursorOpt);
+ isRtl, offset, cursorOpt);
} else if (contextStart >= mGapStart) {
ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
- dir, offset + mGapLength, cursorOpt) - mGapLength;
+ isRtl, offset + mGapLength, cursorOpt) - mGapLength;
} else {
char[] buf = TextUtils.obtain(contextLen);
getChars(contextStart, contextEnd, buf, 0);
ret = p.getTextRunCursor(buf, 0, contextLen,
- dir, offset - contextStart, cursorOpt) + contextStart;
+ isRtl, offset - contextStart, cursorOpt) + contextStart;
TextUtils.recycle(buf);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5adb1ca..e1ffef0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
+import android.os.Build;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
@@ -1293,7 +1294,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getHeight(boolean cap) {
if (cap && mLineCount > mMaximumVisibleLineCount && mMaxLineHeight == -1
&& Log.isLoggable(TAG, Log.WARN)) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index bf2d600..b49a949 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -22,6 +22,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
+import android.os.Build;
import android.text.Layout.Directions;
import android.text.Layout.TabStops;
import android.text.style.CharacterStyle;
@@ -61,7 +62,7 @@
private TabStops mTabs;
private char[] mChars;
private boolean mCharsValid;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Spanned mSpanned;
private PrecomputedText mComputed;
@@ -801,14 +802,13 @@
}
}
- int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
if (mCharsValid) {
return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
- dir, offset, cursorOpt);
+ runIsRtl, offset, cursorOpt);
} else {
return wp.getTextRunCursor(mText, mStart + spanStart,
- mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart;
+ mStart + spanLimit, runIsRtl, mStart + offset, cursorOpt) - mStart;
}
}
diff --git a/core/java/android/text/TextWatcher.java b/core/java/android/text/TextWatcher.java
index bad09f2..a0aef69 100644
--- a/core/java/android/text/TextWatcher.java
+++ b/core/java/android/text/TextWatcher.java
@@ -17,7 +17,7 @@
package android.text;
/**
- * When an object of a type is attached to an Editable, its methods will
+ * When an object of this type is attached to an Editable, its methods will
* be called when the text is changed.
*/
public interface TextWatcher extends NoCopySpan {
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 5f0a46d..d4bcd12 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -310,7 +310,7 @@
return len;
}
- offset = paint.getTextRunCursor(text, offset, len, Paint.DIRECTION_LTR /* not used */,
+ offset = paint.getTextRunCursor(text, offset, len, false /* LTR, not used */,
offset, Paint.CURSOR_AFTER);
return adjustReplacementSpan(text, offset, false /* move to the end */);
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 5210447..be47320 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -420,7 +420,7 @@
intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, hashCode());
context.sendBroadcast(intent);
} else {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = context.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.notifySuggestionPicked(this, original, index);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 0476cf8..183e833 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -35,6 +35,7 @@
public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
+ public static final String EMERGENCY_DIAL_SHORTCUTS = "settings_emergency_dial_shortcuts";
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -45,7 +46,9 @@
DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "false");
DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
+ DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
+ DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "false");
}
/**
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 533d725..1203541 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -410,11 +410,11 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
- static byte[] generateFsverityRootHash(String apkPath)
+ static byte[] generateApkVerityRootHash(String apkPath)
throws IOException, SignatureNotFoundException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -423,7 +423,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateFsverityRootHash(
+ return ApkVerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 758cd2b..939522d 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -534,11 +534,11 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
- static byte[] generateFsverityRootHash(String apkPath)
+ static byte[] generateApkVerityRootHash(String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException,
SignatureNotFoundException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -547,7 +547,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateFsverityRootHash(
+ return ApkVerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index de9f55b..a299b11 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -432,16 +432,16 @@
*
* @return FSverity root hash
*/
- public static byte[] generateFsverityRootHash(String apkPath)
+ public static byte[] generateApkVerityRootHash(String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
// first try v3
try {
- return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureSchemeV3Verifier.generateApkVerityRootHash(apkPath);
} catch (SignatureNotFoundException e) {
// try older version
}
try {
- return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureSchemeV2Verifier.generateApkVerityRootHash(apkPath);
} catch (SignatureNotFoundException e) {
return null;
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index e247c87..081033a 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -332,7 +332,7 @@
try {
byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
apk.length(), signatureInfo);
- ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
+ ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk,
signatureInfo, new ByteBufferFactory() {
@Override
public ByteBuffer create(int capacity) {
@@ -348,26 +348,6 @@
}
/**
- * Generates the fsverity header and hash tree to be used by kernel for the given apk. This
- * method does not check whether the root hash exists in the Signing Block or not.
- *
- * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
- * ByteBufferFactory}.
- *
- * @return the root hash of the generated hash tree.
- */
- public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
- SignatureInfo signatureInfo)
- throws IOException, SignatureNotFoundException, SecurityException, DigestException,
- NoSuchAlgorithmException {
- try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
- ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
- signatureInfo, bufferFactory);
- return result.rootHash;
- }
- }
-
- /**
* Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
*
* @throws IOException if an I/O error occurs while reading the file.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 2dd0117..553511d 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -16,6 +16,9 @@
package android.util.apk;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
@@ -26,8 +29,10 @@
import java.util.ArrayList;
/**
- * ApkVerityBuilder builds the APK verity tree and the verity header, which will be used by the
- * kernel to verity the APK content on access.
+ * ApkVerityBuilder builds the APK verity tree and the verity header. The generated tree format can
+ * be stored on disk for apk-verity setup and used by kernel. Note that since the current
+ * implementation is different from the upstream, we call this implementation apk-verity instead of
+ * fs-verity.
*
* <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to
* the existing APK format, it has to skip APK Signing Block and also has some special treatment for
@@ -47,26 +52,28 @@
private static final byte[] DEFAULT_SALT = new byte[8];
static class ApkVerityResult {
- public final ByteBuffer fsverityData;
+ public final ByteBuffer verityData;
+ public final int merkleTreeSize;
public final byte[] rootHash;
- ApkVerityResult(ByteBuffer fsverityData, byte[] rootHash) {
- this.fsverityData = fsverityData;
+ ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+ this.verityData = verityData;
+ this.merkleTreeSize = merkleTreeSize;
this.rootHash = rootHash;
}
}
/**
- * Generates fsverity metadata and the Merkle tree into the {@link ByteBuffer} created by the
- * {@link ByteBufferFactory}. The bytes layout in the buffer will be used by the kernel and is
- * ready to be appended to the target file to set up fsverity. For fsverity to work, this data
- * must be placed at the next page boundary, and the caller must add additional padding in that
- * case.
+ * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
+ * ByteBuffer} created by the {@link ByteBufferFactory}. The Merkle tree is suitable to be used
+ * as the on-disk format for apk-verity.
*
- * @return ApkVerityResult containing the fsverity data and the root hash of the Merkle tree.
+ * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+ * front, the tree size, and the calculated root hash.
*/
- static ApkVerityResult generateApkVerity(RandomAccessFile apk,
- SignatureInfo signatureInfo, ByteBufferFactory bufferFactory)
+ @NonNull
+ static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+ @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
long signingBlockSize =
signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
@@ -76,86 +83,69 @@
ByteBuffer output = bufferFactory.create(
merkleTreeSize
- + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata
+ + CHUNK_SIZE_BYTES); // maximum size of apk-verity metadata
output.order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer tree = slice(output, 0, merkleTreeSize);
- ByteBuffer header = slice(output, merkleTreeSize,
- merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES);
- ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES,
- merkleTreeSize + CHUNK_SIZE_BYTES);
- byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES];
- ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes);
- apkDigest.order(ByteOrder.LITTLE_ENDIAN);
+ byte[] apkRootHash = generateApkVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT,
+ levelOffset, tree);
+ return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
+ }
- // NB: Buffer limit is set inside once finished.
- calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions);
-
- // Put the reverse offset to fs-verity header at the end.
- output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit());
- output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit()
- + 4); // size of this integer right before EOF
- output.flip();
-
- return new ApkVerityResult(output, apkDigestBytes);
+ static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
+ @NonNull SignatureInfo signatureInfo, @NonNull ByteBuffer footerOutput)
+ throws IOException {
+ footerOutput.order(ByteOrder.LITTLE_ENDIAN);
+ generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT);
+ long signingBlockSize =
+ signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+ generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset,
+ signingBlockSize, signatureInfo.eocdOffset);
}
/**
- * Calculates the fsverity root hash for integrity measurement. This needs to be consistent to
- * what kernel returns.
+ * Calculates the apk-verity root hash for integrity measurement. This needs to be consistent
+ * to what kernel returns.
*/
- static byte[] generateFsverityRootHash(RandomAccessFile apk, ByteBuffer apkDigest,
- SignatureInfo signatureInfo)
+ @NonNull
+ static byte[] generateApkVerityRootHash(@NonNull RandomAccessFile apk,
+ @NonNull ByteBuffer apkDigest, @NonNull SignatureInfo signatureInfo)
throws NoSuchAlgorithmException, DigestException, IOException {
- ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES)
- .order(ByteOrder.LITTLE_ENDIAN);
- ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES);
- ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES,
- CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES);
+ assertSigningBlockAlignedAndHasFullPages(signatureInfo);
- calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
+ ByteBuffer footer = ByteBuffer.allocate(CHUNK_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN);
+ generateApkVerityFooter(apk, signatureInfo, footer);
+ footer.flip();
MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
- md.update(header);
- md.update(extensions);
+ md.update(footer);
md.update(apkDigest);
return md.digest();
}
/**
- * Internal method to generate various parts of FSVerity constructs, including the header,
- * extensions, Merkle tree, and the tree's root hash. The output buffer is flipped to the
- * generated data size and is readey for consuming.
+ * Generates the apk-verity header and hash tree to be used by kernel for the given apk. This
+ * method does not check whether the root hash exists in the Signing Block or not.
+ *
+ * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
+ * ByteBufferFactory}.
+ *
+ * @return the root hash of the generated hash tree.
*/
- private static void calculateFsveritySignatureInternal(
- RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput,
- ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput)
- throws IOException, NoSuchAlgorithmException, DigestException {
- assertSigningBlockAlignedAndHasFullPages(signatureInfo);
- long signingBlockSize =
- signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
- long dataSize = apk.length() - signingBlockSize;
- int[] levelOffset = calculateVerityLevelOffset(dataSize);
-
- if (treeOutput != null) {
- byte[] apkRootHash = generateApkVerityTree(apk, signatureInfo, DEFAULT_SALT,
- levelOffset, treeOutput);
- if (rootHashOutput != null) {
- rootHashOutput.put(apkRootHash);
- rootHashOutput.flip();
- }
- }
-
- if (headerOutput != null) {
- headerOutput.order(ByteOrder.LITTLE_ENDIAN);
- generateFsverityHeader(headerOutput, apk.length(), levelOffset.length - 1,
- DEFAULT_SALT);
- }
-
- if (extensionsOutput != null) {
- extensionsOutput.order(ByteOrder.LITTLE_ENDIAN);
- generateFsverityExtensions(extensionsOutput, signatureInfo.apkSigningBlockOffset,
- signingBlockSize, signatureInfo.eocdOffset);
+ @NonNull
+ static byte[] generateApkVerity(@NonNull String apkPath,
+ @NonNull ByteBufferFactory bufferFactory, @NonNull SignatureInfo signatureInfo)
+ throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+ NoSuchAlgorithmException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+ ApkVerityResult result = generateApkVerityTree(apk, signatureInfo, bufferFactory);
+ ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
+ result.verityData.limit());
+ generateApkVerityFooter(apk, signatureInfo, footer);
+ // Put the reverse offset to apk-verity header at the end.
+ footer.putInt(footer.position() + 4);
+ result.verityData.limit(result.merkleTreeSize + footer.position());
+ return result.rootHash;
}
}
@@ -297,9 +287,13 @@
digester.fillUpLastOutputChunk();
}
- private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo,
- byte[] salt, int[] levelOffset, ByteBuffer output)
+ @NonNull
+ private static byte[] generateApkVerityTreeInternal(@NonNull RandomAccessFile apk,
+ @Nullable SignatureInfo signatureInfo, @NonNull byte[] salt,
+ @NonNull int[] levelOffset, @NonNull ByteBuffer output)
throws IOException, NoSuchAlgorithmException, DigestException {
+ assertSigningBlockAlignedAndHasFullPages(signatureInfo);
+
// 1. Digest the apk to generate the leaf level hashes.
generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
@@ -324,7 +318,7 @@
return rootHash;
}
- private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth,
+ private static ByteBuffer generateApkVerityHeader(ByteBuffer buffer, long fileSize,
byte[] salt) {
if (salt.length != 8) {
throw new IllegalArgumentException("salt is not 8 bytes long");
@@ -351,13 +345,12 @@
buffer.put(salt); // salt (8 bytes)
skip(buffer, 22); // reserved
- buffer.flip();
return buffer;
}
- private static ByteBuffer generateFsverityExtensions(ByteBuffer buffer, long signingBlockOffset,
- long signingBlockSize, long eocdOffset) {
- // Snapshot of the FSVerity structs (subject to change once upstreamed).
+ private static ByteBuffer generateApkVerityExtensions(ByteBuffer buffer,
+ long signingBlockOffset, long signingBlockSize, long eocdOffset) {
+ // Snapshot of the experimental fs-verity structs (different from upstream).
//
// struct fsverity_extension_elide {
// __le64 offset;
@@ -409,7 +402,6 @@
skip(buffer, kPadding); // padding
}
- buffer.flip();
return buffer;
}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index ce16ffc..a872776 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -22,6 +22,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -153,7 +154,7 @@
public String toString() { return "FRAME_CALLBACK_TOKEN"; }
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final Object mLock = new Object();
private final Looper mLooper;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bfe1e95..3bab87a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -17,8 +17,6 @@
package android.view;
import com.android.internal.os.IResultReceiver;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
@@ -71,8 +69,7 @@
boolean stopViewServer(); // Transaction #2
boolean isViewServerRunning(); // Transaction #3
- IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client,
- in IInputContext inputContext);
+ IWindowSession openSession(in IWindowSessionCallback callback);
void getInitialDisplaySize(int displayId, out Point size);
void getBaseDisplaySize(int displayId, out Point size);
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 8641d7f..c38fcf3 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.method.MetaKeyKeyListener;
@@ -1256,7 +1257,7 @@
@UnsupportedAppUsage
private int mDeviceId;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mSource;
private int mDisplayId;
@UnsupportedAppUsage
@@ -1658,7 +1659,7 @@
* @hide
*/
@Override
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final void recycle() {
super.recycle();
mCharacters = null;
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index dc097a1..cf11fd0 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -31,6 +31,7 @@
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -146,7 +147,7 @@
private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>();
private static boolean sUseLargeIcons = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mType;
private int mSystemIconResourceId;
@UnsupportedAppUsage
@@ -319,7 +320,7 @@
* @throws IllegalArgumentException if context is null.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public PointerIcon load(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 79eafa8..7271a9e 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -70,15 +70,8 @@
private static native void nativeDestroy(long nativeObject);
private static native void nativeDisconnect(long nativeObject);
- private static native Bitmap nativeScreenshot(IBinder displayToken,
- Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean allLayers, boolean useIdentityTransform, int rotation);
- private static native GraphicBuffer nativeScreenshotToBuffer(IBinder displayToken,
- Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean allLayers, boolean useIdentityTransform, int rotation);
- private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
- Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean allLayers, boolean useIdentityTransform);
+ private static native GraphicBuffer nativeScreenshot(IBinder displayToken,
+ Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation);
private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
Rect sourceCrop, float frameScale);
@@ -1189,52 +1182,39 @@
}
/**
- * Copy the current screen contents into the provided {@link Surface}
- *
- * @param display The display to take the screenshot of.
- * @param consumer The {@link Surface} to take the screenshot into.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param minLayer The lowest (bottom-most Z order) surface layer to
- * include in the screenshot.
- * @param maxLayer The highest (top-most Z order) surface layer to
- * include in the screenshot.
- * @param useIdentityTransform Replace whatever transformation (rotation,
- * scaling, translation) the surface layers are currently using with the
- * identity transformation while taking the screenshot.
- */
- public static void screenshot(IBinder display, Surface consumer,
- int width, int height, int minLayer, int maxLayer,
- boolean useIdentityTransform) {
- screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer,
- false, useIdentityTransform);
- }
-
- /**
- * Copy the current screen contents into the provided {@link Surface}
- *
- * @param display The display to take the screenshot of.
- * @param consumer The {@link Surface} to take the screenshot into.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- */
- public static void screenshot(IBinder display, Surface consumer,
- int width, int height) {
- screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false);
- }
-
- /**
- * Copy the current screen contents into the provided {@link Surface}
- *
- * @param display The display to take the screenshot of.
- * @param consumer The {@link Surface} to take the screenshot into.
+ * @see SurfaceControl#screenshot(IBinder, Surface, Rect, int, int, boolean, int)
*/
public static void screenshot(IBinder display, Surface consumer) {
- screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false);
+ screenshot(display, consumer, new Rect(), 0, 0, false, 0);
+ }
+
+ /**
+ * Copy the current screen contents into the provided {@link Surface}
+ *
+ * @param consumer The {@link Surface} to take the screenshot into.
+ * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)
+ */
+ public static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width,
+ int height, boolean useIdentityTransform, int rotation) {
+ if (consumer == null) {
+ throw new IllegalArgumentException("consumer must not be null");
+ }
+
+ final GraphicBuffer buffer = screenshotToBuffer(display, sourceCrop, width, height,
+ useIdentityTransform, rotation);
+ try {
+ consumer.attachAndQueueBuffer(buffer);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failed to take screenshot - " + e.getMessage());
+ }
+ }
+
+ /**
+ * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
+ */
+ @UnsupportedAppUsage
+ public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
+ return screenshot(sourceCrop, width, height, false, rotation);
}
/**
@@ -1242,79 +1222,16 @@
* Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into
* a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
*
- * CAVEAT: Versions of screenshot that return a {@link Bitmap} can
- * be extremely slow; avoid use unless absolutely necessary; prefer
- * the versions that use a {@link Surface} instead, such as
- * {@link SurfaceControl#screenshot(IBinder, Surface)}.
+ * CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use
+ * unless absolutely necessary; prefer the versions that use a {@link Surface} such as
+ * {@link SurfaceControl#screenshot(IBinder, Surface)} or {@link GraphicBuffer} such as
+ * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
*
- * @param sourceCrop The portion of the screen to capture into the Bitmap;
- * caller may pass in 'new Rect()' if no cropping is desired.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param minLayer The lowest (bottom-most Z order) surface layer to
- * include in the screenshot.
- * @param maxLayer The highest (top-most Z order) surface layer to
- * include in the screenshot.
- * @param useIdentityTransform Replace whatever transformation (rotation,
- * scaling, translation) the surface layers are currently using with the
- * identity transformation while taking the screenshot.
- * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
- * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take
- * screenshots in its native portrait orientation by default, so this is
- * useful for returning screenshots that are independent of device
- * orientation.
- * @return Returns a hardware Bitmap containing the screen contents, or null
- * if an error occurs. Make sure to call Bitmap.recycle() as soon as
- * possible, once its content is not needed anymore.
+ * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
*/
@UnsupportedAppUsage
public static Bitmap screenshot(Rect sourceCrop, int width, int height,
- int minLayer, int maxLayer, boolean useIdentityTransform,
- int rotation) {
- // TODO: should take the display as a parameter
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
- return nativeScreenshot(displayToken, sourceCrop, width, height,
- minLayer, maxLayer, false, useIdentityTransform, rotation);
- }
-
- /**
- * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)}
- * but returns a GraphicBuffer.
- */
- public static GraphicBuffer screenshotToBuffer(Rect sourceCrop, int width, int height,
- int minLayer, int maxLayer, boolean useIdentityTransform,
- int rotation) {
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
- return nativeScreenshotToBuffer(displayToken, sourceCrop, width, height,
- minLayer, maxLayer, false, useIdentityTransform, rotation);
- }
-
- /**
- * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)} but
- * includes all Surfaces in the screenshot. This will also update the orientation so it
- * sends the correct coordinates to SF based on the rotation value.
- *
- * @param sourceCrop The portion of the screen to capture into the Bitmap;
- * caller may pass in 'new Rect()' if no cropping is desired.
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
- * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take
- * screenshots in its native portrait orientation by default, so this is
- * useful for returning screenshots that are independent of device
- * orientation.
- * @return Returns a Bitmap containing the screen contents, or null
- * if an error occurs. Make sure to call Bitmap.recycle() as soon as
- * possible, once its content is not needed anymore.
- */
- @UnsupportedAppUsage
- public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
+ boolean useIdentityTransform, int rotation) {
// TODO: should take the display as a parameter
IBinder displayToken = SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
@@ -1323,22 +1240,45 @@
}
SurfaceControl.rotateCropForSF(sourceCrop, rotation);
- return nativeScreenshot(displayToken, sourceCrop, width, height, 0, 0, true,
- false, rotation);
+ final GraphicBuffer buffer = screenshotToBuffer(displayToken, sourceCrop, width, height,
+ useIdentityTransform, rotation);
+
+ if (buffer == null) {
+ Log.w(TAG, "Failed to take screenshot");
+ return null;
+ }
+ return Bitmap.createHardwareBitmap(buffer);
}
- @UnsupportedAppUsage
- private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop,
- int width, int height, int minLayer, int maxLayer, boolean allLayers,
- boolean useIdentityTransform) {
+ /**
+ * Captures all the surfaces in a display and returns a {@link GraphicBuffer} with the content.
+ *
+ * @param display The display to take the screenshot of.
+ * @param sourceCrop The portion of the screen to capture into the Bitmap; caller may
+ * pass in 'new Rect()' if no cropping is desired.
+ * @param width The desired width of the returned bitmap; the raw screen will be
+ * scaled down to this size; caller may pass in 0 if no scaling is
+ * desired.
+ * @param height The desired height of the returned bitmap; the raw screen will
+ * be scaled down to this size; caller may pass in 0 if no scaling
+ * is desired.
+ * @param useIdentityTransform Replace whatever transformation (rotation, scaling, translation)
+ * the surface layers are currently using with the identity
+ * transformation while taking the screenshot.
+ * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
+ * Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take
+ * screenshots in its native portrait orientation by default, so
+ * this is useful for returning screenshots that are independent of
+ * device orientation.
+ * @return Returns a GraphicBuffer that contains the captured content.
+ */
+ public static GraphicBuffer screenshotToBuffer(IBinder display, Rect sourceCrop, int width,
+ int height, boolean useIdentityTransform, int rotation) {
if (display == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- if (consumer == null) {
- throw new IllegalArgumentException("consumer must not be null");
- }
- nativeScreenshot(display, consumer, sourceCrop, width, height,
- minLayer, maxLayer, allLayers, useIdentityTransform);
+
+ return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation);
}
private static void rotateCropForSF(Rect crop, int rot) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9bc53ed..19e3f7f62 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4582,7 +4582,7 @@
/**
* Object that handles automatic animation of view properties.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private ViewPropertyAnimator mAnimator = null;
/**
@@ -6876,7 +6876,7 @@
* @param requestCode The request code to use.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void startActivityForResult(Intent intent, int requestCode) {
mStartActivityRequestWho = "@android:view:" + System.identityHashCode(this);
getContext().startActivityForResult(mStartActivityRequestWho, intent, requestCode, null);
@@ -7315,7 +7315,7 @@
// Here we check whether we still need the default focus highlight, and switch it on/off.
switchDefaultFocusHighlight();
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (!gainFocus) {
if (isPressed()) {
setPressed(false);
@@ -8523,6 +8523,11 @@
}
if (importance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
|| importance == IMPORTANT_FOR_AUTOFILL_NO) {
+ if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
+ Log.v(AUTOFILL_LOG_TAG, "View (autofillId=" + getAutofillViewId() + ", "
+ + getClass() + ") is not important for autofill because its "
+ + "importance is " + importance);
+ }
return false;
}
@@ -12476,7 +12481,7 @@
mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
onFinishTemporaryDetach();
if (hasWindowFocus() && hasFocus()) {
- InputMethodManager.getInstance().focusIn(this);
+ getContext().getSystemService(InputMethodManager.class).focusIn(this);
}
notifyEnterOrExitForAutoFillIfNeeded(true);
}
@@ -12867,7 +12872,7 @@
* focus, false otherwise.
*/
public void onWindowFocusChanged(boolean hasWindowFocus) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (!hasWindowFocus) {
if (isPressed()) {
setPressed(false);
@@ -17932,7 +17937,7 @@
rebuildOutline();
if (isFocused()) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.focusIn(this);
}
@@ -18406,7 +18411,7 @@
* communicate with the window manager.
* @return the session object to communicate with the window manager
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
/*package*/ IWindowSession getWindowSession() {
return mAttachInfo != null ? mAttachInfo.mSession : null;
}
@@ -18515,7 +18520,7 @@
onDetachedFromWindow();
onDetachedFromWindowInternal();
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.onViewDetachedFromWindow(this);
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0119d2e..1b3e62d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -8116,7 +8116,7 @@
/**
* Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
* to be done so that the new margins are taken into account. Left and right margins may be
- * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
+ * overridden by {@link android.view.View#requestLayout()} depending on layout direction.
* Margin values should be positive.
*
* @param left the left margin size
@@ -8146,8 +8146,8 @@
/**
* Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
* needs to be done so that the new relative margins are taken into account. Left and right
- * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
- * direction. Margin values should be positive.
+ * margins may be overridden by {@link android.view.View#requestLayout()} depending on
+ * layout direction. Margin values should be positive.
*
* @param start the start margin size
* @param top the top margin size
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 16d202b..2ee7ab9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2579,7 +2579,7 @@
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2695,7 +2695,7 @@
mLastWasImTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPreWindowFocus(mView, hasWindowFocus);
}
@@ -4329,7 +4329,7 @@
enqueueInputEvent(event, null, 0, true);
} break;
case MSG_CHECK_FOCUS: {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.checkFocus();
}
@@ -4871,7 +4871,7 @@
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index f436962..982737a 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.WindowConfiguration;
import android.content.Context;
@@ -422,7 +423,7 @@
* @return boolean You must return true for the panel to be displayed;
* if you return false it will not be shown.
*/
- public boolean onCreatePanelMenu(int featureId, Menu menu);
+ boolean onCreatePanelMenu(int featureId, @NonNull Menu menu);
/**
* Prepare a panel to be displayed. This is called right before the
@@ -438,7 +439,7 @@
*
* @see #onCreatePanelView
*/
- public boolean onPreparePanel(int featureId, View view, Menu menu);
+ boolean onPreparePanel(int featureId, @Nullable View view, @NonNull Menu menu);
/**
* Called when a panel's menu is opened by the user. This may also be
@@ -450,7 +451,7 @@
* @return Return true to allow the menu to open, or false to prevent
* the menu from opening.
*/
- public boolean onMenuOpened(int featureId, Menu menu);
+ boolean onMenuOpened(int featureId, @NonNull Menu menu);
/**
* Called when a panel's menu item has been selected by the user.
@@ -462,7 +463,7 @@
* false to perform the normal menu handling (calling its
* Runnable or sending a Message to its target Handler).
*/
- public boolean onMenuItemSelected(int featureId, MenuItem item);
+ boolean onMenuItemSelected(int featureId, @NonNull MenuItem item);
/**
* This is called whenever the current window attributes change.
@@ -512,7 +513,7 @@
* @param menu If onCreatePanelView() returned null, this is the Menu
* being displayed in the panel.
*/
- public void onPanelClosed(int featureId, Menu menu);
+ void onPanelClosed(int featureId, @NonNull Menu menu);
/**
* Called when the user signals the desire to start a search.
@@ -1079,8 +1080,18 @@
setFlags(flags, flags);
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * Add private flag bits.
+ *
+ * <p>Refer to the individual flags for the permissions needed.
+ *
+ * <p>Note: Only for updateable system components (aka. mainline modules)
+ *
+ * @param flags The flag bits to add.
+ *
+ * @hide
+ */
+ @SystemApi
public void addPrivateFlags(int flags) {
setPrivateFlags(flags, flags);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0404df0..742df5e8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1668,6 +1668,7 @@
* this window is visible.
* @hide
*/
+ @SystemApi
@RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index d810067..92d145c3 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -195,7 +196,11 @@
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
- InputMethodManager imm = InputMethodManager.getInstance();
+ if (InputMethodManager.ENABLE_LEGACY_EAGER_INITIALIZATION) {
+ // Emulate the legacy behavior. The global instance of InputMethodManager
+ // was instantiated here.
+ InputMethodManager.getInstance();
+ }
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@@ -203,8 +208,7 @@
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
- },
- imm.getClient(), imm.getInputContext());
+ });
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -213,7 +217,7 @@
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static IWindowSession peekWindowSession() {
synchronized (WindowManagerGlobal.class) {
return sWindowSession;
@@ -467,7 +471,7 @@
View view = root.getView();
if (view != null) {
- InputMethodManager imm = InputMethodManager.getInstance();
+ InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 3eca854..dccf9d4 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -761,7 +762,7 @@
private CharSequence mPackageName;
private long mEventTime;
int mMovementGranularity;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int mAction;
int mContentChangeTypes;
int mWindowChangeTypes;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index a3fa2ce..4d3f0fc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -632,6 +632,8 @@
private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x0200000;
+ private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000;
+
/**
* Bits that provide the id of a virtual descendant of a view.
*/
@@ -2461,6 +2463,30 @@
}
/**
+ * Returns whether node represents a text entry key that is part of a keyboard or keypad.
+ *
+ * @return {@code true} if the node is a text entry key., {@code false} otherwise.
+ */
+ public boolean isTextEntryKey() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY);
+ }
+
+ /**
+ * Sets whether the node represents a text entry key that is part of a keyboard or keypad.
+ *
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param isTextEntryKey {@code true} if the node is a text entry key, {@code false} otherwise.
+ */
+ public void setTextEntryKey(boolean isTextEntryKey) {
+ setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY, isTextEntryKey);
+ }
+
+ /**
* Gets the package this node comes from.
*
* @return The package name.
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index f89743c..2d1249d 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -41,8 +41,8 @@
* Constructor
*
* @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces
- * an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
- * ease-out effect (i.e., it starts even faster and ends evens slower)
+ * an upside-down y=x^2 parabola. Increasing factor above 1.0f exaggerates the
+ * ease-out effect (i.e., it starts even faster and ends evens slower).
*/
public DecelerateInterpolator(float factor) {
mFactor = factor;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9419e93..e87048e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -45,6 +45,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.KeyEvent;
@@ -559,6 +560,9 @@
// different bridge based on which activity is currently focused
// in the current process. Since compat would be rarely used, just
// create and register a new instance every time.
+ if (sDebug) {
+ Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
+ }
mCompatibilityBridge = new CompatibilityBridge();
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index c51c5e2..635c74f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -57,7 +57,7 @@
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
-import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
@@ -226,6 +226,47 @@
static final String PENDING_EVENT_COUNTER = "aq:imm";
+ /**
+ * {@code true} if we want to instantiate {@link InputMethodManager} eagerly in
+ * {@link android.view.WindowManagerGlobal#getWindowSession()}, which is often called in an
+ * early stage of process startup, which is how Android has worked.
+ *
+ * <p>We still have this settings because we know there are possible compatibility concerns if
+ * we stop doing so. Here are scenarios we know and there could be more scenarios we are not
+ * aware of right know.</p>
+ *
+ * <ul>
+ * <li>Apps that directly access {@link #sInstance} via reflection, which is currently
+ * allowed because of {@link UnsupportedAppUsage} annotation. Currently
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+ * {@link #sInstance} is not {@code null} when such an app is accessing it, but removing
+ * that code from {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal
+ * untested code paths in their apps, which probably happen in an early startup time of that
+ * app.</li>
+ * <li>Apps that directly access {@link #peekInstance()} via reflection, which is currently
+ * allowed because of {@link UnsupportedAppUsage} annotation. Currently
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+ * {@link #peekInstance()} returns non-{@code null} object when such an app is calling
+ * {@link #peekInstance()}, but removing that code from
+ * {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal untested code
+ * paths in their apps, which probably happen in an early startup time of that app. The good
+ * news is that unlike {@link #sInstance}'s case we can at least work around this scenario
+ * by changing the semantics of {@link #peekInstance()}, which is currently defined as
+ * "retrieve the global {@link InputMethodManager} instance, if it exists" to something that
+ * always returns non-{@code null} {@link InputMethodManager}. However, introducing such an
+ * workaround can also trigger different compatibility issues if {@link #peekInstance()} was
+ * called before {@link android.view.WindowManagerGlobal#getWindowSession()} and it expected
+ * {@link #peekInstance()} to return {@code null} as written in the JavaDoc.</li>
+ * </ul>
+ *
+ * <p>TODO(Bug 116157766): Check if we can set {@code false} here then remove this settings.</p>
+ * @hide
+ */
+ public static final boolean ENABLE_LEGACY_EAGER_INITIALIZATION = true;
+
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
@UnsupportedAppUsage
static InputMethodManager sInstance;
@@ -336,7 +377,7 @@
/**
* The InputConnection that was last retrieved from the served view.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
ControlledInputConnectionWrapper mServedInputConnectionWrapper;
/**
* The completions that were last provided by the served view.
@@ -387,9 +428,6 @@
final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
- private final InputMethodPrivilegedOperationsRegistry mPrivOpsRegistry =
- new InputMethodPrivilegedOperationsRegistry();
-
// -----------------------------------------------------------
static final int MSG_DUMP = 1;
@@ -652,11 +690,13 @@
*/
@UnsupportedAppUsage
public static InputMethodManager getInstance() {
- synchronized (InputMethodManager.class) {
+ synchronized (sLock) {
if (sInstance == null) {
try {
- sInstance = new InputMethodManager(Looper.getMainLooper());
- } catch (ServiceNotFoundException e) {
+ final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper());
+ imm.mService.addClient(imm.mClient, imm.mIInputContext);
+ sInstance = imm;
+ } catch (ServiceNotFoundException | RemoteException e) {
throw new IllegalStateException(e);
}
}
@@ -665,13 +705,17 @@
}
/**
- * Private optimization: retrieve the global InputMethodManager instance,
- * if it exists.
+ * Private optimization: retrieve the global InputMethodManager instance, if it exists.
* @hide
+ * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully
+ * support multi-display scenario.
*/
+ @Deprecated
@UnsupportedAppUsage
public static InputMethodManager peekInstance() {
- return sInstance;
+ synchronized (sLock) {
+ return sInstance;
+ }
}
/** @hide */
@@ -739,7 +783,7 @@
*/
@Deprecated
public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
- mPrivOpsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
+ InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
}
/**
@@ -749,7 +793,7 @@
*/
@Deprecated
public void hideStatusIcon(IBinder imeToken) {
- mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
+ InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
}
/** @hide */
@@ -1350,6 +1394,10 @@
mServedView.getWindowToken() == appWindowToken) {
finishInputLocked();
}
+ if (mCurRootView != null &&
+ mCurRootView.getWindowToken() == appWindowToken) {
+ mCurRootView = null;
+ }
}
}
@@ -1790,7 +1838,7 @@
public void setInputMethod(IBinder token, String id) {
if (token == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
mService.setInputMethod(token, id);
@@ -1799,7 +1847,7 @@
}
return;
}
- mPrivOpsRegistry.get(token).setInputMethod(id);
+ InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
}
/**
@@ -1819,7 +1867,7 @@
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
if (token == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
mService.setInputMethodAndSubtype(token, id, subtype);
@@ -1828,7 +1876,7 @@
}
return;
}
- mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
+ InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
}
/**
@@ -1848,7 +1896,7 @@
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
- mPrivOpsRegistry.get(token).hideMySoftInput(flags);
+ InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
}
/**
@@ -1869,7 +1917,7 @@
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
- mPrivOpsRegistry.get(token).showMySoftInput(flags);
+ InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
}
/**
@@ -2229,7 +2277,7 @@
public boolean switchToLastInputMethod(IBinder imeToken) {
if (imeToken == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToPreviousInputMethod(imeToken);
@@ -2237,7 +2285,7 @@
throw e.rethrowFromSystemServer();
}
}
- return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
}
/**
@@ -2257,7 +2305,7 @@
public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
if (imeToken == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
- // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
@@ -2265,7 +2313,8 @@
throw e.rethrowFromSystemServer();
}
}
- return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+ .switchToNextInputMethod(onlyCurrentIme);
}
/**
@@ -2284,7 +2333,8 @@
*/
@Deprecated
public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
- return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+ .shouldOfferSwitchingToNextInputMethod();
}
/**
@@ -2420,34 +2470,4 @@
sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
return sb.toString();
}
-
- /**
- * Called by {@link InputMethodService} so that API calls to deprecated ones defined in this
- * class can be forwarded to {@link InputMethodPrivilegedOperations}.
- *
- * <p>Note: this method does not hold strong references to {@code token} and {@code ops}. The
- * registry entry will be automatically cleared after {@code token} is garbage collected.</p>
- *
- * @param token IME token that is associated with {@code ops}
- * @param ops {@link InputMethodPrivilegedOperations} that is associated with {@code token}
- * @hide
- */
- public void registerInputMethodPrivOps(IBinder token, InputMethodPrivilegedOperations ops) {
- mPrivOpsRegistry.put(token, ops);
- }
-
- /**
- * Called from {@link InputMethodService#onDestroy()} to make sure that deprecated IME APIs
- * defined in this class can no longer access to {@link InputMethodPrivilegedOperations}.
- *
- * <p>Note: Calling this method is optional, but at least gives more explict error message in
- * logcat when IME developers are doing something unsupported (e.g. trying to call IME APIs
- * after {@link InputMethodService#onDestroy()}).</p>
- *
- * @param token IME token to be removed.
- * @hide
- */
- public void unregisterInputMethodPrivOps(IBinder token) {
- mPrivOpsRegistry.remove(token);
- }
}
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index a7d7a8d..14c879e 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -28,7 +28,7 @@
import android.text.TextUtils;
import android.util.Slog;
-import com.android.internal.inputmethod.InputMethodUtils;
+import com.android.internal.inputmethod.SubtypeLocaleUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -384,7 +384,7 @@
if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
mCachedLocaleObj = Locale.forLanguageTag(mSubtypeLanguageTag);
} else {
- mCachedLocaleObj = InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
+ mCachedLocaleObj = SubtypeLocaleUtils.constructLocaleFromString(mSubtypeLocale);
}
return mCachedLocaleObj;
}
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index 026610e..d904d467 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -25,7 +25,7 @@
import android.text.TextUtils;
import android.util.Slog;
-import com.android.internal.inputmethod.InputMethodUtils;
+import com.android.internal.inputmethod.SubtypeLocaleUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -228,7 +228,7 @@
if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
return Locale.forLanguageTag(mSubtypeLanguageTag);
}
- return InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
+ return SubtypeLocaleUtils.constructLocaleFromString(mSubtypeLocale);
}
/**
diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java
index 1d3a617..7839a00 100644
--- a/core/java/android/webkit/SafeBrowsingResponse.java
+++ b/core/java/android/webkit/SafeBrowsingResponse.java
@@ -36,14 +36,14 @@
public abstract void showInterstitial(boolean allowReporting);
/**
- * Act as if the user clicked "visit this unsafe site."
+ * Act as if the user clicked the "visit this unsafe site" button.
*
* @param report {@code true} to enable Safe Browsing reporting.
*/
public abstract void proceed(boolean report);
/**
- * Act as if the user clicked "back to safety."
+ * Act as if the user clicked the "back to safety" button.
*
* @param report {@code true} to enable Safe Browsing reporting.
*/
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index 05c0304..30f465c 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -36,7 +36,7 @@
* <pre class="prettyprint">
* TracingController tracingController = TracingController.getInstance();
* tracingController.start(new TracingConfig.Builder()
- * .addCategories(CATEGORIES_WEB_DEVELOPER).build());
+ * .addCategories(TracingConfig.CATEGORIES_WEB_DEVELOPER).build());
* ...
* tracingController.stop(new FileOutputStream("trace.json"),
* Executors.newSingleThreadExecutor());
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index dd6c923..f3fe16e 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -682,7 +682,7 @@
/**
* Sets the currently selected item. To support accessibility subclasses that
- * override this method must invoke the overriden super method first.
+ * override this method must invoke the overridden super method first.
*
* @param position Index (starting at 0) of the data item to be selected.
*/
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 71d13a9..7d6564f 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -24,6 +24,7 @@
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.text.Editable;
import android.text.Selection;
import android.text.TextUtils;
@@ -533,7 +534,7 @@
*
* @hide Pending API council approval
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void setDropDownAnimationStyle(int animationStyle) {
mPopup.setAnimationStyle(animationStyle);
}
@@ -1157,7 +1158,7 @@
* <p>Closes the drop down if present on screen.</p>
*/
public void dismissDropDown() {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
imm.displayCompletions(this, null);
}
@@ -1246,7 +1247,7 @@
private void buildImeCompletions() {
final ListAdapter adapter = mAdapter;
if (adapter != null) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
final int count = Math.min(adapter.getCount(), 20);
CompletionInfo[] completions = new CompletionInfo[count];
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index cc8b5500..e250f63 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -365,7 +365,7 @@
* closed.
*
* @param newCursor The new cursor to be used.
- * @return Returns the previously set Cursor, or null if there wasa not one.
+ * @return Returns the previously set Cursor, or null if there was not one.
* If the given new Cursor is the same instance is the previously set
* Cursor, null is also returned.
*/
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index f88a4e2..5f15110 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -639,7 +639,7 @@
// changed the value via the IME and there is a next input the IME will
// be shown, otherwise the user chose another means of changing the
// value and having the IME up makes no sense.
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
if (inputMethodManager != null) {
if (inputMethodManager.isActive(mYearSpinnerInput)) {
mYearSpinnerInput.clearFocus();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4428598..8dd30f6 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1539,6 +1539,10 @@
}
}
+ private InputMethodManager getInputMethodManager() {
+ return mTextView.getContext().getSystemService(InputMethodManager.class);
+ }
+
public void beginBatchEdit() {
mInBatchEditControllers = true;
final InputMethodState ims = mInputMethodState;
@@ -1707,7 +1711,7 @@
if (req == null) {
return false;
}
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (imm == null) {
return false;
}
@@ -1742,7 +1746,7 @@
private void sendUpdateSelection() {
if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (null != imm) {
final int selectionStart = mTextView.getSelectionStart();
final int selectionEnd = mTextView.getSelectionEnd();
@@ -1768,7 +1772,7 @@
final InputMethodState ims = mInputMethodState;
if (ims != null && ims.mBatchEditNesting == 0) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) {
if (imm.isActive(mTextView)) {
if (ims.mContentChanged || ims.mSelectionModeChanged) {
@@ -2245,7 +2249,7 @@
&& mTextView.isTextEditable() && !mTextView.isTextSelectable()
&& mShowSoftInputOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text.
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
imm.showSoftInput(mTextView, 0, null);
}
@@ -2255,7 +2259,7 @@
private boolean extractedTextModeWillBeStarted() {
if (!(mTextView.isInExtractedMode())) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
return imm != null && imm.isFullscreenMode();
}
return false;
@@ -4272,7 +4276,7 @@
if (ims == null || ims.mBatchEditNesting > 0) {
return;
}
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
if (null == imm) {
return;
}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 452e903..f2e478d 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -653,7 +653,7 @@
}
/**
- * <p>Returns the view at the specified index. This method can be overriden
+ * <p>Returns the view at the specified index. This method can be overridden
* to take into account virtual children. Refer to
* {@link android.widget.TableLayout} and {@link android.widget.TableRow}
* for an example.</p>
@@ -1527,7 +1527,7 @@
/**
* <p>Measure the child according to the parent's measure specs. This
- * method should be overriden by subclasses to force the sizing of
+ * method should be overridden by subclasses to force the sizing of
* children. This method is called by {@link #measureVertical(int, int)} and
* {@link #measureHorizontal(int, int)}.</p>
*
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b6ed22c..a28cc40 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1293,7 +1293,8 @@
* Shows the soft input for its input text.
*/
private void showSoftInput() {
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager =
+ getContext().getSystemService(InputMethodManager.class);
if (inputMethodManager != null) {
if (mHasSelectorWheel) {
mInputText.setVisibility(View.VISIBLE);
@@ -1307,7 +1308,8 @@
* Hides the soft input if it is active for the input text.
*/
private void hideSoftInput() {
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager =
+ getContext().getSystemService(InputMethodManager.class);
if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 5b5950d..10e1dfb 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -36,6 +36,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1292,7 +1293,7 @@
mSearchSrcTextView.dismissDropDown();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private void onCloseClicked() {
CharSequence text = mSearchSrcTextView.getText();
if (TextUtils.isEmpty(text)) {
@@ -1590,7 +1591,7 @@
/**
* Sets the text in the query box, without updating the suggestions.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private void setQuery(CharSequence query) {
mSearchSrcTextView.setText(query, true);
// Move the cursor to the end
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 9a24061..c78f4ac 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -268,7 +268,7 @@
* Intent shareIntent = new Intent(Intent.ACTION_SEND);
* shareIntent.setType("image/*");
* Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
- * shareIntent.putExtra(Intent.EXTRA_STREAM, uri));</pre>
+ * shareIntent.putExtra(Intent.EXTRA_STREAM, uri);</pre>
*
* @param shareIntent The share intent.
*
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d913273..bbfac44 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2156,7 +2156,7 @@
if (!enabled) {
// Hide the soft input if the currently active TextView is disabled
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null && imm.isActive(this)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@@ -2166,7 +2166,7 @@
if (enabled) {
// Make sure IME is updated with current editor info.
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
}
@@ -2392,7 +2392,7 @@
if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL;
}
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
}
@@ -3609,7 +3609,7 @@
/**
* Read the Text Appearance attributes from a given TypedArray and set its values to the given
* set. If the TypedArray contains a value that was already set in the given attributes, that
- * will be overriden.
+ * will be overridden.
*
* @param context The Context to be used
* @param appearance The TypedArray to read properties from
@@ -5769,7 +5769,7 @@
Editable t = mEditableFactory.newEditable(text);
text = t;
setFilters(t, mFilters);
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
} else if (precomputed != null) {
if (mTextDir == null) {
@@ -6148,7 +6148,7 @@
setTextInternal(removeSuggestionSpans(mText));
}
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null) imm.restartInput(this);
}
@@ -6436,7 +6436,7 @@
return;
} else if (actionCode == EditorInfo.IME_ACTION_DONE) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null && imm.isActive(this)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@@ -7902,7 +7902,7 @@
if (!hasOnClickListeners()) {
if (mMovement != null && mText instanceof Editable
&& mLayout != null && onCheckIsTextEditor()) {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
viewClicked(imm);
if (imm != null && getShowSoftInputOnFocus()) {
imm.showSoftInput(this, 0);
@@ -7956,7 +7956,7 @@
& KeyEvent.FLAG_EDITOR_ACTION) != 0) {
// No target for next focus, but make sure the IME
// if this came from it.
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
if (imm != null && imm.isActive(this)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@@ -10260,7 +10260,7 @@
if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
// Show the IME, except when selecting in read-only text.
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
viewClicked(imm);
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
imm.showSoftInput(this, 0);
@@ -11299,7 +11299,7 @@
// Show the IME, except when selecting in read-only text.
if ((mMovement != null || onCheckIsTextEditor()) && hasSpannableText() && mLayout != null
&& (isTextEditable() || isTextSelectable()) && isFocused()) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
+ final InputMethodManager imm = getInputMethodManager();
viewClicked(imm);
if (!isTextSelectable() && mEditor.mShowSoftInputOnFocus && imm != null) {
handled |= imm.showSoftInput(this, 0);
@@ -11367,13 +11367,17 @@
sendAccessibilityEventUnchecked(event);
}
+ private InputMethodManager getInputMethodManager() {
+ return getContext().getSystemService(InputMethodManager.class);
+ }
+
/**
* Returns whether this text view is a current input method target. The
* default implementation just checks with {@link InputMethodManager}.
* @return True if the TextView is a current input method target; false otherwise.
*/
public boolean isInputMethodTarget() {
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = getInputMethodManager();
return imm != null && imm.isActive(this);
}
@@ -12491,11 +12495,11 @@
advancesIndex);
}
- public int getTextRunCursor(int contextStart, int contextEnd, int dir,
+ public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl,
int offset, int cursorOpt, Paint p) {
int contextCount = contextEnd - contextStart;
return p.getTextRunCursor(mChars, contextStart + mStart,
- contextCount, dir, offset + mStart, cursorOpt);
+ contextCount, isRtl, offset + mStart, cursorOpt);
}
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 77670b3..6a3a8f0 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -269,7 +269,7 @@
mRadialTimePickerModeButton.setContentDescription(
mTextInputPickerModeEnabledDescription);
updateTextInputPicker();
- InputMethodManager imm = InputMethodManager.peekInstance();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
}
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index cc79b9c..83c86d5 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -471,7 +471,7 @@
// changed the value via the IME and there is a next input the IME will
// be shown, otherwise the user chose another means of changing the
// value and having the IME up makes no sense.
- InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+ InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
if (inputMethodManager != null) {
if (inputMethodManager.isActive(mHourSpinnerInput)) {
mHourSpinnerInput.clearFocus();
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 3fbc819..10cf702 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -27,6 +27,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -443,7 +444,7 @@
* schedule handleShow into the right thread
*/
@Override
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void show(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 1bbd7e8..adb7f2f 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Layout;
@@ -137,7 +138,7 @@
@UnsupportedAppUsage
private TextView mTitleTextView;
private TextView mSubtitleTextView;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private ImageButton mNavButtonView;
private ImageView mLogoView;
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 14881eb..8b45d99 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -38,6 +38,7 @@
import android.media.TtmlRenderer;
import android.media.WebVttRenderer;
import android.net.Uri;
+import android.os.Build;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
@@ -85,7 +86,7 @@
// all possible internal states
private static final int STATE_ERROR = -1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static final int STATE_IDLE = 0;
private static final int STATE_PREPARING = 1;
private static final int STATE_PREPARED = 2;
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 90712fd..e83e79b 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -16,6 +16,10 @@
package com.android.internal.accessibility;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+
+import static com.android.internal.util.ArrayUtils.convertToLongArray;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -34,23 +38,23 @@
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.Voice;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-
import android.widget.Toast;
+
import com.android.internal.R;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.util.Collections;
+import java.util.Locale;
import java.util.Map;
-import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-
-import static com.android.internal.util.ArrayUtils.convertToLongArray;
-
/**
* Class to help manage the accessibility shortcut
*/
@@ -70,6 +74,7 @@
private static Map<ComponentName, ToggleableFrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
private final Context mContext;
+ private final Handler mHandler;
private AlertDialog mAlertDialog;
private boolean mIsShortcutEnabled;
private boolean mEnabledOnLockScreen;
@@ -123,6 +128,7 @@
public AccessibilityShortcutController(Context context, Handler handler, int initialUserId) {
mContext = context;
+ mHandler = handler;
mUserId = initialUserId;
// Keep track of state of shortcut settings
@@ -189,22 +195,6 @@
final int userId = ActivityManager.getCurrentUser();
final int dialogAlreadyShown = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
- // Use USAGE_ASSISTANCE_ACCESSIBILITY for TVs to ensure that TVs play the ringtone as they
- // have less ways of providing feedback like vibration.
- final int audioAttributesUsage = hasFeatureLeanback()
- ? AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
- : AudioAttributes.USAGE_NOTIFICATION_EVENT;
-
- // Play a notification tone
- final Ringtone tone =
- RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_NOTIFICATION_URI);
- if (tone != null) {
- tone.setAudioAttributes(new AudioAttributes.Builder()
- .setUsage(audioAttributesUsage)
- .build());
- tone.play();
- }
-
// Play a notification vibration
Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
if ((vibrator != null) && vibrator.hasVibrator()) {
@@ -223,6 +213,9 @@
if (mAlertDialog == null) {
return;
}
+ if (!performTtsPrompt(mAlertDialog)) {
+ playNotificationTone();
+ }
Window w = mAlertDialog.getWindow();
WindowManager.LayoutParams attr = w.getAttributes();
attr.type = TYPE_KEYGUARD_DIALOG;
@@ -231,6 +224,7 @@
Settings.Secure.putIntForUser(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1, userId);
} else {
+ playNotificationTone();
if (mAlertDialog != null) {
mAlertDialog.dismiss();
mAlertDialog = null;
@@ -344,6 +338,102 @@
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
+ private void playNotificationTone() {
+ // Use USAGE_ASSISTANCE_ACCESSIBILITY for TVs to ensure that TVs play the ringtone as they
+ // have less ways of providing feedback like vibration.
+ final int audioAttributesUsage = hasFeatureLeanback()
+ ? AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
+ : AudioAttributes.USAGE_NOTIFICATION_EVENT;
+
+ // Play a notification tone
+ final Ringtone tone = mFrameworkObjectProvider.getRingtone(mContext,
+ Settings.System.DEFAULT_NOTIFICATION_URI);
+ if (tone != null) {
+ tone.setAudioAttributes(new AudioAttributes.Builder()
+ .setUsage(audioAttributesUsage)
+ .build());
+ tone.play();
+ }
+ }
+
+ private boolean performTtsPrompt(AlertDialog alertDialog) {
+ final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+ if (serviceInfo == null) {
+ return false;
+ }
+ if ((serviceInfo.flags & AccessibilityServiceInfo
+ .FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK) == 0) {
+ return false;
+ }
+ final TtsPrompt tts = new TtsPrompt();
+ alertDialog.setOnDismissListener(dialog -> tts.dismiss());
+ return true;
+ }
+
+ /**
+ * Class to wrap TextToSpeech for shortcut dialog spoken feedback.
+ */
+ private class TtsPrompt implements TextToSpeech.OnInitListener {
+ private final CharSequence mText;
+ private boolean mDismiss;
+ private TextToSpeech mTts;
+
+ TtsPrompt() {
+ mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback);
+ mTts = mFrameworkObjectProvider.getTextToSpeech(mContext, this);
+ }
+
+ /**
+ * Releases the resources used by the TextToSpeech, when dialog dismiss.
+ */
+ public void dismiss() {
+ mDismiss = true;
+ mHandler.sendMessage(PooledLambda.obtainMessage(TextToSpeech::shutdown, mTts));
+ }
+
+ @Override
+ public void onInit(int status) {
+ if (status != TextToSpeech.SUCCESS) {
+ Slog.d(TAG, "Tts init fail, status=" + Integer.toString(status));
+ playNotificationTone();
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(TtsPrompt::play, this));
+ }
+
+ private void play() {
+ if (mDismiss) {
+ return;
+ }
+ int status = TextToSpeech.ERROR;
+ if (setLanguage(Locale.getDefault())) {
+ status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
+ }
+ if (status != TextToSpeech.SUCCESS) {
+ Slog.d(TAG, "Tts play fail");
+ playNotificationTone();
+ }
+ }
+
+ /**
+ * @return false if tts language is not available
+ */
+ private boolean setLanguage(final Locale locale) {
+ int status = mTts.isLanguageAvailable(locale);
+ if (status == TextToSpeech.LANG_MISSING_DATA
+ || status == TextToSpeech.LANG_NOT_SUPPORTED) {
+ return false;
+ }
+ mTts.setLanguage(locale);
+ Voice voice = mTts.getVoice();
+ if (voice == null || (voice.getFeatures() != null && voice.getFeatures()
+ .contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED))) {
+ return false;
+ }
+ return true;
+ }
+ }
+
/**
* Immutable class to hold info about framework features that can be controlled by shortcut
*/
@@ -406,5 +496,23 @@
public Context getSystemUiContext() {
return ActivityThread.currentActivityThread().getSystemUiContext();
}
+
+ /**
+ * @param ctx A context for TextToSpeech
+ * @param listener TextToSpeech initialization callback
+ * @return TextToSpeech instance
+ */
+ public TextToSpeech getTextToSpeech(Context ctx, TextToSpeech.OnInitListener listener) {
+ return new TextToSpeech(ctx, listener);
+ }
+
+ /**
+ * @param ctx context for ringtone
+ * @param uri ringtone uri
+ * @return Ringtone instance
+ */
+ public Ringtone getRingtone(Context ctx, Uri uri) {
+ return RingtoneManager.getRingtone(ctx, uri);
+ }
}
}
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 9171959..0f8295a 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -16,8 +16,7 @@
package com.android.internal.app;
-import com.android.internal.R;
-
+import android.annotation.NonNull;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
@@ -32,6 +31,9 @@
import android.provider.Settings;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Set;
+
/**
* Utility method for dealing with the assistant aspects of
* {@link com.android.internal.app.IVoiceInteractionManagerService IVoiceInteractionManagerService}.
@@ -62,6 +64,30 @@
return false;
}
+ /**
+ * Checks the availability of a set of voice actions for the current active voice service.
+ *
+ * @param voiceActions A set of supported voice actions to be checked.
+ * @param callback The callback which will deliver a set of supported voice actions. If
+ * no voice actions are supported for the given voice action set, then null
+ * or empty set is provided.
+ */
+ public void getActiveServiceSupportedActions(@NonNull Set<String> voiceActions,
+ @NonNull IVoiceActionCheckCallback callback) {
+ try {
+ if (mVoiceInteractionManagerService != null) {
+ mVoiceInteractionManagerService
+ .getActiveServiceSupportedActions(new ArrayList<>(voiceActions), callback);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call activeServiceSupportedActions", e);
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException re) {
+ }
+ }
+ }
+
public void launchVoiceAssistFromKeyguard() {
try {
if (mVoiceInteractionManagerService != null) {
@@ -157,7 +183,7 @@
return getActiveServiceComponentName();
}
final SearchManager searchManager =
- (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+ (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
if (searchManager == null) {
return null;
}
diff --git a/packages/SystemUI/res/values-h320dp/config.xml b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
similarity index 63%
rename from packages/SystemUI/res/values-h320dp/config.xml
rename to core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
index a9c19db..66ba93d 100644
--- a/packages/SystemUI/res/values-h320dp/config.xml
+++ b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
@@ -1,22 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2017, The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-*/
--->
+ */
-<resources>
- <!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">2</integer>
-</resources>
+package com.android.internal.app;
+
+oneway interface IVoiceActionCheckCallback {
+ void onComplete(in List<String> voiceActions);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ff75a8b..5088cca 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.os.Bundle;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -143,4 +144,11 @@
* Register a voice interaction listener.
*/
void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
+
+ /**
+ * Checks the availability of a set of voice actions for the current active voice service.
+ * Returns all supported voice actions.
+ */
+ void getActiveServiceSupportedActions(in List<String> voiceActions,
+ in IVoiceActionCheckCallback callback);
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b33a5c4..81dab2f 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -232,6 +232,7 @@
displayName = FileUtils.buildValidFatFilename(displayName);
final File before = getFileForDocId(docId);
+ final File beforeVisibleFile = getFileForDocId(docId, true);
final File after = FileUtils.buildUniqueFile(before.getParentFile(), displayName);
if (!before.renameTo(after)) {
throw new IllegalStateException("Failed to rename to " + after);
@@ -241,7 +242,6 @@
onDocIdChanged(docId);
onDocIdChanged(afterDocId);
- final File beforeVisibleFile = getFileForDocId(docId, true);
final File afterVisibleFile = getFileForDocId(afterDocId, true);
moveInMediaStore(beforeVisibleFile, afterVisibleFile);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
index 3255153..1436aed 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
@@ -29,12 +29,19 @@
/**
* A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
* used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
+ *
+ * <p>This class is designed to be used as a per-process global registry.</p>
*/
public final class InputMethodPrivilegedOperationsRegistry {
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>>
- mRegistry = new WeakHashMap<>();
+ private InputMethodPrivilegedOperationsRegistry() {
+ // Not intended to be instantiated.
+ }
+
+ private static final Object sLock = new Object();
+
+ @Nullable
+ @GuardedBy("sLock")
+ private static WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>> sRegistry;
@Nullable
private static InputMethodPrivilegedOperations sNop;
@@ -62,10 +69,13 @@
* @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
*/
@AnyThread
- public void put(IBinder token, InputMethodPrivilegedOperations ops) {
- synchronized (mLock) {
+ public static void put(IBinder token, InputMethodPrivilegedOperations ops) {
+ synchronized (sLock) {
+ if (sRegistry == null) {
+ sRegistry = new WeakHashMap<>();
+ }
final WeakReference<InputMethodPrivilegedOperations> previousOps =
- mRegistry.put(token, new WeakReference<>(ops));
+ sRegistry.put(token, new WeakReference<>(ops));
if (previousOps != null) {
throw new IllegalStateException(previousOps.get() + " is already registered for "
+ " this token=" + token + " newOps=" + ops);
@@ -84,9 +94,12 @@
*/
@NonNull
@AnyThread
- public InputMethodPrivilegedOperations get(IBinder token) {
- synchronized (mLock) {
- final WeakReference<InputMethodPrivilegedOperations> wrapperRef = mRegistry.get(token);
+ public static InputMethodPrivilegedOperations get(IBinder token) {
+ synchronized (sLock) {
+ if (sRegistry == null) {
+ return getNopOps();
+ }
+ final WeakReference<InputMethodPrivilegedOperations> wrapperRef = sRegistry.get(token);
if (wrapperRef == null) {
return getNopOps();
}
@@ -108,9 +121,15 @@
* @param token IME token to be removed.
*/
@AnyThread
- public void remove(IBinder token) {
- synchronized (mLock) {
- mRegistry.remove(token);
+ public static void remove(IBinder token) {
+ synchronized (sLock) {
+ if (sRegistry == null) {
+ return;
+ }
+ sRegistry.remove(token);
+ if (sRegistry.isEmpty()) {
+ sRegistry = null;
+ }
}
}
}
diff --git a/core/java/com/android/internal/inputmethod/SubtypeLocaleUtils.java b/core/java/com/android/internal/inputmethod/SubtypeLocaleUtils.java
new file mode 100644
index 0000000..9ea4fa2
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/SubtypeLocaleUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.util.Locale;
+
+/**
+ * A utility class to handle {@link Locale} related logic for
+ * {@link android.view.inputmethod.InputMethodSubtype} and
+ * {@link android.view.textservice.SpellCheckerSubtype}.
+ */
+public class SubtypeLocaleUtils {
+ /**
+ * Maintains deprecated logic about how subtype locales specified in XML resources have been
+ * parsed.
+ *
+ * <p>This logic is kept basically for compatibility purpose. Consider relying on languageTag
+ * attribute instead.</p>
+ *
+ * @param localeStr string representation that is specified in the locale attribute
+ * @return {@link Locale} object parsed from {@code localeStr}. {@code null} for unexpected
+ * format
+ *
+ * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale
+ * @attr ref android.R.styleable#InputMethod_Subtype_languageTag
+ * @attr ref android.R.styleable#SpellChecker_Subtype_languageTag
+ * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeLocale
+ */
+ @Nullable
+ public static Locale constructLocaleFromString(String localeStr) {
+ if (TextUtils.isEmpty(localeStr)) {
+ return null;
+ }
+ // TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}.
+ String[] localeParams = localeStr.split("_", 3);
+ if (localeParams.length >= 1 && "tl".equals(localeParams[0])) {
+ // Convert a locale whose language is "tl" to one whose language is "fil".
+ // For example, "tl_PH" will get converted to "fil_PH".
+ // Versions of Android earlier than Lollipop did not support three letter language
+ // codes, and used "tl" (Tagalog) as the language string for "fil" (Filipino).
+ // On Lollipop and above, the current three letter version must be used.
+ localeParams[0] = "fil";
+ }
+ // The length of localeStr is guaranteed to always return a 1 <= value <= 3
+ // because localeStr is not empty.
+ if (localeParams.length == 1) {
+ return new Locale(localeParams[0]);
+ } else if (localeParams.length == 2) {
+ return new Locale(localeParams[0], localeParams[1]);
+ } else if (localeParams.length == 3) {
+ return new Locale(localeParams[0], localeParams[1], localeParams[2]);
+ }
+ return null;
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d0795c9..31f13c3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5758,8 +5758,6 @@
for (int i = 0; i < N; i++) {
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i),
- numNewResults);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5768,8 +5766,6 @@
final WorkChain wc = workChains.get(i);
int uid = mapUid(wc.getAttributionUid());
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED,
- wc.getUids(), wc.getTags(), numNewResults);
}
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java
index 396deb4..c233ea8 100644
--- a/core/java/com/android/internal/os/KernelCpuProcReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcReader.java
@@ -24,13 +24,14 @@
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
+import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
/**
* Reads cpu time proc files with throttling (adjustable interval).
@@ -55,7 +56,6 @@
private static final int ERROR_THRESHOLD = 5;
// Throttle interval in milliseconds
private static final long DEFAULT_THROTTLE_INTERVAL = 3000L;
- private static final int INITIAL_BUFFER_SIZE = 8 * 1024;
private static final int MAX_BUFFER_SIZE = 1024 * 1024;
private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state";
private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time";
@@ -84,13 +84,12 @@
private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
private long mLastReadTime = Long.MIN_VALUE;
private final Path mProc;
- private ByteBuffer mBuffer;
+ private byte[] mBuffer = new byte[8 * 1024];
+ private int mContentSize;
@VisibleForTesting
public KernelCpuProcReader(String procFile) {
mProc = Paths.get(procFile);
- mBuffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE);
- mBuffer.clear();
}
/**
@@ -108,38 +107,45 @@
return null;
}
if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) {
- if (mBuffer.limit() > 0 && mBuffer.limit() < mBuffer.capacity()) {
- // mBuffer has data.
- return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+ if (mContentSize > 0) {
+ return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
+ .order(ByteOrder.nativeOrder());
}
return null;
}
mLastReadTime = SystemClock.elapsedRealtime();
- mBuffer.clear();
+ mContentSize = 0;
final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (FileChannel fc = FileChannel.open(mProc, StandardOpenOption.READ)) {
- while (fc.read(mBuffer) == mBuffer.capacity()) {
- if (!resize()) {
- mErrors++;
- Slog.e(TAG, "Proc file is too large: " + mProc);
- return null;
+ try (InputStream in = Files.newInputStream(mProc)) {
+ int numBytes = 0;
+ int curr;
+ while ((curr = in.read(mBuffer, numBytes, mBuffer.length - numBytes)) >= 0) {
+ numBytes += curr;
+ if (numBytes == mBuffer.length) {
+ // Hit the limit. Resize mBuffer.
+ if (mBuffer.length == MAX_BUFFER_SIZE) {
+ mErrors++;
+ Slog.e(TAG, "Proc file is too large: " + mProc);
+ return null;
+ }
+ mBuffer = Arrays.copyOf(mBuffer,
+ Math.min(mBuffer.length << 1, MAX_BUFFER_SIZE));
}
- fc.position(0);
}
+ mContentSize = numBytes;
+ return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
+ .order(ByteOrder.nativeOrder());
} catch (NoSuchFileException | FileNotFoundException e) {
// Happens when the kernel does not provide this file. Not a big issue. Just log it.
mErrors++;
Slog.w(TAG, "File not exist: " + mProc);
- return null;
} catch (IOException e) {
mErrors++;
Slog.e(TAG, "Error reading: " + mProc, e);
- return null;
} finally {
StrictMode.setThreadPolicyMask(oldMask);
}
- mBuffer.flip();
- return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+ return null;
}
/**
@@ -153,14 +159,4 @@
mThrottleInterval = throttleInterval;
}
}
-
- private boolean resize() {
- if (mBuffer.capacity() >= MAX_BUFFER_SIZE) {
- return false;
- }
- int newSize = Math.min(mBuffer.capacity() << 1, MAX_BUFFER_SIZE);
- // Slog.i(TAG, "Resize buffer " + mBuffer.capacity() + " => " + newSize);
- mBuffer = ByteBuffer.allocateDirect(newSize);
- return true;
- }
}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 02a8b22..e4724ff 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -17,6 +17,7 @@
package com.android.internal.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -36,7 +37,7 @@
* @hide Only for use within the system server.
*/
public class LooperStats implements Looper.Observer {
- private static final int TOKEN_POOL_SIZE = 50;
+ private static final int SESSION_POOL_SIZE = 50;
@GuardedBy("mLock")
private final SparseArray<Entry> mEntries = new SparseArray<>(512);
@@ -78,17 +79,19 @@
}
DispatchSession session = (DispatchSession) token;
- Entry entry = getOrCreateEntry(msg);
- synchronized (entry) {
- entry.messageCount++;
- if (session != DispatchSession.NOT_SAMPLED) {
- entry.recordedMessageCount++;
- long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
- long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
- entry.totalLatencyMicro += latency;
- entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
- entry.cpuUsageMicro += cpuUsage;
- entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+ Entry entry = findEntry(msg, /* allowCreateNew= */session != DispatchSession.NOT_SAMPLED);
+ if (entry != null) {
+ synchronized (entry) {
+ entry.messageCount++;
+ if (session != DispatchSession.NOT_SAMPLED) {
+ entry.recordedMessageCount++;
+ long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
+ long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
+ entry.totalLatencyMicro += latency;
+ entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
+ entry.cpuUsageMicro += cpuUsage;
+ entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+ }
}
}
@@ -102,10 +105,11 @@
}
DispatchSession session = (DispatchSession) token;
- Entry entry = getOrCreateEntry(msg);
+ Entry entry = findEntry(msg, /* allowCreateNew= */true);
synchronized (entry) {
entry.exceptionCount++;
}
+
recycleSession(session);
}
@@ -116,29 +120,29 @@
/** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */
public List<ExportedEntry> getEntries() {
- final ArrayList<ExportedEntry> entries;
+ final ArrayList<ExportedEntry> exportedEntries;
synchronized (mLock) {
final int size = mEntries.size();
- entries = new ArrayList<>(size);
+ exportedEntries = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Entry entry = mEntries.valueAt(i);
synchronized (entry) {
- entries.add(new ExportedEntry(entry));
+ exportedEntries.add(new ExportedEntry(entry));
}
}
}
// Add the overflow and collision entries only if they have any data.
- if (mOverflowEntry.messageCount > 0 || mOverflowEntry.exceptionCount > 0) {
- synchronized (mOverflowEntry) {
- entries.add(new ExportedEntry(mOverflowEntry));
+ maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
+ maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
+ return exportedEntries;
+ }
+
+ private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) {
+ synchronized (specialEntry) {
+ if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) {
+ exportedEntries.add(new ExportedEntry(specialEntry));
}
}
- if (mHashCollisionEntry.messageCount > 0 || mHashCollisionEntry.exceptionCount > 0) {
- synchronized (mHashCollisionEntry) {
- entries.add(new ExportedEntry(mHashCollisionEntry));
- }
- }
- return entries;
}
/** Removes all collected data. */
@@ -158,24 +162,28 @@
mSamplingInterval = samplingInterval;
}
- @NonNull
- private Entry getOrCreateEntry(Message msg) {
+ @Nullable
+ private Entry findEntry(Message msg, boolean allowCreateNew) {
final boolean isInteractive = mDeviceState.isScreenInteractive();
final int id = Entry.idFor(msg, isInteractive);
Entry entry;
synchronized (mLock) {
entry = mEntries.get(id);
if (entry == null) {
- if (mEntries.size() >= mEntriesSizeCap) {
- // If over the size cap, track totals under a single entry.
+ if (!allowCreateNew) {
+ return null;
+ } else if (mEntries.size() >= mEntriesSizeCap) {
+ // If over the size cap track totals under OVERFLOW entry.
return mOverflowEntry;
+ } else {
+ entry = new Entry(msg, isInteractive);
+ mEntries.put(id, entry);
}
- entry = new Entry(msg, isInteractive);
- mEntries.put(id, entry);
}
}
- if (entry.handler.getClass() != msg.getTarget().getClass()
+ if (entry.workSourceUid != msg.workSourceUid
+ || entry.handler.getClass() != msg.getTarget().getClass()
|| entry.handler.getLooper().getThread() != msg.getTarget().getLooper().getThread()
|| entry.isInteractive != isInteractive) {
// If a hash collision happened, track totals under a single entry.
@@ -185,7 +193,7 @@
}
private void recycleSession(DispatchSession session) {
- if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) {
+ if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < SESSION_POOL_SIZE) {
mSessionPool.add(session);
}
}
@@ -209,6 +217,7 @@
}
private static class Entry {
+ public final int workSourceUid;
public final Handler handler;
public final String messageName;
public final boolean isInteractive;
@@ -221,12 +230,14 @@
public long maxCpuUsageMicro;
Entry(Message msg, boolean isInteractive) {
+ this.workSourceUid = msg.workSourceUid;
this.handler = msg.getTarget();
this.messageName = handler.getMessageName(msg);
this.isInteractive = isInteractive;
}
Entry(String specialEntryName) {
+ this.workSourceUid = Message.UID_NONE;
this.messageName = specialEntryName;
this.handler = null;
this.isInteractive = false;
@@ -244,6 +255,7 @@
static int idFor(Message msg, boolean isInteractive) {
int result = 7;
+ result = 31 * result + msg.workSourceUid;
result = 31 * result + msg.getTarget().getLooper().getThread().hashCode();
result = 31 * result + msg.getTarget().getClass().hashCode();
result = 31 * result + (isInteractive ? 1231 : 1237);
@@ -257,6 +269,7 @@
/** Aggregated data of Looper message dispatching in the in the current process. */
public static class ExportedEntry {
+ public final int workSourceUid;
public final String handlerClassName;
public final String threadName;
public final String messageName;
@@ -270,6 +283,7 @@
public final long maxCpuUsageMicros;
ExportedEntry(Entry entry) {
+ this.workSourceUid = entry.workSourceUid;
if (entry.handler != null) {
this.handlerClassName = entry.handler.getClass().getName();
this.threadName = entry.handler.getLooper().getThread().getName();
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c4214cf..9b8f120 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -141,7 +141,8 @@
void showShutdownUi(boolean isReboot, String reason);
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type);
+ void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index e48e733..90f2002 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -91,7 +91,8 @@
void showPinningEscapeToast();
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type);
+ void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 6f9c87a..ececba1 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -68,7 +69,7 @@
@GuardedBy("mLock")
@Nullable
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private InputConnection mInputConnection;
private Looper mMainLooper;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 543f4a5..5f1243f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -31,6 +31,8 @@
* applications.
*/
interface IInputMethodManager {
+ void addClient(in IInputMethodClient client, in IInputContext inputContext);
+
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList();
List<InputMethodInfo> getVrInputMethodList();
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 7548c22..101fd41 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -139,8 +139,7 @@
* The client should try to restart input when its {@link android.view.Window} is focused
* again.</p>
*
- * @see com.android.server.wm.WindowManagerInternal#inputMethodClientHasFocus(
- * IInputMethodClient)
+ * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int)
*/
int ERROR_NOT_IME_TARGET_WINDOW = 11;
/**
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 0b37d57..4773e16 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -840,7 +840,11 @@
scale *= 10;
}
value = (float) (Math.rint(value * scale) / scale);
-
+
+ // Corner case: (int)-0.1 will become zero, so the negative sign gets lost
+ if ((int) value == 0 && value < 0) {
+ append("-");
+ }
append((int) value);
if (precision != 0) {
diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
index 65be161..b2e8b5e 100644
--- a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
+++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
@@ -37,6 +37,7 @@
import org.apache.http.params.HttpParams;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
@@ -181,7 +182,7 @@
private final javax.net.ssl.SSLSocketFactory socketfactory;
@UnsupportedAppUsage
private final HostNameResolver nameResolver;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
public SSLSocketFactory(
@@ -251,7 +252,7 @@
* This constructor is used exclusively to instantiate the factory for
* {@link #getSocketFactory getSocketFactory}.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private SSLSocketFactory() {
super();
this.sslcontext = null;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index bb8ee14..762b430 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -230,7 +230,6 @@
static_libs: [
"libgif",
"libseccomp_policy",
- "libselinux",
"libgrallocusage",
"libscrypt_static",
],
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 0a25271..0c9a85a 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -15,7 +15,6 @@
#include "SkPM4f.h"
#include "SkPM4fPriv.h"
#include "GraphicsJNI.h"
-#include "SkDither.h"
#include "SkUnPreMultiply.h"
#include "SkStream.h"
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 743b9f6..b70177f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -157,23 +157,17 @@
return Rect(left, top, right, bottom);
}
-static jobject nativeScreenshotToBuffer(JNIEnv* env, jclass clazz,
+static jobject nativeScreenshot(JNIEnv* env, jclass clazz,
jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
- jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform,
- int rotation) {
+ bool useIdentityTransform, int rotation) {
sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
if (displayToken == NULL) {
return NULL;
}
Rect sourceCrop = rectFromObj(env, sourceCropObj);
- if (allLayers) {
- minLayer = INT32_MIN;
- maxLayer = INT32_MAX;
- }
sp<GraphicBuffer> buffer;
- status_t res = ScreenshotClient::capture(displayToken,
- sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform,
- rotation, &buffer);
+ status_t res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
+ useIdentityTransform, rotation, &buffer);
if (res != NO_ERROR) {
return NULL;
}
@@ -187,100 +181,6 @@
(jlong)buffer.get());
}
-static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
- jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
- jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform,
- int rotation) {
- sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
- if (displayToken == NULL) {
- return NULL;
- }
-
- Rect sourceCrop = rectFromObj(env, sourceCropObj);
-
- std::unique_ptr<ScreenshotClient> screenshot(new ScreenshotClient());
- status_t res;
- if (allLayers) {
- minLayer = INT32_MIN;
- maxLayer = INT32_MAX;
- }
-
- sp<GraphicBuffer> buffer;
- res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
- minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer);
- if (res != NO_ERROR) {
- return NULL;
- }
-
- SkColorType colorType;
- SkAlphaType alphaType;
-
- PixelFormat format = buffer->getPixelFormat();
- switch (format) {
- case PIXEL_FORMAT_RGBX_8888: {
- colorType = kRGBA_8888_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
- }
- case PIXEL_FORMAT_RGBA_8888: {
- colorType = kRGBA_8888_SkColorType;
- alphaType = kPremul_SkAlphaType;
- break;
- }
- case PIXEL_FORMAT_RGBA_FP16: {
- colorType = kRGBA_F16_SkColorType;
- alphaType = kPremul_SkAlphaType;
- break;
- }
- case PIXEL_FORMAT_RGB_565: {
- colorType = kRGB_565_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
- }
- default: {
- return NULL;
- }
- }
-
- SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
- colorType, alphaType,
- SkColorSpace::MakeSRGB());
-
- auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info));
- return bitmap::createBitmap(env, bitmap.release(),
- android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
-}
-
-static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
- jobject surfaceObj, jobject sourceCropObj, jint width, jint height,
- jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
- sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
- if (displayToken == NULL) {
- return;
- }
-
- sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
- if (consumer == NULL) {
- return;
- }
-
- Rect sourceCrop;
- if (sourceCropObj != NULL) {
- sourceCrop = rectFromObj(env, sourceCropObj);
- }
-
- if (allLayers) {
- minLayer = INT32_MIN;
- maxLayer = INT32_MAX;
- }
-
- sp<GraphicBuffer> buffer;
- ScreenshotClient::capture(displayToken, sourceCrop, width, height, minLayer, maxLayer,
- useIdentityTransform, 0, &buffer);
-
- Surface::attachAndQueueBuffer(consumer.get(), buffer);
-}
-
static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
jobject sourceCropObj, jfloat frameScale) {
@@ -919,10 +819,6 @@
(void*)nativeDestroy },
{"nativeDisconnect", "(J)V",
(void*)nativeDisconnect },
- {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/Bitmap;",
- (void*)nativeScreenshotBitmap },
- {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V",
- (void*)nativeScreenshot },
{"nativeCreateTransaction", "()J",
(void*)nativeCreateTransaction },
{"nativeApplyTransaction", "(JZ)V",
@@ -1013,9 +909,8 @@
(void*)nativeDestroyInTransaction },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
(void*)nativeGetHandle },
- {"nativeScreenshotToBuffer",
- "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;",
- (void*)nativeScreenshotToBuffer },
+ {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
+ (void*)nativeScreenshot },
{"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
(void*)nativeCaptureLayers },
};
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 1257336..d33ea0c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -157,6 +157,7 @@
optional int32 rotation = 11;
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
optional DisplayFramesProto display_frames = 13;
+ optional int32 surface_size = 14;
}
/* represents DisplayFrames */
@@ -211,6 +212,8 @@
optional .android.graphics.RectProto bounds = 5;
optional .android.graphics.RectProto temp_inset_bounds = 6;
optional bool defer_removal = 7;
+ optional int32 surface_width = 8;
+ optional int32 surface_height = 9;
}
/* represents AppWindowToken */
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index ccbd027..c08d7ca 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -200,6 +200,10 @@
optional string condition_id = 8;
optional ConditionProto condition = 9;
optional android.content.ComponentNameProto component = 10;
+ optional ZenPolicyProto zenPolicy = 11;
+
+ // Indicates whether this ZenRule has been modified after its initial creation
+ optional bool modified = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
// A dump from com.android.server.notification.ZenModeHelper.
@@ -212,3 +216,47 @@
repeated android.content.ComponentNameProto suppressors = 4;
optional android.app.PolicyProto policy = 5;
}
+
+// An android.service.notification.ZenPolicy object
+message ZenPolicyProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ enum State {
+ STATE_UNSET = 0;
+ STATE_ALLOW = 1;
+ STATE_DISALLOW = 2;
+ }
+
+ // Notifications and sounds allowed/disallowed when DND is active
+ optional State reminders = 1;
+ optional State events = 2;
+ optional State messages = 3;
+ optional State calls = 4;
+ optional State repeat_callers = 5;
+ optional State alarms = 6;
+ optional State media = 7;
+ optional State system = 8;
+
+ // Visual effects allowed/disallowed for intercepted notifications when DND is active
+ optional State full_screen_intent = 9;
+ optional State lights = 10;
+ optional State peek = 11;
+ optional State status_bar = 12;
+ optional State badge= 13;
+ optional State ambient = 14;
+ optional State notification_list = 15;
+
+ enum Sender {
+ SENDER_UNSET = 0;
+ // Any sender is prioritized.
+ SENDER_ANY = 1;
+ // Saved contacts are prioritized.
+ SENDER_CONTACTS = 2;
+ // Only starred contacts are prioritized.
+ SENDER_STARRED = 3;
+ // No calls/messages are prioritized.
+ SENDER_NONE = 4;
+ }
+ optional Sender priority_calls = 16;
+ optional Sender priority_messages = 17;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bb53638..5a1f2e8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3056,6 +3056,18 @@
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an app to reset the device password.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.RESET_PASSWORD"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an app to lock the device.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.LOCK_DEVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows low-level access to setting the orientation (actually
rotation) of the screen.
<p>Not for use by third-party applications.
@@ -3758,6 +3770,10 @@
<permission android:name="android.permission.USE_BIOMETRIC_INTERNAL"
android:protectionLevel="signature" />
+ <!-- Allows the system to control the BiometricDialog (SystemUI). Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG"
+ android:protectionLevel="signature" />
+
<!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FACE_LOCKOUT"
android:protectionLevel="signature" />
@@ -4114,7 +4130,7 @@
<!-- Allows an application to directly open the "Open by default" page inside a package's
Details screen.
@hide <p>Not for use by third-party applications. -->
- <permission android:name="android.permission.OPEN_APPLICATION_DETAILS_OPEN_BY_DEFAULT_PAGE"
+ <permission android:name="android.permission.OPEN_APP_OPEN_BY_DEFAULT_SETTINGS"
android:protectionLevel="signature" />
<!-- Allows hidden API checks to be disabled when starting a process.
diff --git a/packages/SettingsLib/res/drawable/ic_info.xml b/core/res/res/drawable/ic_info.xml
similarity index 94%
rename from packages/SettingsLib/res/drawable/ic_info.xml
rename to core/res/res/drawable/ic_info.xml
index afe7e6b..f14c4b4 100644
--- a/packages/SettingsLib/res/drawable/ic_info.xml
+++ b/core/res/res/drawable/ic_info.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/colorAccent">
+ android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
diff --git a/core/res/res/values-mcc262-mnc02/strings.xml b/core/res/res/values-mcc262-mnc02/strings.xml
deleted file mode 100644
index 2b89401..0000000
--- a/core/res/res/values-mcc262-mnc02/strings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2017, Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Do not translate. Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi Calling</item>
- </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc370/strings.xml b/core/res/res/values-mcc302-mnc370/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc370/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi</item>
- </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc720/strings.xml b/core/res/res/values-mcc302-mnc720/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc720/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi</item>
- </string-array>
-</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2de2b9e..65b8807 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3606,6 +3606,8 @@
<flag name="flagRequestAccessibilityButton" value="0x00000100" />
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_FINGERPRINT_GESTURES}. -->
<flag name="flagRequestFingerprintGestures" value="0x00000200" />
+ <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK}. -->
+ <flag name="flagRequestShortcutWarningDialogSpokenFeedback" value="0x00000400" />
</attr>
<!-- Component name of an activity that allows the user to modify
the settings for this service. This setting cannot be changed at runtime. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 293d90e..1404383 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -913,7 +913,7 @@
<!-- Control whether to lock day/night mode change from normal application. When it is
true, day / night mode change is only allowed to apps with MODIFY_DAY_NIGHT_MODE
permission. -->
- <bool name="config_lockDayNightMode">true</bool>
+ <bool name="config_lockDayNightMode">false</bool>
<!-- Control the default night mode to use when there is no other mode override set.
One of the following values (see UiModeManager.java):
@@ -3486,6 +3486,9 @@
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
<string name="config_headlineFontFamilyMedium">@string/font_family_button_material</string>
+ <!-- Size of icon shown beside a preference locked by admin -->
+ <dimen name="config_restricted_icon_size">@dimen/restricted_icon_size_material</dimen>
+
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
<!-- Package name that should be granted Notification Assistant access -->
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 210f30e..a0b40ed 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -117,6 +117,9 @@
<!-- Default rounded corner for controls -->
<dimen name="control_corner_material">2dp</dimen>
+ <!-- Size of icon shown beside a preference locked by admin -->
+ <dimen name="restricted_icon_size_material">16dp</dimen>
+
<dimen name="edit_text_inset_horizontal_material">4dp</dimen>
<dimen name="edit_text_inset_top_material">10dp</dimen>
<dimen name="edit_text_inset_bottom_material">7dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c751af3..cc99a4e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,14 +2909,16 @@
<public name="opticalInsetBottom" />
<public name="allowForceDark" />
<public name="supportsAmbientMode" />
- </public-group>
-
- <public-group type="attr" first-id="0x0101058d">
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="usesNonSdkApi" />
<public name="minimumUiTimeout" />
</public-group>
+ <public-group type="drawable" first-id="0x010800b4">
+ <!-- @hide @SystemApi -->
+ <public name="ic_info" />
+ </public-group>
+
<public-group type="style" first-id="0x010302e2">
</public-group>
@@ -2943,6 +2945,11 @@
<public name="config_sendPackageName" />
</public-group>
+ <public-group type="dimen" first-id="0x01050007">
+ <!-- @hide @SystemApi -->
+ <public name="config_restricted_icon_size" />
+ </public-group>
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 365e4a4..6d0127a4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -240,10 +240,31 @@
<item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
</string-array>
<!-- Template for showing mobile network operator name while WFC is active -->
- <string-array name="wfcSpnFormats">
- <item>%s</item>
- <item>%s Wi-Fi Calling</item>
+ <string-array name="wfcSpnFormats" translatable="false">
+ <item>@string/wfcSpnFormat_spn</item>
+ <item>@string/wfcSpnFormat_spn_wifi_calling</item>
+ <item>@string/wfcSpnFormat_wlan_call</item>
+ <item>@string/wfcSpnFormat_spn_wlan_call</item>
+ <item>@string/wfcSpnFormat_spn_wifi</item>
+ <item>@string/wfcSpnFormat_wifi_calling_bar_spn</item>
+ <item>@string/wfcSpnFormat_spn_vowifi</item>
</string-array>
+
+ <!-- Spn during Wi-Fi Calling: "<operator>" -->
+ <string name="wfcSpnFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
+ <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi Calling" -->
+ <string name="wfcSpnFormat_spn_wifi_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi Calling</string>
+ <!-- Spn during Wi-Fi Calling: "WLAN Call" -->
+ <string name="wfcSpnFormat_wlan_call">WLAN Call</string>
+ <!-- Spn during Wi-Fi Calling: "<operator> WLAN Call" -->
+ <string name="wfcSpnFormat_spn_wlan_call"><xliff:g id="spn" example="Operator">%s</xliff:g> WLAN Call</string>
+ <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi" -->
+ <string name="wfcSpnFormat_spn_wifi"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi</string>
+ <!-- Spn during Wi-Fi Calling: "WiFi Calling | <operator>" -->
+ <string name="wfcSpnFormat_wifi_calling_bar_spn">WiFi Calling | <xliff:g id="spn" example="Operator">%s</xliff:g></string>
+ <!-- Spn during Wi-Fi Calling: "<operator> VoWifi" -->
+ <string name="wfcSpnFormat_spn_vowifi"><xliff:g id="spn" example="Operator">%s</xliff:g> VoWifi</string>
+
<!-- WFC, summary for Disabled -->
<string name="wifi_calling_off_summary">Off</string>
<!-- WFC, summary for Wi-Fi Preferred -->
@@ -1418,6 +1439,10 @@
<string name="biometric_not_recognized">Not recognized</string>
<!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] -->
<string name="fingerprint_authenticated">Fingerprint authenticated</string>
+ <!-- Accessibility message announced when a face has been authenticated [CHAR LIMIT=NONE] -->
+ <string name="face_authenticated_no_confirmation_required">Face authenticated</string>
+ <!-- Accessibility message announced when a face has been authenticated, but requires the user to press the confirm button [CHAR LIMIT=NONE] -->
+ <string name="face_authenticated_confirmation_required">Face authenticated, please press confirm</string>
<!-- Error message shown when the fingerprint hardware can't be accessed -->
<string name="fingerprint_error_hw_not_available">Fingerprint hardware not available.</string>
@@ -1495,6 +1520,8 @@
<string name="face_error_no_space">Face can\u2019t be stored.</string>
<!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
<string name="face_error_canceled">Face operation canceled.</string>
+ <!-- Generic error message shown when the face authentication operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=50] -->
+ <string name="face_error_user_canceled">Face authentication canceled by user.</string>
<!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
<string name="face_error_lockout">Too many attempts. Try again later.</string>
<!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
@@ -4334,6 +4361,9 @@
<string name="accessibility_shortcut_disabling_service">Accessibility Shortcut turned
<xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>
+ <!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] -->
+ <string name="accessibility_shortcut_spoken_feedback">Use Accessibility Shortcut again to start the current accessibility feature</string>
+
<!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
<string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7b6e064..cf85986 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2429,6 +2429,7 @@
<java-symbol type="string" name="face_error_timeout" />
<java-symbol type="array" name="face_error_vendor" />
<java-symbol type="string" name="face_error_canceled" />
+ <java-symbol type="string" name="face_error_user_canceled" />
<java-symbol type="string" name="face_error_lockout" />
<java-symbol type="string" name="face_error_lockout_permanent" />
<java-symbol type="string" name="face_error_not_enrolled" />
@@ -2446,6 +2447,8 @@
<java-symbol type="string" name="face_acquired_not_detected" />
<java-symbol type="array" name="face_acquired_vendor" />
<java-symbol type="string" name="face_name_template" />
+ <java-symbol type="string" name="face_authenticated_no_confirmation_required" />
+ <java-symbol type="string" name="face_authenticated_confirmation_required" />
<!-- Face config -->
<java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
@@ -3083,6 +3086,7 @@
<java-symbol type="string" name="color_inversion_feature_name" />
<java-symbol type="string" name="color_correction_feature_name" />
<java-symbol type="string" name="config_defaultAccessibilityService" />
+ <java-symbol type="string" name="accessibility_shortcut_spoken_feedback" />
<!-- Accessibility Button -->
<java-symbol type="layout" name="accessibility_button_chooser" />
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index cb4e5c4..6cf6a82 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,13 +18,17 @@
-->
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
-<zen version="7">
+<zen version="8">
<allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false"
reminders="false" events="false" repeatCallers="true" />
-
+ <automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1"
+ component="android/com.android.server.notification.EventConditionProvider"
+ conditionId="condition://android/event?userId=-10000&calendar=&reply=1"/>
+ <automatic ruleId="EVERY_NIGHT_DEFAULT_RULE" enabled="false" snoozing="false" name="Sleeping"
+ zen="1" component="android/com.android.server.notification.ScheduleConditionProvider"
+ conditionId="condition://android/schedule?days=1.2.3.4.5.6.7&start=22.0&end=7.0&exitAtAlarm=true"/>
<!-- all visual effects that exist as of P -->
<disallow visualEffects="511" />
-
<!-- whether there are notification channels that can bypass dnd -->
<state areChannelsBypassingDnd="false" />
</zen>
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 2f28606..b5ed01f 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -186,44 +186,28 @@
Paint p = new Paint();
final int count = end - start;
- final float[][] advanceArrays = new float[4][count];
-
- final float advance = p.getTextRunAdvances(str, start, end, contextStart, contextEnd,
- isRtl, advanceArrays[0], 0);
-
+ final int contextCount = contextEnd - contextStart;
+ final float[][] advanceArrays = new float[2][count];
char chars[] = str.toCharArray();
- final float advance_c = p.getTextRunAdvances(chars, start, count, contextStart,
- contextEnd - contextStart, isRtl, advanceArrays[1], 0);
- assertEquals(advance, advance_c, 1.0f);
-
+ final float advance = p.getTextRunAdvances(chars, start, count,
+ contextStart, contextCount, isRtl, advanceArrays[0], 0);
for (int c = 1; c < count; ++c) {
- final float firstPartAdvance = p.getTextRunAdvances(str, start, start + c,
- contextStart, contextEnd, isRtl, advanceArrays[2], 0);
- final float secondPartAdvance = p.getTextRunAdvances(str, start + c, end,
- contextStart, contextEnd, isRtl, advanceArrays[2], c);
+ final float firstPartAdvance = p.getTextRunAdvances(chars, start, c,
+ contextStart, contextCount, isRtl, advanceArrays[1], 0);
+ final float secondPartAdvance = p.getTextRunAdvances(chars, start + c, count - c,
+ contextStart, contextCount, isRtl, advanceArrays[1], c);
assertEquals(advance, firstPartAdvance + secondPartAdvance, 1.0f);
-
- final float firstPartAdvance_c = p.getTextRunAdvances(chars, start, c,
- contextStart, contextEnd - contextStart, isRtl, advanceArrays[3], 0);
- final float secondPartAdvance_c = p.getTextRunAdvances(chars, start + c,
- count - c, contextStart, contextEnd - contextStart, isRtl,
- advanceArrays[3], c);
- assertEquals(advance, firstPartAdvance_c + secondPartAdvance_c, 1.0f);
- assertEquals(firstPartAdvance, firstPartAdvance_c, 1.0f);
- assertEquals(secondPartAdvance, secondPartAdvance_c, 1.0f);
-
- for (int i = 1; i < advanceArrays.length; i++) {
- for (int j = 0; j < count; j++) {
- assertEquals(advanceArrays[0][j], advanceArrays[i][j], 1.0f);
- }
+ for (int j = 0; j < count; j++) {
+ assertEquals(advanceArrays[0][j], advanceArrays[1][j], 1.0f);
}
+
// Compare results with measureText, getRunAdvance, and getTextWidths.
if (compareWithOtherMethods && start == contextStart && end == contextEnd) {
assertEquals(advance, p.measureText(str, start, end), 1.0f);
assertEquals(advance, p.getRunAdvance(
- str, start, end, contextStart, contextEnd, isRtl, end), 1.0f);
+ chars, start, count, contextStart, contextCount, isRtl, end), 1.0f);
final float[] widths = new float[count];
p.getTextWidths(str, start, end, widths);
@@ -236,19 +220,7 @@
public void testGetTextRunAdvances_invalid() {
Paint p = new Paint();
- String text = "test";
-
- try {
- p.getTextRunAdvances((String)null, 0, 0, 0, 0, false, null, 0);
- fail("Should throw an IllegalArgumentException.");
- } catch (IllegalArgumentException e) {
- }
-
- try {
- p.getTextRunAdvances((CharSequence)null, 0, 0, 0, 0, false, null, 0);
- fail("Should throw an IllegalArgumentException.");
- } catch (IllegalArgumentException e) {
- }
+ char[] text = "test".toCharArray();
try {
p.getTextRunAdvances((char[])null, 0, 0, 0, 0, false, null, 0);
@@ -257,50 +229,43 @@
}
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
- new float[text.length() - 1], 0);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length, false,
+ new float[text.length - 1], 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
- new float[text.length()], 1);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length, false,
+ new float[text.length], 1);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// 0 > contextStart
try {
- p.getTextRunAdvances(text, 0, text.length(), -1, text.length(), false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, -1, text.length, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// contextStart > start
try {
- p.getTextRunAdvances(text, 0, text.length(), 1, text.length(), false, null, 0);
- fail("Should throw an IndexOutOfBoundsException.");
- } catch (IndexOutOfBoundsException e) {
- }
-
- // start > end
- try {
- p.getTextRunAdvances(text, 1, 0, 0, text.length(), false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, 1, text.length, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// end > contextEnd
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length() - 1, false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length - 1, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
// contextEnd > text.length
try {
- p.getTextRunAdvances(text, 0, text.length(), 0, text.length() + 1, false, null, 0);
+ p.getTextRunAdvances(text, 0, text.length, 0, text.length + 1, false, null, 0);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 1c4039b..3ce2589 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -44,7 +44,7 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
-import java.util.HashSet;
+import java.util.ArrayList;
import java.util.Locale;
@SmallTest
@@ -112,7 +112,7 @@
private static void buildSystemFallback(String xml,
ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
- final HashSet<Font> availableFonts = new HashSet<>();
+ final ArrayList<Font> availableFonts = new ArrayList<>();
try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) {
fos.write(xml.getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
@@ -127,7 +127,7 @@
public void testBuildSystemFallback() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
- final HashSet<Font> availableFonts = new HashSet<>();
+ final ArrayList<Font> availableFonts = new ArrayList<>();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML,
SYSTEM_FONT_DIR, fallbackMap, availableFonts);
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index e84aed1..fee470d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -241,7 +241,6 @@
Settings.Global.EUICC_SUPPORTED_COUNTRIES,
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
- Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index 355be61..898e78c 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -33,7 +33,7 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
-import java.util.HashSet;
+import java.util.ArrayList;
public class FontFallbackSetup implements AutoCloseable {
private final String[] mTestFontFiles;
@@ -76,7 +76,7 @@
}
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
- final HashSet<Font> availableFonts = new HashSet<>();
+ final ArrayList<Font> availableFonts = new ArrayList<>();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml,
mTestFontsDir, fallbackMap, availableFonts);
Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases);
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 922b79a..69d2828 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -57,7 +57,7 @@
// The number of flags held in boolean properties. Their values should also be double-checked
// in the methods above.
- private static final int NUM_BOOLEAN_PROPERTIES = 17;
+ private static final int NUM_BOOLEAN_PROPERTIES = 18;
@Test
public void testStandardActions_serializationFlagIsValid() {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index f3e10e0..a302657 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -16,6 +16,31 @@
package com.android.internal.accessibility;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.fail;
+import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.AlertDialog;
import android.content.ComponentName;
@@ -25,11 +50,14 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
+import android.media.Ringtone;
import android.os.Handler;
+import android.os.Message;
import android.os.Vibrator;
import android.provider.Settings;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.Voice;
import android.support.test.runner.AndroidJUnit4;
-
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.view.Window;
@@ -37,9 +65,10 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.widget.Toast;
+
import com.android.internal.R;
-import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider;
+import com.android.internal.util.test.FakeSettingsProvider;
import org.junit.After;
import org.junit.Before;
@@ -48,31 +77,12 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Map;
-import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
-import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED;
-import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
-import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.fail;
-import static org.mockito.AdditionalMatchers.aryEq;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutControllerTest {
@@ -102,6 +112,9 @@
private @Mock Vibrator mVibrator;
private @Mock ApplicationInfo mApplicationInfo;
private @Mock PackageManager mPackageManager;
+ private @Mock TextToSpeech mTextToSpeech;
+ private @Mock Voice mVoice;
+ private @Mock Ringtone mRingtone;
private MockContentResolver mContentResolver;
private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
@@ -137,6 +150,9 @@
when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
.thenReturn(mToast);
when(mFrameworkObjectProvider.getSystemUiContext()).thenReturn(mContext);
+ when(mFrameworkObjectProvider.getTextToSpeech(eq(mContext), any()))
+ .thenReturn(mTextToSpeech);
+ when(mFrameworkObjectProvider.getRingtone(eq(mContext), any())).thenReturn(mRingtone);
when(mResources.getString(anyInt())).thenReturn("Howdy %s");
when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT);
@@ -172,6 +188,8 @@
throw new RuntimeException("Unable to set mWindowAttributes", e);
}
when(mAlertDialog.getWindow()).thenReturn(window);
+
+ when(mTextToSpeech.getVoice()).thenReturn(mVoice);
}
@After
@@ -308,8 +326,10 @@
mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0));
verify(mResources).getString(R.string.accessibility_shortcut_toogle_warning);
verify(mAlertDialog).show();
- verify(mAccessibilityManagerService).getInstalledAccessibilityServiceList(anyInt());
+ verify(mAccessibilityManagerService, atLeastOnce()).getInstalledAccessibilityServiceList(
+ anyInt());
verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut();
+ verify(mFrameworkObjectProvider, times(0)).getTextToSpeech(any(), any());
}
@Test
@@ -434,6 +454,49 @@
verify(mAccessibilityManagerService).performAccessibilityShortcut();
}
+ @Test
+ public void testOnAccessibilityShortcut_showsWarningDialog_shouldTtsSpokenPrompt() {
+ configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+ configureValidShortcutService();
+ configureTtsSpokenPromptEnabled();
+ configureHandlerCallbackInvocation();
+ AccessibilityShortcutController accessibilityShortcutController = getController();
+ Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+ accessibilityShortcutController.performAccessibilityShortcut();
+
+ verify(mAlertDialog).show();
+ ArgumentCaptor<TextToSpeech.OnInitListener> onInitCap = ArgumentCaptor.forClass(
+ TextToSpeech.OnInitListener.class);
+ verify(mFrameworkObjectProvider).getTextToSpeech(any(), onInitCap.capture());
+ onInitCap.getValue().onInit(TextToSpeech.SUCCESS);
+ verify(mTextToSpeech).speak(any(), eq(TextToSpeech.QUEUE_FLUSH), any(), any());
+ ArgumentCaptor<DialogInterface.OnDismissListener> onDismissCap = ArgumentCaptor.forClass(
+ DialogInterface.OnDismissListener.class);
+ verify(mAlertDialog).setOnDismissListener(onDismissCap.capture());
+ onDismissCap.getValue().onDismiss(mAlertDialog);
+ verify(mTextToSpeech).shutdown();
+ verify(mRingtone, times(0)).play();
+ }
+
+ @Test
+ public void testOnAccessibilityShortcut_showsWarningDialog_ttsInitFail_noSpokenPrompt() {
+ configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+ configureValidShortcutService();
+ configureTtsSpokenPromptEnabled();
+ configureHandlerCallbackInvocation();
+ AccessibilityShortcutController accessibilityShortcutController = getController();
+ Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+ accessibilityShortcutController.performAccessibilityShortcut();
+
+ verify(mAlertDialog).show();
+ ArgumentCaptor<TextToSpeech.OnInitListener> onInitCap = ArgumentCaptor.forClass(
+ TextToSpeech.OnInitListener.class);
+ verify(mFrameworkObjectProvider).getTextToSpeech(any(), onInitCap.capture());
+ onInitCap.getValue().onInit(TextToSpeech.ERROR);
+ verify(mTextToSpeech, times(0)).speak(any(), anyInt(), any(), any());
+ verify(mRingtone).play();
+ }
+
private void configureNoShortcutService() {
Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
}
@@ -481,6 +544,19 @@
mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, lockscreen ? 1 : 0);
}
+ private void configureTtsSpokenPromptEnabled() {
+ mServiceInfo.flags |= AccessibilityServiceInfo
+ .FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK;
+ }
+
+ private void configureHandlerCallbackInvocation() {
+ doAnswer((InvocationOnMock invocation) -> {
+ Message m = (Message) invocation.getArguments()[0];
+ m.getCallback().run();
+ return true;
+ }).when(mHandler).sendMessageAtTime(any(), anyLong());
+ }
+
private AccessibilityShortcutController getController() {
AccessibilityShortcutController accessibilityShortcutController =
new AccessibilityShortcutController(mContext, mHandler, 0);
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
new file mode 100644
index 0000000..8179328
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SubtypeLocaleUtilsTest {
+ private static final Locale LOCALE_EN = new Locale("en");
+ private static final Locale LOCALE_EN_US = new Locale("en", "US");
+ private static final Locale LOCALE_EN_GB = new Locale("en", "GB");
+ private static final Locale LOCALE_EN_IN = new Locale("en", "IN");
+ private static final Locale LOCALE_FIL = new Locale("fil");
+ private static final Locale LOCALE_FIL_PH = new Locale("fil", "PH");
+ private static final Locale LOCALE_JA = new Locale("ja");
+ private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
+ private static final Locale LOCALE_TH = new Locale("ht");
+ private static final Locale LOCALE_TH_TH = new Locale("ht", "TH");
+ private static final Locale LOCALE_TH_TH_TH = new Locale("ht", "TH", "TH");
+
+ @Test
+ public void testConstructLocaleFromString() throws Exception {
+ assertEquals(new Locale("en"), SubtypeLocaleUtils.constructLocaleFromString("en"));
+ assertEquals(new Locale("en", "US"), SubtypeLocaleUtils.constructLocaleFromString("en_US"));
+ assertEquals(new Locale("en", "US", "POSIX"),
+ SubtypeLocaleUtils.constructLocaleFromString("en_US_POSIX"));
+
+ // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not
+ // support three letter language codes, and used "tl" (Tagalog) as the language string for
+ // "fil" (Filipino).
+ assertEquals(new Locale("fil"), SubtypeLocaleUtils.constructLocaleFromString("tl"));
+ assertEquals(new Locale("fil", "PH"),
+ SubtypeLocaleUtils.constructLocaleFromString("tl_PH"));
+ assertEquals(new Locale("fil", "PH", "POSIX"),
+ SubtypeLocaleUtils.constructLocaleFromString("tl_PH_POSIX"));
+
+ // So far rejecting an invalid/unexpected locale string is out of the scope of this method.
+ assertEquals(new Locale("a"), SubtypeLocaleUtils.constructLocaleFromString("a"));
+ assertEquals(new Locale("a b c"), SubtypeLocaleUtils.constructLocaleFromString("a b c"));
+ assertEquals(new Locale("en-US"), SubtypeLocaleUtils.constructLocaleFromString("en-US"));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 0eb3d06..0c8dd9d 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -21,6 +21,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.Message;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -74,14 +75,17 @@
public void testSingleMessageDispatched() {
TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ Message message = mHandlerFirst.obtainMessage(1000);
+ message.workSourceUid = 1000;
Object token = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
looperStats.tickThreadTime(10);
- looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000));
+ looperStats.messageDispatched(token, message);
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
assertThat(entries).hasSize(1);
LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.workSourceUid).isEqualTo(1000);
assertThat(entry.threadName).isEqualTo("TestThread1");
assertThat(entry.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -100,15 +104,17 @@
public void testThrewException() {
TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ Message message = mHandlerFirst.obtainMessage(7);
+ message.workSourceUid = 123;
Object token = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
looperStats.tickThreadTime(10);
- looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7),
- new ArithmeticException());
+ looperStats.dispatchingThrewException(token, message, new ArithmeticException());
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
assertThat(entries).hasSize(1);
LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.workSourceUid).isEqualTo(123);
assertThat(entry.threadName).isEqualTo("TestThread1");
assertThat(entry.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -146,31 +152,39 @@
looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> {
}));
- // Contributes to entry1.
+ // Will not be sampled so does not contribute to any entries.
Object token4 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(0));
+
+ // Contributes to entry1.
+ Object token5 = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
looperStats.tickThreadTime(100);
- looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1));
+ looperStats.messageDispatched(token5, mHandlerAnonymous.obtainMessage(1));
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
assertThat(entries).hasSize(3);
entries.sort(Comparator.comparing(e -> e.handlerClassName));
- // Captures data for token4 call.
+ // Captures data for token5 call.
LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.workSourceUid).isEqualTo(-1);
assertThat(entry1.threadName).isEqualTo("TestThread1");
assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1");
assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */);
assertThat(entry1.messageCount).isEqualTo(1);
- assertThat(entry1.recordedMessageCount).isEqualTo(0);
+ assertThat(entry1.recordedMessageCount).isEqualTo(1);
assertThat(entry1.exceptionCount).isEqualTo(0);
- assertThat(entry1.totalLatencyMicros).isEqualTo(0);
- assertThat(entry1.maxLatencyMicros).isEqualTo(0);
- assertThat(entry1.cpuUsageMicros).isEqualTo(0);
- assertThat(entry1.maxCpuUsageMicros).isEqualTo(0);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(100);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(100);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(100);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(100);
// Captures data for token1 and token2 calls.
LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.workSourceUid).isEqualTo(-1);
assertThat(entry2.threadName).isEqualTo("TestThread1");
assertThat(entry2.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -185,6 +199,7 @@
// Captures data for token3 call.
LooperStats.ExportedEntry entry3 = entries.get(2);
+ assertThat(entry3.workSourceUid).isEqualTo(-1);
assertThat(entry3.threadName).isEqualTo("TestThread2");
assertThat(entry3.handlerClassName).isEqualTo(
"com.android.internal.os.LooperStatsTest$TestHandlerSecond");
@@ -265,7 +280,7 @@
@Test
public void testMessagesOverSizeCap() {
- TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */);
+ TestableLooperStats looperStats = new TestableLooperStats(1, 1 /* sizeCap */);
Object token1 = looperStats.messageDispatchStarting();
looperStats.tickRealtime(100);
@@ -296,12 +311,12 @@
assertThat(entry1.handlerClassName).isEqualTo("");
assertThat(entry1.messageName).isEqualTo("OVERFLOW");
assertThat(entry1.messageCount).isEqualTo(3);
- assertThat(entry1.recordedMessageCount).isEqualTo(1);
+ assertThat(entry1.recordedMessageCount).isEqualTo(3);
assertThat(entry1.exceptionCount).isEqualTo(0);
- assertThat(entry1.totalLatencyMicros).isEqualTo(10);
- assertThat(entry1.maxLatencyMicros).isEqualTo(10);
- assertThat(entry1.cpuUsageMicros).isEqualTo(10);
- assertThat(entry1.maxCpuUsageMicros).isEqualTo(10);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(70);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(50);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(40);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(20);
LooperStats.ExportedEntry entry2 = entries.get(1);
assertThat(entry2.threadName).isEqualTo("TestThread1");
@@ -342,6 +357,20 @@
assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2);
}
+ @Test
+ public void testReset() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 1);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(2000));
+ looperStats.reset();
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(0);
+ }
+
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
try {
r.run();
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c4017d15..73c10d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -210,6 +210,11 @@
<allow-in-power-save-except-idle package="com.android.providers.calendar" />
<allow-in-power-save-except-idle package="com.android.providers.contacts" />
+ <!-- The PAC proxy process must have network access, otherwise no app will
+ be able to connect to the internet when such a proxy is in use, since
+ all outgoing connections originate from this app. -->
+ <allow-in-power-save-except-idle package="com.android.proxyhandler" />
+
<!-- These are the packages that are white-listed to be able to run as system user -->
<system-user-whitelisted-app package="com.android.settings" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 616a8d6..114c228 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -281,6 +281,7 @@
<permission name="android.permission.MANAGE_FINGERPRINT"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+ <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
</privapp-permissions>
<privapp-permissions package="com.android.sharedstoragebackup">
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index 522db3c..b463dd8 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -21,8 +21,6 @@
key 0x126 DPAD_DOWN
key 0x127 DPAD_LEFT
-key 0x120 BUTTON_SELECT
-key 0x123 BUTTON_START
key 0x12e BUTTON_A
key 0x12d BUTTON_B
key 0x12f BUTTON_X
@@ -34,9 +32,6 @@
key 0x121 BUTTON_THUMBL
key 0x122 BUTTON_THUMBR
-# PS key
-key 0x2d0 BUTTON_MODE
-
# Left Analog Stick
axis 0x00 X
axis 0x01 Y
@@ -74,3 +69,11 @@
# Square
# axis 0x37
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x120 BUTTON_SELECT
+# Start
+key 0x123 BUTTON_START
+# PS key
+key 0x2d0 BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+# and when connected over Bluetooth
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+# and when connected over Bluetooth
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
new file mode 100644
index 0000000..5fe35f7
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.12 and when
+# connected over USB
+#
+
+key 0x220 DPAD_UP
+key 0x223 DPAD_RIGHT
+key 0x221 DPAD_DOWN
+key 0x222 DPAD_LEFT
+
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_X
+key 0x133 BUTTON_Y
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# L2 trigger
+axis 0x02 LTRIGGER
+
+# R2 trigger
+axis 0x05 RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a BUTTON_SELECT
+# Start
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4.kl b/data/keyboards/Vendor_054c_Product_05c4.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4.kl
@@ -60,7 +60,6 @@
key 0x138 BUTTON_SELECT
# Options
key 0x139 BUTTON_START
-
# PS key
key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+# connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc.kl b/data/keyboards/Vendor_054c_Product_09cc.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc.kl
@@ -60,7 +60,6 @@
key 0x138 BUTTON_SELECT
# Options
key 0x139 BUTTON_START
-
# PS key
key 0x13c BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+# and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+# connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_0ba0.kl b/data/keyboards/Vendor_054c_Product_0ba0.kl
new file mode 100644
index 0000000..bc6fc3b
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0.kl
@@ -0,0 +1,70 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# L2 axis
+axis 0x03 LTRIGGER
+# R2 axis
+axis 0x04 RTRIGGER
+
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+# The touchpad for this joystick will become a separate input device in future releases
+# and this button will be equivalent to left mouse button
+# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
new file mode 100644
index 0000000..8b85a38
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
@@ -0,0 +1,67 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+# - Version 0x8111 is for Linux hid-sony driver >=4.10
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a BUTTON_SELECT
+# Options
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 9b5027d..43282d3 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -17,6 +17,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
+import android.os.Build;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -166,7 +167,7 @@
This can be called from JNI code.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private BitmapRegionDecoder(long decoder) {
mNativeBitmapRegionDecoder = decoder;
mRecycled = false;
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index ef58c8f..36c1c21 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -126,7 +126,7 @@
}
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public Canvas(long nativeCanvas) {
if (nativeCanvas == 0) {
throw new IllegalStateException();
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 8162544..6f030ff 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -18,6 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
+import android.os.Build;
import java.io.FileInputStream;
import java.io.InputStream;
@@ -30,7 +31,7 @@
@UnsupportedAppUsage
private long mNativeMovie;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Movie(long nativeMovie) {
if (nativeMovie == 0) {
throw new RuntimeException("native movie creation failed");
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 6f30653..33caa00 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,7 +17,10 @@
package android.graphics;
import android.annotation.ColorInt;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.Px;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
@@ -35,6 +38,8 @@
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -307,38 +312,47 @@
*/
public static final int DIRECTION_RTL = 1;
+ /** @hide */
+ @IntDef(prefix = { "CURSOR_" }, value = {
+ CURSOR_AFTER, CURSOR_AT_OR_AFTER, CURSOR_BEFORE, CURSOR_AT_OR_BEFORE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CursorOption {}
+
/**
- * Option for getTextRunCursor to compute the valid cursor after
- * offset or the limit of the context, whichever is less.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor after offset or the limit of the context, whichever is less.
*/
public static final int CURSOR_AFTER = 0;
/**
- * Option for getTextRunCursor to compute the valid cursor at or after
- * the offset or the limit of the context, whichever is less.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor at or after the offset or the limit of the context, whichever is
+ * less.
*/
public static final int CURSOR_AT_OR_AFTER = 1;
/**
- * Option for getTextRunCursor to compute the valid cursor before
- * offset or the start of the context, whichever is greater.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor before offset or the start of the context, whichever is greater.
*/
public static final int CURSOR_BEFORE = 2;
/**
- * Option for getTextRunCursor to compute the valid cursor at or before
- * offset or the start of the context, whichever is greater.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Compute the valid cursor at or before offset or the start of the context, whichever is
+ * greater.
*/
public static final int CURSOR_AT_OR_BEFORE = 3;
/**
- * Option for getTextRunCursor to return offset if the cursor at offset
- * is valid, or -1 if it isn't.
- * @hide
+ * Option for getTextRunCursor.
+ *
+ * Return offset if the cursor at offset is valid, or -1 if it isn't.
*/
public static final int CURSOR_AT = 4;
@@ -2325,17 +2339,53 @@
}
/**
- * Convenience overload that takes a char array instead of a
- * String.
+ * Retrieve the character advances of the text.
*
- * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
- * @hide
+ * Returns the total advance width for the characters in the run from {@code index} for
+ * {@code count} of chars, and if {@code advances} is not null, the advance assigned to each of
+ * these characters (java chars).
+ *
+ * <p>
+ * The trailing surrogate in a valid surrogate pair is assigned an advance of 0. Thus the
+ * number of returned advances is always equal to count, not to the number of unicode codepoints
+ * represented by the run.
+ * </p>
+ *
+ * <p>
+ * In the case of conjuncts or combining marks, the total advance is assigned to the first
+ * logical character, and the following characters are assigned an advance of 0.
+ * </p>
+ *
+ * <p>
+ * This generates the sum of the advances of glyphs for characters in a reordered cluster as the
+ * width of the first logical character in the cluster, and 0 for the widths of all other
+ * characters in the cluster. In effect, such clusters are treated like conjuncts.
+ * </p>
+ *
+ * <p>
+ * The shaping bounds limit the amount of context available outside start and end that can be
+ * used for shaping analysis. These bounds typically reflect changes in bidi level or font
+ * metrics across which shaping does not occur.
+ * </p>
+ *
+ * @param chars the text to measure.
+ * @param index the index of the first character to measure
+ * @param count the number of characters to measure
+ * @param contextIndex the index of the first character to use for shaping context.
+ * Context must cover the measureing target.
+ * @param contextCount the number of character to use for shaping context.
+ * Context must cover the measureing target.
+ * @param isRtl whether the run is in RTL direction
+ * @param advances array to receive the advances, must have room for all advances.
+ * This can be null if only total advance is needed
+ * @param advancesIndex the position in advances at which to put the advance corresponding to
+ * the character at start
+ * @return the total advance in pixels
*/
- @UnsupportedAppUsage
- public float getTextRunAdvances(char[] chars, int index, int count,
- int contextIndex, int contextCount, boolean isRtl, float[] advances,
- int advancesIndex) {
-
+ public float getTextRunAdvances(@NonNull char[] chars, @IntRange(from = 0) int index,
+ @IntRange(from = 0) int count, @IntRange(from = 0) int contextIndex,
+ @IntRange(from = 0) int contextCount, boolean isRtl, @Nullable float[] advances,
+ @IntRange(from = 0) int advancesIndex) {
if (chars == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2372,159 +2422,32 @@
}
/**
- * Convenience overload that takes a CharSequence instead of a
- * String.
+ * Returns the next cursor position in the run.
*
- * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
- * @hide
- */
- public float getTextRunAdvances(CharSequence text, int start, int end,
- int contextStart, int contextEnd, boolean isRtl, float[] advances,
- int advancesIndex) {
- if (text == null) {
- throw new IllegalArgumentException("text cannot be null");
- }
- if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
- | (start - contextStart) | (contextEnd - end)
- | (text.length() - contextEnd)
- | (advances == null ? 0 :
- (advances.length - advancesIndex - (end - start)))) < 0) {
- throw new IndexOutOfBoundsException();
- }
-
- if (text instanceof String) {
- return getTextRunAdvances((String) text, start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex);
- }
- if (text instanceof SpannedString ||
- text instanceof SpannableString) {
- return getTextRunAdvances(text.toString(), start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex);
- }
- if (text instanceof GraphicsOperations) {
- return ((GraphicsOperations) text).getTextRunAdvances(start, end,
- contextStart, contextEnd, isRtl, advances, advancesIndex, this);
- }
- if (text.length() == 0 || end == start) {
- return 0f;
- }
-
- int contextLen = contextEnd - contextStart;
- int len = end - start;
- char[] buf = TemporaryBuffer.obtain(contextLen);
- TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- float result = getTextRunAdvances(buf, start - contextStart, len,
- 0, contextLen, isRtl, advances, advancesIndex);
- TemporaryBuffer.recycle(buf);
- return result;
- }
-
- /**
- * Returns the total advance width for the characters in the run
- * between start and end, and if advances is not null, the advance
- * assigned to each of these characters (java chars).
+ * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+ * between base characters and combining marks, or within a reordering cluster.
*
- * <p>The trailing surrogate in a valid surrogate pair is assigned
- * an advance of 0. Thus the number of returned advances is
- * always equal to count, not to the number of unicode codepoints
- * represented by the run.
+ * <p>
+ * ContextStart and offset are relative to the start of text.
+ * The context is the shaping context for cursor movement, generally the bounds of the metric
+ * span enclosing the cursor in the direction of movement.
*
- * <p>In the case of conjuncts or combining marks, the total
- * advance is assigned to the first logical character, and the
- * following characters are assigned an advance of 0.
- *
- * <p>This generates the sum of the advances of glyphs for
- * characters in a reordered cluster as the width of the first
- * logical character in the cluster, and 0 for the widths of all
- * other characters in the cluster. In effect, such clusters are
- * treated like conjuncts.
- *
- * <p>The shaping bounds limit the amount of context available
- * outside start and end that can be used for shaping analysis.
- * These bounds typically reflect changes in bidi level or font
- * metrics across which shaping does not occur.
- *
- * @param text the text to measure. Cannot be null.
- * @param start the index of the first character to measure
- * @param end the index past the last character to measure
- * @param contextStart the index of the first character to use for shaping context,
- * must be <= start
- * @param contextEnd the index past the last character to use for shaping context,
- * must be >= end
- * @param isRtl whether the run is in RTL direction
- * @param advances array to receive the advances, must have room for all advances,
- * can be null if only total advance is needed
- * @param advancesIndex the position in advances at which to put the
- * advance corresponding to the character at start
- * @return the total advance
- *
- * @hide
- */
- public float getTextRunAdvances(String text, int start, int end, int contextStart,
- int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
- if (text == null) {
- throw new IllegalArgumentException("text cannot be null");
- }
- if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
- | (start - contextStart) | (contextEnd - end)
- | (text.length() - contextEnd)
- | (advances == null ? 0 :
- (advances.length - advancesIndex - (end - start)))) < 0) {
- throw new IndexOutOfBoundsException();
- }
-
- if (text.length() == 0 || start == end) {
- return 0f;
- }
-
- if (!mHasCompatScaling) {
- return nGetTextAdvances(mNativePaint, text, start, end, contextStart, contextEnd,
- isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
- }
-
- final float oldSize = getTextSize();
- setTextSize(oldSize * mCompatScaling);
- final float totalAdvance = nGetTextAdvances(mNativePaint, text, start, end, contextStart,
- contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
- setTextSize(oldSize);
-
- if (advances != null) {
- for (int i = advancesIndex, e = i + (end - start); i < e; i++) {
- advances[i] *= mInvCompatScaling;
- }
- }
- return totalAdvance * mInvCompatScaling; // assume errors are insignificant
- }
-
- /**
- * Returns the next cursor position in the run. This avoids placing the
- * cursor between surrogates, between characters that form conjuncts,
- * between base characters and combining marks, or within a reordering
- * cluster.
- *
- * <p>ContextStart and offset are relative to the start of text.
- * The context is the shaping context for cursor movement, generally
- * the bounds of the metric span enclosing the cursor in the direction of
- * movement.
- *
- * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
- * cursor position, this returns -1. Otherwise this will never return a
- * value before contextStart or after contextStart + contextLength.
+ * <p>
+ * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+ * returns -1. Otherwise this will never return a value before contextStart or after
+ * contextStart + contextLength.
*
* @param text the text
* @param contextStart the start of the context
* @param contextLength the length of the context
- * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param isRtl true if the paragraph context is RTL, otherwise false
* @param offset the cursor position to move from
- * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
- * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
- * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+ * @param cursorOpt how to move the cursor
* @return the offset of the next position, or -1
- * @hide
*/
- @UnsupportedAppUsage
- public int getTextRunCursor(char[] text, int contextStart, int contextLength,
- int dir, int offset, int cursorOpt) {
+ public int getTextRunCursor(@NonNull char[] text, @IntRange(from = 0) int contextStart,
+ @IntRange(from = 0) int contextLength, boolean isRtl, @IntRange(from = 0) int offset,
+ @CursorOption int cursorOpt) {
int contextEnd = contextStart + contextLength;
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
@@ -2533,85 +2456,87 @@
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength, dir, offset,
- cursorOpt);
+ return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength,
+ isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
}
/**
- * Returns the next cursor position in the run. This avoids placing the
- * cursor between surrogates, between characters that form conjuncts,
- * between base characters and combining marks, or within a reordering
- * cluster.
+ * Returns the next cursor position in the run.
*
- * <p>ContextStart, contextEnd, and offset are relative to the start of
+ * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+ * between base characters and combining marks, or within a reordering cluster.
+ *
+ * <p>
+ * ContextStart, contextEnd, and offset are relative to the start of
* text. The context is the shaping context for cursor movement, generally
* the bounds of the metric span enclosing the cursor in the direction of
* movement.
*
- * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
- * cursor position, this returns -1. Otherwise this will never return a
- * value before contextStart or after contextEnd.
+ * <p>
+ * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+ * returns -1. Otherwise this will never return a value before contextStart or after
+ * contextEnd.
*
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param isRtl true if the paragraph context is RTL, otherwise false
* @param offset the cursor position to move from
- * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
- * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
- * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+ * @param cursorOpt how to move the cursor
* @return the offset of the next position, or -1
- * @hide
*/
- public int getTextRunCursor(CharSequence text, int contextStart,
- int contextEnd, int dir, int offset, int cursorOpt) {
+ public int getTextRunCursor(@NonNull CharSequence text, @IntRange(from = 0) int contextStart,
+ @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+ @CursorOption int cursorOpt) {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
return getTextRunCursor(text.toString(), contextStart, contextEnd,
- dir, offset, cursorOpt);
+ isRtl, offset, cursorOpt);
}
if (text instanceof GraphicsOperations) {
return ((GraphicsOperations) text).getTextRunCursor(
- contextStart, contextEnd, dir, offset, cursorOpt, this);
+ contextStart, contextEnd, isRtl, offset, cursorOpt, this);
}
int contextLen = contextEnd - contextStart;
char[] buf = TemporaryBuffer.obtain(contextLen);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
- int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
+ int relPos = getTextRunCursor(buf, 0, contextLen, isRtl, offset - contextStart, cursorOpt);
TemporaryBuffer.recycle(buf);
return (relPos == -1) ? -1 : relPos + contextStart;
}
/**
- * Returns the next cursor position in the run. This avoids placing the
- * cursor between surrogates, between characters that form conjuncts,
- * between base characters and combining marks, or within a reordering
- * cluster.
+ * Returns the next cursor position in the run.
*
- * <p>ContextStart, contextEnd, and offset are relative to the start of
- * text. The context is the shaping context for cursor movement, generally
- * the bounds of the metric span enclosing the cursor in the direction of
- * movement.
+ * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+ * between base characters and combining marks, or within a reordering cluster.
*
- * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
- * cursor position, this returns -1. Otherwise this will never return a
- * value before contextStart or after contextEnd.
+ * <p>
+ * ContextStart, contextEnd, and offset are relative to the start of text. The context is the
+ * shaping context for cursor movement, generally the bounds of the metric span enclosing the
+ * cursor in the direction of movement.
+ * </p>
+ *
+ * <p>
+ * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+ * returns -1. Otherwise this will never return a value before contextStart or after
+ * contextEnd.
+ * </p>
*
* @param text the text
* @param contextStart the start of the context
* @param contextEnd the end of the context
- * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+ * @param isRtl true if the paragraph context is RTL, otherwise false.
* @param offset the cursor position to move from
- * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
- * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
- * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+ * @param cursorOpt how to move the cursor
* @return the offset of the next position, or -1
* @hide
*/
- public int getTextRunCursor(String text, int contextStart, int contextEnd,
- int dir, int offset, int cursorOpt) {
+ public int getTextRunCursor(@NonNull String text, @IntRange(from = 0) int contextStart,
+ @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+ @CursorOption int cursorOpt) {
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
| (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2619,8 +2544,8 @@
throw new IndexOutOfBoundsException();
}
- return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd, dir, offset,
- cursorOpt);
+ return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd,
+ isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
}
/**
@@ -2686,11 +2611,13 @@
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
*
+ * Note that styles are ignored even if you pass {@link android.text.Spanned} instance.
+ * Use {@link android.text.StaticLayout} for measuring bounds of {@link android.text.Spanned}.
+ *
* @param text text to measure and return its bounds
* @param start index of the first char in the text to measure
* @param end 1 past the last char in the text to measure
* @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
- * @hide
*/
public void getTextBounds(CharSequence text, int start, int end, Rect bounds) {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5aa09ce..492c236 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -30,6 +30,7 @@
import android.graphics.fonts.FontVariationAxis;
import android.graphics.fonts.SystemFonts;
import android.net.Uri;
+import android.os.Build;
import android.provider.FontRequest;
import android.provider.FontsContract;
import android.text.FontConfig;
@@ -1076,7 +1077,7 @@
}
// don't allow clients to call this directly
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Typeface(long ni) {
if (ni == 0) {
throw new RuntimeException("native typeface cannot be made");
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 00380c5..11a46c4 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -24,6 +24,7 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.LongSparseLongArray;
@@ -67,7 +68,7 @@
private static final String ELEMENT_TRANSITION = "transition";
private static final String ELEMENT_ITEM = "item";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private AnimatedStateListState mState;
/** The currently running transition, if any. */
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index a337773..7efe522 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -34,6 +34,7 @@
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Parcel;
@@ -101,7 +102,7 @@
private static final int VERSION_STREAM_SERIALIZER = 1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mType;
private ColorStateList mTintList;
@@ -118,7 +119,7 @@
// TYPE_RESOURCE: package name
// TYPE_URI: uri string
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private String mString1;
// TYPE_RESOURCE: resId
@@ -174,7 +175,7 @@
* valid compressed bitmap data is found.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getDataOffset() {
if (mType != TYPE_DATA) {
throw new IllegalStateException("called getDataOffset() on " + this);
@@ -189,7 +190,7 @@
* bitmap data.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public byte[] getDataBytes() {
if (mType != TYPE_DATA) {
throw new IllegalStateException("called getDataBytes() on " + this);
@@ -203,7 +204,7 @@
* @return The {@link android.content.res.Resources} for this {@link #TYPE_RESOURCE} Icon.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public Resources getResources() {
if (mType != TYPE_RESOURCE) {
throw new IllegalStateException("called getResources() on " + this);
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 8aa4845..1458c66 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -119,7 +119,7 @@
private @Nullable ByteBuffer mBuffer;
private @Nullable File mFile;
- private @NonNull LocaleList mLocaleList = LocaleList.getEmptyLocaleList();
+ private @NonNull String mLocaleList = "";
private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED;
private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED;
private @IntRange(from = 0) int mTtcIndex = 0;
@@ -150,7 +150,7 @@
* @hide
*/
public Builder(@NonNull ByteBuffer buffer, @NonNull File path,
- @NonNull LocaleList localeList) {
+ @NonNull String localeList) {
this(buffer);
mFile = path;
mLocaleList = localeList;
@@ -457,7 +457,7 @@
private final boolean mItalic;
private final @IntRange(from = 0) int mTtcIndex;
private final @Nullable FontVariationAxis[] mAxes;
- private final @NonNull LocaleList mLocaleList;
+ private final @NonNull String mLocaleList;
/**
* Use Builder instead
@@ -465,7 +465,7 @@
private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
@IntRange(from = 0, to = 1000) int weight, boolean italic,
@IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes,
- @NonNull LocaleList localeList) {
+ @NonNull String localeList) {
mBuffer = buffer;
mFile = file;
mWeight = weight;
@@ -546,7 +546,7 @@
* @return a locale list
*/
public @NonNull LocaleList getLocaleList() {
- return mLocaleList;
+ return LocaleList.forLanguageTags(mLocaleList);
}
/** @hide */
@@ -580,7 +580,7 @@
+ ", italic=" + mItalic
+ ", ttcIndex=" + mTtcIndex
+ ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
- + ", localeList=" + mLocaleList.toLanguageTags()
+ + ", localeList=" + mLocaleList
+ ", buffer=" + mBuffer
+ "}";
}
diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java
index 242cbb8..bcee559 100644
--- a/graphics/java/android/graphics/fonts/FontVariationAxis.java
+++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -29,7 +30,7 @@
* Class that holds information about single font variation axis.
*/
public final class FontVariationAxis {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mTag;
private final String mTagString;
@UnsupportedAppUsage
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 1c957b8..f4a2199 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
-import android.os.LocaleList;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -54,7 +53,7 @@
private static final Map<String, FontFamily[]> sSystemFallbackMap;
private static final FontConfig.Alias[] sAliases;
- private static final Set<Font> sAvailableFonts;
+ private static final List<Font> sAvailableFonts;
/**
* Returns all available font files in the system.
@@ -63,7 +62,9 @@
* @return an array of system fonts
*/
public static @NonNull Set<Font> getAvailableFonts() {
- return sAvailableFonts;
+ HashSet<Font> set = new HashSet<>();
+ set.addAll(sAvailableFonts);
+ return set;
}
/**
@@ -114,7 +115,7 @@
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
@NonNull Map<String, ByteBuffer> cache,
@NonNull String fontDir,
- @NonNull HashSet<Font> availableFonts) {
+ @NonNull ArrayList<Font> availableFonts) {
final String languageTags = xmlFamily.getLanguages();
final int variant = xmlFamily.getVariant();
@@ -170,13 +171,12 @@
@FontConfig.Family.Variant int variant,
@NonNull Map<String, ByteBuffer> cache,
@NonNull String fontDir,
- @NonNull HashSet<Font> availableFonts) {
+ @NonNull ArrayList<Font> availableFonts) {
if (fonts.size() == 0) {
return null;
}
FontFamily.Builder b = null;
- final LocaleList localeList = LocaleList.forLanguageTags(languageTags);
for (int i = 0; i < fonts.size(); i++) {
final FontConfig.Font fontConfig = fonts.get(i);
final String fullPath = fontDir + fontConfig.getFontName();
@@ -194,7 +194,7 @@
final Font font;
try {
- font = new Font.Builder(buffer, new File(fullPath), localeList)
+ font = new Font.Builder(buffer, new File(fullPath), languageTags)
.setWeight(fontConfig.getWeight())
.setItalic(fontConfig.isItalic())
.setTtcIndex(fontConfig.getTtcIndex())
@@ -228,7 +228,7 @@
public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath,
@NonNull String fontDir,
@NonNull ArrayMap<String, FontFamily[]> fallbackMap,
- @NonNull HashSet<Font> availableFonts) {
+ @NonNull ArrayList<Font> availableFonts) {
try {
final FileInputStream fontsIn = new FileInputStream(xmlPath);
final FontConfig fontConfig = FontListParser.parse(fontsIn);
@@ -284,11 +284,11 @@
static {
final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
- final HashSet<Font> availableFonts = new HashSet<>();
+ final ArrayList<Font> availableFonts = new ArrayList<>();
sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/",
systemFallbackMap, availableFonts);
sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
- sAvailableFonts = Collections.unmodifiableSet(availableFonts);
+ sAvailableFonts = Collections.unmodifiableList(availableFonts);
}
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 04bbe24..11dad2e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -175,6 +175,7 @@
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/SkiaVulkanPipeline.cpp",
"pipeline/skia/VectorDrawableAtlas.cpp",
+ "pipeline/skia/VkFunctorDrawable.cpp",
"renderstate/RenderState.cpp",
"renderthread/CacheManager.cpp",
"renderthread/CanvasContext.cpp",
@@ -247,7 +248,7 @@
// Enables fine-grained GLES error checking
// If enabled, every GLES call is wrapped & error checked
// Has moderate overhead
- "hwui_enable_opengl_validation",
+ //"hwui_enable_opengl_validation",
],
}
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index adcdc18..a03b317 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -28,6 +28,7 @@
#include <cmath>
#include <log/log.h>
+#include <SkHighContrastFilter.h>
namespace android::uirenderer {
@@ -113,4 +114,20 @@
return true;
}
+bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) {
+ bool shouldInvert = false;
+ if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) {
+ shouldInvert = true;
+ }
+ if (palette == BitmapPalette::Dark && transform == ColorTransform::Light) {
+ shouldInvert = true;
+ }
+ if (shouldInvert) {
+ SkHighContrastConfig config;
+ config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+ paint->setColorFilter(SkHighContrastFilter::Make(config)->makeComposed(paint->refColorFilter()));
+ }
+ return shouldInvert;
+}
+
}; // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
index 32d9a05..e723d64 100644
--- a/libs/hwui/CanvasTransform.h
+++ b/libs/hwui/CanvasTransform.h
@@ -16,6 +16,8 @@
#pragma once
+#include "hwui/Bitmap.h"
+
#include <SkCanvas.h>
#include <SkPaintFilterCanvas.h>
@@ -26,6 +28,7 @@
enum class UsageHint {
Unknown = 0,
Background = 1,
+ Foreground = 2,
};
enum class ColorTransform {
@@ -37,4 +40,6 @@
// True if the paint was modified, false otherwise
bool transformPaint(ColorTransform transform, SkPaint* paint);
+bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette);
+
} // namespace android::uirenderer;
\ No newline at end of file
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 837d546..b772e5b 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -29,12 +29,16 @@
, mGLContextAttached(false)
, mUpdateTexImage(false)
, mLayer(nullptr) {
- renderState.registerDeferredLayerUpdater(this);
+ renderState.registerContextCallback(this);
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
setTransform(nullptr);
- mRenderState.unregisterDeferredLayerUpdater(this);
+ mRenderState.removeContextCallback(this);
+ destroyLayer();
+}
+
+void DeferredLayerUpdater::onContextDestroyed() {
destroyLayer();
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 4c323b8..b2c5131 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -27,6 +27,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include "renderstate/RenderState.h"
#include "surfacetexture/SurfaceTexture.h"
#include "Layer.h"
#include "Rect.h"
@@ -38,7 +39,7 @@
// Container to hold the properties a layer should be set to at the start
// of a render pass
-class DeferredLayerUpdater : public VirtualLightRefBase {
+class DeferredLayerUpdater : public VirtualLightRefBase, public IGpuContextCallback {
public:
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
@@ -98,6 +99,9 @@
void destroyLayer();
+protected:
+ void onContextDestroyed() override;
+
private:
RenderState& mRenderState;
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index a43f58c..21fbbdc 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -26,8 +26,6 @@
#include <log/log.h>
-#include <GLES2/gl2.h>
-
namespace android {
namespace uirenderer {
@@ -46,35 +44,12 @@
1920, // viewportH
};
-static DeviceInfo* sDeviceInfo = nullptr;
-static std::once_flag sInitializedFlag;
-
const DeviceInfo* DeviceInfo::get() {
- LOG_ALWAYS_FATAL_IF(!sDeviceInfo, "DeviceInfo not yet initialized.");
- return sDeviceInfo;
+ static DeviceInfo sDeviceInfo;
+ return &sDeviceInfo;
}
-void DeviceInfo::initialize() {
- std::call_once(sInitializedFlag, []() {
- sDeviceInfo = new DeviceInfo();
- sDeviceInfo->load();
- });
-}
-
-void DeviceInfo::initialize(int maxTextureSize) {
- std::call_once(sInitializedFlag, [maxTextureSize]() {
- sDeviceInfo = new DeviceInfo();
- sDeviceInfo->mDisplayInfo = DeviceInfo::queryDisplayInfo();
- sDeviceInfo->mMaxTextureSize = maxTextureSize;
- });
-}
-
-void DeviceInfo::load() {
- mDisplayInfo = queryDisplayInfo();
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-}
-
-DisplayInfo DeviceInfo::queryDisplayInfo() {
+DisplayInfo QueryDisplayInfo() {
if (Properties::isolatedProcess) {
return sDummyDisplay;
}
@@ -86,5 +61,36 @@
return displayInfo;
}
+void QueryCompositionPreference(ui::Dataspace* dataSpace,
+ ui::PixelFormat* pixelFormat) {
+ if (Properties::isolatedProcess) {
+ *dataSpace = ui::Dataspace::V0_SRGB;
+ *pixelFormat = ui::PixelFormat::RGBA_8888;
+ }
+
+ status_t status =
+ SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat);
+ LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status);
+}
+
+DeviceInfo::DeviceInfo() {
+#if HWUI_NULL_GPU
+ mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE;
+#else
+ mMaxTextureSize = -1;
+#endif
+ mDisplayInfo = QueryDisplayInfo();
+ QueryCompositionPreference(&mTargetDataSpace, &mTargetPixelFormat);
+}
+
+int DeviceInfo::maxTextureSize() const {
+ LOG_ALWAYS_FATAL_IF(mMaxTextureSize < 0, "MaxTextureSize has not been initialized yet.");
+ return mMaxTextureSize;
+}
+
+void DeviceInfo::setMaxTextureSize(int maxTextureSize) {
+ const_cast<DeviceInfo*>(DeviceInfo::get())->mMaxTextureSize = maxTextureSize;
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 297b266..1d747741 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -17,47 +17,43 @@
#define DEVICEINFO_H
#include <ui/DisplayInfo.h>
+#include <ui/GraphicTypes.h>
-#include "Extensions.h"
#include "utils/Macros.h"
namespace android {
namespace uirenderer {
+namespace renderthread {
+ class RenderThread;
+}
+
class DeviceInfo {
PREVENT_COPY_AND_ASSIGN(DeviceInfo);
public:
- // returns nullptr if DeviceInfo is not initialized yet
- // Note this does not have a memory fence so it's up to the caller
- // to use one if required. Normally this should not be necessary
static const DeviceInfo* get();
- // only call this after GL has been initialized, or at any point if compiled
- // with HWUI_NULL_GPU
- static void initialize();
- static void initialize(int maxTextureSize);
+ // this value is only valid after the GPU has been initialized and there is a valid graphics
+ // context or if you are using the HWUI_NULL_GPU
+ int maxTextureSize() const;
- int maxTextureSize() const { return mMaxTextureSize; }
+ ui::Dataspace getTargetDataSpace() const { return mTargetDataSpace; }
+ ui::PixelFormat getTargetPixelFormat() const { return mTargetPixelFormat; }
const DisplayInfo& displayInfo() const { return mDisplayInfo; }
- const Extensions& extensions() const { return mExtensions; }
-
- static uint32_t multiplyByResolution(uint32_t in) {
- auto di = DeviceInfo::get()->displayInfo();
- return di.w * di.h * in;
- }
-
- static DisplayInfo queryDisplayInfo();
private:
- DeviceInfo() {}
- ~DeviceInfo() {}
+ friend class renderthread::RenderThread;
+ static void setMaxTextureSize(int maxTextureSize);
- void load();
+ DeviceInfo();
int mMaxTextureSize;
DisplayInfo mDisplayInfo;
- Extensions mExtensions;
+
+ // TODO(lpy) Replace below with android_ prefix types.
+ ui::Dataspace mTargetDataSpace;
+ ui::PixelFormat mTargetPixelFormat;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
deleted file mode 100644
index e90f40c1..0000000
--- a/libs/hwui/Extensions.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_EXTENSIONS_H
-#define ANDROID_HWUI_EXTENSIONS_H
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-class Extensions {
-public:
- Extensions() {}
-
- inline bool hasNPot() const { return false; }
- inline bool hasFramebufferFetch() const { return false; }
- inline bool hasDiscardFramebuffer() const { return false; }
- inline bool hasDebugMarker() const { return false; }
- inline bool has1BitStencil() const { return false; }
- inline bool has4BitStencil() const { return false; }
- inline bool hasUnpackRowLength() const { return mVersionMajor >= 3; }
- inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
- inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
- inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
- inline bool hasRenderableFloatTextures() const {
- return (mVersionMajor >= 3 && mVersionMinor >= 2);
- }
- inline bool hasSRGB() const { return false; }
- inline bool hasSRGBWriteControl() const { return hasSRGB() && false; }
- inline bool hasLinearBlending() const { return hasSRGB() && false; }
-
- inline int getMajorGlVersion() const { return mVersionMajor; }
- inline int getMinorGlVersion() const { return mVersionMinor; }
-
-private:
- int mVersionMajor = 2;
- int mVersionMinor = 0;
-}; // class Extensions
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_EXTENSIONS_H
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index ab80d3d..165fc48 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -45,7 +45,7 @@
}
static void checkIdleTimeout() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
if (sPendingUploads == 0 && shouldTimeOutLocked()) {
sEglManager.destroy();
} else {
@@ -54,7 +54,7 @@
}
static void beginUpload() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
sPendingUploads++;
if (!sUploadThread) {
@@ -75,13 +75,13 @@
}
static void endUpload() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
sPendingUploads--;
sLastUpload = systemTime();
}
static EGLDisplay getUploadEglDisplay() {
- std::lock_guard{sLock};
+ std::lock_guard _lock{sLock};
LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
return sEglManager.eglDisplay();
}
@@ -107,39 +107,6 @@
#define FENCE_TIMEOUT 2000000000
-class AutoEglImage {
-public:
- AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
- EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
- image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
- imageAttrs);
- }
-
- ~AutoEglImage() {
- if (image != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(mDisplay, image);
- }
- }
-
- EGLImageKHR image = EGL_NO_IMAGE_KHR;
-
-private:
- EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
-class AutoSkiaGlTexture {
-public:
- AutoSkiaGlTexture() {
- glGenTextures(1, &mTexture);
- glBindTexture(GL_TEXTURE_2D, mTexture);
- }
-
- ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
-
-private:
- GLuint mTexture = 0;
-};
-
struct FormatInfo {
PixelFormat pixelFormat;
GLint format, type;
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e6d2a6f..f2d50cd 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -146,7 +146,7 @@
frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
// If we hit the deadline, cool!
- if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+ if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline || totalDuration < mFrameInterval) {
if (isTripleBuffered) {
mData->reportJankType(JankType::kHighInputLatency);
(*mGlobalData)->reportJankType(JankType::kHighInputLatency);
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index b14b80c..6857999 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -19,6 +19,7 @@
#include <utils/StrongPointer.h>
#include "Rect.h"
+#include "RenderNode.h"
#include "utils/Macros.h"
#include <unordered_map>
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 3eaff03..5f54c02 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -278,8 +278,8 @@
struct DrawImage final : Op {
static const auto kType = Type::DrawImage;
- DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint)
- : image(std::move(image)), x(x), y(y) {
+ DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette)
+ : image(std::move(image)), x(x), y(y), palette(palette) {
if (paint) {
this->paint = *paint;
}
@@ -287,6 +287,7 @@
sk_sp<const SkImage> image;
SkScalar x, y;
SkPaint paint;
+ BitmapPalette palette;
void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x, y, &paint); }
};
struct DrawImageNine final : Op {
@@ -309,8 +310,8 @@
struct DrawImageRect final : Op {
static const auto kType = Type::DrawImageRect;
DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SkCanvas::SrcRectConstraint constraint)
- : image(std::move(image)), dst(dst), constraint(constraint) {
+ const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, BitmapPalette palette)
+ : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) {
this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height());
if (paint) {
this->paint = *paint;
@@ -320,6 +321,7 @@
SkRect src, dst;
SkPaint paint;
SkCanvas::SrcRectConstraint constraint;
+ BitmapPalette palette;
void draw(SkCanvas* c, const SkMatrix&) const {
c->drawImageRect(image.get(), src, dst, &paint, constraint);
}
@@ -609,8 +611,8 @@
this->push<DrawPicture>(0, picture, matrix, paint);
}
void DisplayListData::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y,
- const SkPaint* paint) {
- this->push<DrawImage>(0, std::move(image), x, y, paint);
+ const SkPaint* paint, BitmapPalette palette) {
+ this->push<DrawImage>(0, std::move(image), x, y, paint, palette);
}
void DisplayListData::drawImageNine(sk_sp<const SkImage> image, const SkIRect& center,
const SkRect& dst, const SkPaint* paint) {
@@ -618,8 +620,8 @@
}
void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* src,
const SkRect& dst, const SkPaint* paint,
- SkCanvas::SrcRectConstraint constraint) {
- this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint);
+ SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) {
+ this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette);
}
void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
@@ -638,28 +640,33 @@
const SkPaint& paint) {
void* pod = this->push<DrawText>(bytes, bytes, x, y, paint);
copy_v(pod, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawPosText(const void* text, size_t bytes, const SkPoint pos[],
const SkPaint& paint) {
int n = paint.countText(text, bytes);
void* pod = this->push<DrawPosText>(n * sizeof(SkPoint) + bytes, bytes, paint, n);
copy_v(pod, pos, n, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawPosTextH(const void* text, size_t bytes, const SkScalar xs[], SkScalar y,
const SkPaint& paint) {
int n = paint.countText(text, bytes);
void* pod = this->push<DrawPosTextH>(n * sizeof(SkScalar) + bytes, bytes, y, paint, n);
copy_v(pod, xs, n, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawTextRSXform(const void* text, size_t bytes, const SkRSXform xforms[],
const SkRect* cull, const SkPaint& paint) {
int n = paint.countText(text, bytes);
void* pod = this->push<DrawTextRSXform>(bytes + n * sizeof(SkRSXform), bytes, n, cull, paint);
copy_v(pod, xforms, n, (const char*)text, bytes);
+ mHasText = true;
}
void DisplayListData::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
this->push<DrawTextBlob>(0, blob, x, y, paint);
+ mHasText = true;
}
void DisplayListData::drawPatch(const SkPoint points[12], const SkColor colors[4],
@@ -733,20 +740,35 @@
}
template <class T>
-using has_paint_t = decltype(std::declval<T>().paint);
+using has_paint_helper = decltype(std::declval<T>().paint);
+
+template <class T>
+constexpr bool has_paint = std::experimental::is_detected_v<has_paint_helper, T>;
+
+template <class T>
+using has_palette_helper = decltype(std::declval<T>().palette);
+
+template <class T>
+constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper, T>;
template <class T>
constexpr color_transform_fn colorTransformForOp() {
- if
- constexpr(std::experimental::is_detected_v<has_paint_t, T>) {
- return [](const void* op, ColorTransform transform) {
- // TODO: We should be const. Or not. Or just use a different map
- // Unclear, but this is the quick fix
- transformPaint(transform,
- const_cast<SkPaint*>(&(reinterpret_cast<const T*>(op)->paint)));
- };
- }
- else {
+ if constexpr(has_paint<T> && has_palette<T>) {
+ // It's a bitmap
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+ };
+ } else if constexpr(has_paint<T>) {
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ };
+ } else {
return nullptr;
}
}
@@ -875,7 +897,7 @@
void RecordingCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y,
const SkPaint* paint) {
- fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint);
+ fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, const SkRect& dst,
const SkPaint* paint) {
@@ -883,16 +905,26 @@
}
void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint) {
- fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint);
+ fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint);
}
+void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
+ const SkPaint* paint, BitmapPalette palette) {
+ fDL->drawImage(image, x, y, paint, palette);
+}
+
+void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette) {
+ fDL->drawImageRect(image, &src, dst, paint, constraint, palette);
+}
+
void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
const SkPaint* paint) {
- fDL->drawImage(sk_ref_sp(img), x, y, paint);
+ fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawImageNine(const SkImage* img, const SkIRect& center, const SkRect& dst,
const SkPaint* paint) {
@@ -900,7 +932,7 @@
}
void RecordingCanvas::onDrawImageRect(const SkImage* img, const SkRect* src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint) {
- fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint);
+ fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint, BitmapPalette::Unknown);
}
void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
const SkRect& dst, const SkPaint* paint) {
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 32ce1d3..eecf51c 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -17,6 +17,7 @@
#pragma once
#include "CanvasTransform.h"
+#include "hwui/Bitmap.h"
#include "hwui/Canvas.h"
#include "utils/Macros.h"
#include "utils/TypeLogic.h"
@@ -53,6 +54,7 @@
class DisplayListData final {
public:
+ DisplayListData() : mHasText(false) {}
~DisplayListData();
void draw(SkCanvas* canvas) const;
@@ -62,6 +64,8 @@
void applyColorTransform(ColorTransform transform);
+ bool hasText() const { return mHasText; }
+
private:
friend class RecordingCanvas;
@@ -101,10 +105,10 @@
void drawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*, const SkPaint&);
void drawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&);
- void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*);
+ void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*, BitmapPalette palette);
void drawImageNine(sk_sp<const SkImage>, const SkIRect&, const SkRect&, const SkPaint*);
void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
- SkCanvas::SrcRectConstraint);
+ SkCanvas::SrcRectConstraint, BitmapPalette palette);
void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
const SkPaint*);
@@ -126,6 +130,8 @@
SkAutoTMalloc<uint8_t> fBytes;
size_t fUsed = 0;
size_t fReserved = 0;
+
+ bool mHasText : 1;
};
class RecordingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {
@@ -178,6 +184,12 @@
void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
SrcRectConstraint) override;
+ void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top,
+ const SkPaint* paint, BitmapPalette pallete);
+
+ void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette);
+
void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d5afb20..06dbb24 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -158,7 +158,7 @@
CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
CC_UNLIKELY(!properties().fitsOnLayer())) {
if (CC_UNLIKELY(hasLayer())) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
return;
}
@@ -272,8 +272,12 @@
mStagingDisplayList = nullptr;
if (mDisplayList) {
mDisplayList->syncContents();
+
if (CC_UNLIKELY(Properties::forceDarkMode)) {
auto usage = usageHint();
+ if (mDisplayList->hasText()) {
+ usage = UsageHint::Foreground;
+ }
if (usage == UsageHint::Unknown) {
if (mDisplayList->mChildNodes.size() > 1) {
usage = UsageHint::Background;
@@ -313,7 +317,7 @@
void RenderNode::destroyHardwareResources(TreeInfo* info) {
if (hasLayer()) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
setStagingDisplayList(nullptr);
@@ -323,7 +327,7 @@
void RenderNode::destroyLayers() {
if (hasLayer()) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
if (mDisplayList) {
mDisplayList->updateChildren([](RenderNode* child) { child->destroyLayers(); });
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 83b0c22..211dd2d 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -107,7 +107,7 @@
bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); }
bool hasProjectionReceiver() const {
- return mDisplayList && mDisplayList->projectionReceiveIndex >= 0;
+ return mDisplayList && mDisplayList->containsProjectionReceiver();
}
const char* getName() const { return mName.string(); }
diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h
index 27a29aa..3da6e80 100644
--- a/libs/hwui/debug/wrap_gles.h
+++ b/libs/hwui/debug/wrap_gles.h
@@ -28,6 +28,9 @@
#include <GLES3/gl31.h>
#include <GLES3/gl32.h>
+// constant used by the NULL GPU implementation as well as HWUI's unit tests
+constexpr int NULL_GPU_MAX_TEXTURE_SIZE = 2048;
+
// Generate stubs that route all the calls to our function table
#include "gles_redefine.h"
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 440620a..9c28453 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -230,8 +230,6 @@
mPixelStorage.hardware.buffer = nullptr;
break;
}
-
- android::uirenderer::renderthread::RenderProxy::onBitmapDestroyed(getStableID());
}
bool Bitmap::hasHardwareMipMap() const {
@@ -330,15 +328,6 @@
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;
}
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index dcfe6b3..e4ba13d 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -138,7 +138,7 @@
renderNodeDrawable->getRenderNode()->output(mOutput, mLevel + 1);
return;
}
- auto glFunctorDrawable = getGLFunctorDrawable(drawable);
+ auto glFunctorDrawable = getFunctorDrawable(drawable);
if (nullptr != glFunctorDrawable) {
mOutput << std::string(mLevel * 2, ' ') << "drawGLFunctorDrawable" << std::endl;
return;
@@ -157,10 +157,10 @@
return nullptr;
}
- GLFunctorDrawable* getGLFunctorDrawable(SkDrawable* drawable) {
+ FunctorDrawable* getFunctorDrawable(SkDrawable* drawable) {
for (auto& child : mDisplayList.mChildFunctors) {
- if (drawable == &child) {
- return &child;
+ if (drawable == child) {
+ return child;
}
}
return nullptr;
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
new file mode 100644
index 0000000..162d137
--- /dev/null
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -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.
+ */
+
+#pragma once
+
+#include "GlFunctorLifecycleListener.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class FunctorDrawable : public SkDrawable {
+public:
+ FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+ : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ virtual ~FunctorDrawable() {}
+
+ virtual void syncFunctor() const = 0;
+
+protected:
+ virtual SkRect onGetBounds() override { return mBounds; }
+
+ Functor* mFunctor;
+ sp<GlFunctorLifecycleListener> mListener;
+ const SkRect mBounds;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index b0fec7a..90d5e71 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -18,7 +18,6 @@
#include <GrContext.h>
#include <private/hwui/DrawGlInfo.h>
#include "GlFunctorLifecycleListener.h"
-#include "Properties.h"
#include "RenderNode.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkClipStack.h"
@@ -80,11 +79,6 @@
return;
}
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- canvas->clear(SK_ColorRED);
- return;
- }
-
GLuint fboID = 0;
SkISize fboSize;
if (!GetFboDetails(canvas, &fboID, &fboSize)) {
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index d9e65c9..dd6ef25 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -16,12 +16,8 @@
#pragma once
-#include "GlFunctorLifecycleListener.h"
+#include "FunctorDrawable.h"
-#include <SkCanvas.h>
-#include <SkDrawable.h>
-
-#include <utils/Functor.h>
#include <utils/RefBase.h>
namespace android {
@@ -33,22 +29,16 @@
* This drawable wraps a OpenGL functor enabling it to be recorded into a list
* of Skia drawing commands.
*/
-class GLFunctorDrawable : public SkDrawable {
+class GLFunctorDrawable : public FunctorDrawable {
public:
GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ : FunctorDrawable(functor, listener, canvas) {}
virtual ~GLFunctorDrawable();
- void syncFunctor() const;
+ void syncFunctor() const override;
protected:
- virtual SkRect onGetBounds() override { return mBounds; }
virtual void onDraw(SkCanvas* canvas) override;
-
-private:
- Functor* mFunctor;
- sp<GlFunctorLifecycleListener> mListener;
- const SkRect mBounds;
};
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 82179a3..3890513 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -29,7 +29,7 @@
void SkiaDisplayList::syncContents() {
for (auto& functor : mChildFunctors) {
- functor.syncFunctor();
+ functor->syncFunctor();
}
for (auto& animatedImage : mAnimatedImages) {
animatedImage->syncProperties();
@@ -132,7 +132,6 @@
mChildFunctors.clear();
mChildNodes.clear();
- projectionReceiveIndex = -1;
allocator.~LinearAllocator();
new (&allocator) LinearAllocator();
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 4f30f98..ac7bb7b 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -17,7 +17,7 @@
#pragma once
#include "hwui/AnimatedImageDrawable.h"
-#include "GLFunctorDrawable.h"
+#include "FunctorDrawable.h"
#include "RecordingCanvas.h"
#include "RenderNodeDrawable.h"
#include "TreeInfo.h"
@@ -49,9 +49,6 @@
*/
class SkiaDisplayList {
public:
- // index of DisplayListOp restore, after which projected descendants should be drawn
- int projectionReceiveIndex = -1;
-
size_t getUsedSize() { return allocator.usedSize(); }
~SkiaDisplayList() {
@@ -77,7 +74,7 @@
* that creates them. Allocator dtor invokes all SkDrawable dtors.
*/
template <class T, typename... Params>
- SkDrawable* allocateDrawable(Params&&... params) {
+ T* allocateDrawable(Params&&... params) {
return allocator.create<T>(std::forward<Params>(params)...);
}
@@ -96,6 +93,8 @@
*/
bool hasVectorDrawables() const { return !mVectorDrawables.empty(); }
+ bool hasText() const { return mDisplayList.hasText(); }
+
/**
* Attempts to reset and reuse this DisplayList.
*
@@ -155,7 +154,7 @@
* cannot relocate.
*/
std::deque<RenderNodeDrawable> mChildNodes;
- std::deque<GLFunctorDrawable> mChildFunctors;
+ std::deque<FunctorDrawable*> mChildFunctors;
std::vector<SkImage*> mMutableImages;
std::vector<VectorDrawableRoot*> mVectorDrawables;
std::vector<AnimatedImageDrawable*> mAnimatedImages;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index d58b59e..e8bf492 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -21,12 +21,15 @@
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
#include "hwui/Bitmap.h"
+#include "private/hwui/DrawGlInfo.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "renderthread/Frame.h"
#include "utils/GLUtils.h"
#include "utils/TraceUtils.h"
+#include <GLES3/gl3.h>
+
#include <GrBackendSurface.h>
#include <SkBlendMode.h>
#include <SkImageInfo.h>
@@ -41,7 +44,13 @@
namespace skiapipeline {
SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
- : SkiaPipeline(thread), mEglManager(thread.eglManager()) {}
+ : SkiaPipeline(thread), mEglManager(thread.eglManager()) {
+ thread.renderState().registerContextCallback(this);
+}
+
+SkiaOpenGLPipeline::~SkiaOpenGLPipeline() {
+ mRenderThread.renderState().removeContextCallback(this);
+}
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
// TODO: Figure out why this workaround is needed, see b/13913604
@@ -62,21 +71,23 @@
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, bool wideColorGamut, const LightInfo& lightInfo,
+ bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
mEglManager.damageFrame(frame, dirty);
- SkColorType colorType;
+ SkColorType colorType = getSurfaceColorType();
// setup surface for fbo0
GrGLFramebufferInfo fboInfo;
fboInfo.fFBOID = 0;
- if (wideColorGamut) {
+ if (colorType == kRGBA_F16_SkColorType) {
fboInfo.fFormat = GL_RGBA16F;
- colorType = kRGBA_F16_SkColorType;
- } else {
+ } else if (colorType == kN32_SkColorType) {
+ // Note: The default preference of pixel format is RGBA_8888, when other
+ // pixel format is available, we should branch out and do more check.
fboInfo.fFormat = GL_RGBA8;
- colorType = kN32_SkColorType;
+ } else {
+ LOG_ALWAYS_FATAL("Unsupported color type.");
}
GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo);
@@ -89,8 +100,7 @@
nullptr, &props));
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds,
- surface);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
layerUpdateQueue->clear();
// Draw visual debugging features
@@ -132,6 +142,13 @@
return new DeferredLayerUpdater(mRenderThread.renderState());
}
+void SkiaOpenGLPipeline::onContextDestroyed() {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+}
+
void SkiaOpenGLPipeline::onStop() {
if (mEglManager.isCurrent(mEglSurface)) {
mEglManager.makeCurrent(EGL_NO_SURFACE);
@@ -147,8 +164,7 @@
if (surface) {
mRenderThread.requireGlContext();
- const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
- mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
+ mEglSurface = mEglManager.createSurface(surface, colorMode);
}
if (mEglSurface != EGL_NO_SURFACE) {
@@ -168,6 +184,14 @@
return CC_LIKELY(mEglManager.hasEglContext());
}
+SkColorType SkiaOpenGLPipeline::getSurfaceColorType() const {
+ return mEglManager.getSurfaceColorType();
+}
+
+sk_sp<SkColorSpace> SkiaOpenGLPipeline::getSurfaceColorSpace() {
+ return mEglManager.getSurfaceColorSpace();
+}
+
void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
if (thread.eglManager().hasEglContext()) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 808685a..086a760 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -18,6 +18,8 @@
#include "SkiaPipeline.h"
+#include "renderstate/RenderState.h"
+
namespace android {
class Bitmap;
@@ -25,17 +27,16 @@
namespace uirenderer {
namespace skiapipeline {
-class SkiaOpenGLPipeline : public SkiaPipeline {
+class SkiaOpenGLPipeline : public SkiaPipeline, public IGpuContextCallback {
public:
SkiaOpenGLPipeline(renderthread::RenderThread& thread);
- virtual ~SkiaOpenGLPipeline() {}
+ virtual ~SkiaOpenGLPipeline();
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const LightInfo& lightInfo,
+ const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
@@ -46,9 +47,14 @@
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
+ SkColorType getSurfaceColorType() const override;
+ sk_sp<SkColorSpace> getSurfaceColorSpace() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+protected:
+ void onContextDestroyed() override;
+
private:
renderthread::EglManager& mEglManager;
EGLSurface mEglSurface = EGL_NO_SURFACE;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 988981d..2dfe7c7 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -83,16 +83,15 @@
void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, bool opaque,
- bool wideColorGamut, const LightInfo& lightInfo) {
+ const LightInfo& lightInfo) {
updateLighting(lightGeometry, lightInfo);
ATRACE_NAME("draw layers");
renderVectorDrawableCache();
- renderLayersImpl(*layerUpdateQueue, opaque, wideColorGamut);
+ renderLayersImpl(*layerUpdateQueue, opaque);
layerUpdateQueue->clear();
}
-void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
- bool wideColorGamut) {
+void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
sk_sp<GrContext> cachedContext;
// Render all layers that need to be updated, in order.
@@ -161,7 +160,7 @@
}
bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
- bool wideColorGamut, ErrorHandler* errorHandler) {
+ ErrorHandler* errorHandler) {
// compute the size of the surface (i.e. texture) to be allocated for this layer
const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
@@ -169,12 +168,8 @@
SkSurface* layer = node->getLayerSurface();
if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
SkImageInfo info;
- if (wideColorGamut) {
- info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType,
- kPremul_SkAlphaType);
- } else {
- info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight);
- }
+ info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
+ kPremul_SkAlphaType);
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
@@ -205,10 +200,6 @@
return false;
}
-void SkiaPipeline::destroyLayer(RenderNode* node) {
- node->setLayerSurface(nullptr);
-}
-
void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
GrContext* context = thread.getGrContext();
if (context) {
@@ -321,19 +312,18 @@
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
- bool wideColorGamut, const Rect& contentDrawBounds,
- sk_sp<SkSurface> surface) {
+ const Rect& contentDrawBounds, sk_sp<SkSurface> surface) {
renderVectorDrawableCache();
// draw all layers up front
- renderLayersImpl(layers, opaque, wideColorGamut);
+ renderLayersImpl(layers, opaque);
// initialize the canvas for the current frame, that might be a recording canvas if SKP
// capture is enabled.
std::unique_ptr<SkPictureRecorder> recorder;
SkCanvas* canvas = tryCapture(surface.get());
- renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
+ renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
endCapture(surface.get());
@@ -354,13 +344,12 @@
void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
- bool wideColorGamut, const Rect& contentDrawBounds,
- SkCanvas* canvas) {
+ const Rect& contentDrawBounds, SkCanvas* canvas) {
SkAutoCanvasRestore saver(canvas, true);
canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
// STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
- if (!opaque || wideColorGamut) {
+ if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) {
canvas->clear(SK_ColorTRANSPARENT);
}
@@ -493,7 +482,7 @@
// each time a pixel would have been drawn.
// Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
// initialized.
- renderFrameImpl(layers, clip, nodes, true, false, contentDrawBounds, &overdrawCanvas);
+ renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas);
sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
// Draw overdraw colors to the canvas. The color filter will convert counts to colors.
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 8c9c803..ee9158c 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -42,24 +42,21 @@
void unpinImages() override;
void onPrepareTree() override;
- void renderLayers(const LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
- const LightInfo& lightInfo) override;
+ void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ bool opaque, const LightInfo& lightInfo) override;
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
- bool wideColorGamut, ErrorHandler* errorHandler) override;
+ ErrorHandler* errorHandler) override;
void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, sk_sp<SkSurface> surface);
std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
- static void destroyLayer(RenderNode* node);
-
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
- void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
+ void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
static float getLightRadius() {
if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
@@ -112,7 +109,7 @@
private:
void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, SkCanvas* canvas);
/**
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 83d7e6a..3042006e 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -23,6 +23,8 @@
#include "NinePatchUtils.h"
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
+#include "pipeline/skia/VkFunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -111,16 +113,21 @@
// use staging property, since recording on UI thread
if (renderNode->stagingProperties().isProjectionReceiver()) {
mDisplayList->mProjectionReceiver = &renderNodeDrawable;
- // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
- mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
}
}
void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) {
- // Drawable dtor will be invoked when mChildFunctors deque is cleared.
- mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
- drawDrawable(&mDisplayList->mChildFunctors.back());
+ FunctorDrawable* functorDrawable;
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, listener,
+ asSkCanvas());
+ } else {
+ functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
+ asSkCanvas());
+ }
+ mDisplayList->mChildFunctors.push_back(functorDrawable);
+ drawDrawable(functorDrawable);
}
class VectorDrawable : public SkDrawable {
@@ -187,7 +194,7 @@
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
+ mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
// if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
// it is not safe to store a raw SkImage pointer, because the image object will be destroyed
// when this function ends.
@@ -202,7 +209,7 @@
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
+ mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
}
@@ -217,7 +224,7 @@
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
- SkCanvas::kFast_SrcRectConstraint);
+ SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
!dstRect.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 611a34c..e34f160 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -22,6 +22,7 @@
#include "SkiaProfileRenderer.h"
#include "renderstate/RenderState.h"
#include "renderthread/Frame.h"
+#include "VkFunctorDrawable.h"
#include <SkSurface.h>
#include <SkTypes.h>
@@ -63,8 +64,7 @@
bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, bool wideColorGamut,
- const LightInfo& lightInfo,
+ bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface();
@@ -72,8 +72,7 @@
return false;
}
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds,
- backBuffer);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
layerUpdateQueue->clear();
// Draw visual debugging features
@@ -139,10 +138,16 @@
return CC_LIKELY(mVkManager.hasVkContext());
}
+SkColorType SkiaVulkanPipeline::getSurfaceColorType() const {
+ return mVkManager.getSurfaceColorType();
+}
+
+sk_sp<SkColorSpace> SkiaVulkanPipeline::getSurfaceColorSpace() {
+ return mVkManager.getSurfaceColorSpace();
+}
+
void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
- // TODO: we currently don't support OpenGL WebView's
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- (*functor)(mode, nullptr);
+ VkFunctorDrawable::vkInvokeFunctor(functor);
}
sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 900b054..6e723a8 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -32,8 +32,7 @@
renderthread::Frame getFrame() override;
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const LightInfo& lightInfo,
+ const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
@@ -44,6 +43,8 @@
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
+ SkColorType getSurfaceColorType() const override;
+ sk_sp<SkColorSpace> getSurfaceColorSpace() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
new file mode 100644
index 0000000..6486ddb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VkFunctorDrawable.h"
+#include <private/hwui/DrawGlInfo.h>
+
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <utils/GLUtils.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static std::mutex sLock{};
+static ThreadBase* sGLDrawThread = nullptr;
+static renderthread::EglManager sEglManager;
+
+// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
+class ScopedDrawRequest {
+public:
+ ScopedDrawRequest() { beginDraw(); }
+private:
+ void beginDraw() {
+ std::lock_guard{sLock};
+
+ if (!sGLDrawThread) {
+ sGLDrawThread = new ThreadBase{};
+ }
+
+ if (!sGLDrawThread->isRunning()) {
+ sGLDrawThread->start("GLFunctorThread");
+ }
+
+ if (!sEglManager.hasEglContext()) {
+ sGLDrawThread->queue().runSync([]() {
+ sEglManager.initialize();
+ });
+ }
+ }
+};
+
+void VkFunctorDrawable::vkInvokeFunctor(Functor* functor) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ EGLDisplay display = sEglManager.eglDisplay();
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (display != EGL_NO_DISPLAY) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ (*functor)(mode, nullptr);
+ });
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+void VkFunctorDrawable::onDraw(SkCanvas* canvas) {
+ ATRACE_CALL();
+
+ if (canvas->getGrContext() == nullptr) {
+ SkDEBUGF(("Attempting to draw VkFunctor into an unsupported surface"));
+ return;
+ }
+
+ ScopedDrawRequest _drawRequest{};
+
+ SkImageInfo surfaceInfo = canvas->imageInfo();
+
+ if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
+ // Buffer will be used as an OpenGL ES render target.
+ mFrameBuffer = new GraphicBuffer(
+ //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+ static_cast<uint32_t>(surfaceInfo.width()),
+ static_cast<uint32_t>(surfaceInfo.height()),
+ ColorTypeToPixelFormat(surfaceInfo.colorType()),
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+ std::string("VkFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + "]");
+ status_t error = mFrameBuffer->initCheck();
+ if (error < 0) {
+ ALOGW("VkFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
+ return;
+ }
+
+ mFBInfo = surfaceInfo;
+ }
+
+ //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+ //TODO: draw command has completed.
+ //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+ //TODO: GrVkGpu::destroyResources() for example.
+ bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+ ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
+ EGLDisplay display = sEglManager.eglDisplay();
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ // We use an EGLImage to access the content of the GraphicBuffer
+ // The EGL image is later bound to a 2D texture
+ EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
+ AutoEglImage autoImage(display, clientBuffer);
+ if (autoImage.image == EGL_NO_IMAGE_KHR) {
+ ALOGW("Could not create EGL image, err =%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ return false;
+ }
+
+ AutoSkiaGlTexture glTexture;
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+ GL_CHECKPOINT(MODERATE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ DrawGlInfo info;
+ SkMatrix44 mat4(canvas->getTotalMatrix());
+ SkIRect clipBounds = canvas->getDeviceClipBounds();
+
+ info.clipLeft = clipBounds.fLeft;
+ info.clipTop = clipBounds.fTop;
+ info.clipRight = clipBounds.fRight;
+ info.clipBottom = clipBounds.fBottom;
+ info.isLayer = true;
+ info.width = mFBInfo.width();
+ info.height = mFBInfo.height();
+ mat4.asColMajorf(&info.transform[0]);
+
+ glViewport(0, 0, info.width, info.height);
+
+ AutoGLFramebuffer glFb;
+ // Bind texture to the frame buffer.
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ glTexture.mTexture, 0);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Failed framebuffer check for created target buffer: %s",
+ GLUtils::getGLFramebufferError());
+ return false;
+ }
+
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+ EGLSyncKHR glDrawFinishedFence =
+ eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+ LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
+ "Could not create sync fence %#x", eglGetError());
+ glFlush();
+ // TODO: export EGLSyncKHR in file descr
+ // TODO: import file desc in Vulkan Semaphore
+ // TODO: instead block the GPU: probably by using external Vulkan semaphore.
+ // Block the CPU until the glFlush finish.
+ EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
+ FENCE_TIMEOUT);
+ LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+ "Failed to wait for the fence %#x", eglGetError());
+ eglDestroySyncKHR(display, glDrawFinishedFence);
+ return true;
+ });
+
+ if (!success) {
+ return;
+ }
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrcOver);
+ canvas->save();
+ // The size of the image matches the size of the canvas. We've used the matrix already, while
+ // drawing into the offscreen surface, so we need to reset it here.
+ canvas->resetMatrix();
+
+ auto functorImage = SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
+ nullptr, kBottomLeft_GrSurfaceOrigin);
+ canvas->drawImage(functorImage, 0, 0, &paint);
+ canvas->restore();
+}
+
+VkFunctorDrawable::~VkFunctorDrawable() {
+ if (mListener.get() != nullptr) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ mListener->onGlFunctorReleased(mFunctor);
+ });
+ }
+}
+
+void VkFunctorDrawable::syncFunctor() const {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+ });
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
new file mode 100644
index 0000000..e37f6fb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class VkFunctorDrawable : public FunctorDrawable {
+public:
+ VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+ : FunctorDrawable(functor, listener, canvas) {}
+ virtual ~VkFunctorDrawable();
+
+ void syncFunctor() const override;
+
+ static void vkInvokeFunctor(Functor* functor);
+
+protected:
+ virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+
+ // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
+ sp<GraphicBuffer> mFrameBuffer;
+ SkImageInfo mFBInfo;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index b524bcb..fad9440 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -14,91 +14,28 @@
* limitations under the License.
*/
#include "renderstate/RenderState.h"
-#include <GpuMemoryTracker.h>
-#include "DeferredLayerUpdater.h"
-#include "Snapshot.h"
-#include "renderthread/CanvasContext.h"
-#include "renderthread/EglManager.h"
-#include "utils/GLUtils.h"
-
-#include <algorithm>
-
-#include <ui/ColorSpace.h>
+#include "renderthread/RenderThread.h"
+#include "GpuMemoryTracker.h"
namespace android {
namespace uirenderer {
-RenderState::RenderState(renderthread::RenderThread& thread)
- : mRenderThread(thread), mViewportWidth(0), mViewportHeight(0), mFramebuffer(0) {
+RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thread) {
mThreadId = pthread_self();
}
-RenderState::~RenderState() {
-}
-
void RenderState::onContextCreated() {
GpuMemoryTracker::onGpuContextCreated();
}
void RenderState::onContextDestroyed() {
- destroyLayersInUpdater();
+ for(auto callback : mContextCallbacks) {
+ callback->onContextDestroyed();
+ }
GpuMemoryTracker::onGpuContextDestroyed();
}
-GrContext* RenderState::getGrContext() const {
- return mRenderThread.getGrContext();
-}
-
-void RenderState::onBitmapDestroyed(uint32_t pixelRefId) {
- // DEAD CODE
-}
-
-void RenderState::setViewport(GLsizei width, GLsizei height) {
- mViewportWidth = width;
- mViewportHeight = height;
- glViewport(0, 0, mViewportWidth, mViewportHeight);
-}
-
-void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
- *outWidth = mViewportWidth;
- *outHeight = mViewportHeight;
-}
-
-void RenderState::bindFramebuffer(GLuint fbo) {
- if (mFramebuffer != fbo) {
- mFramebuffer = fbo;
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
- }
-}
-
-GLuint RenderState::createFramebuffer() {
- GLuint ret;
- glGenFramebuffers(1, &ret);
- return ret;
-}
-
-void RenderState::deleteFramebuffer(GLuint fbo) {
- if (mFramebuffer == fbo) {
- // GL defines that deleting the currently bound FBO rebinds FBO 0.
- // Reflect this in our cached value.
- mFramebuffer = 0;
- }
- glDeleteFramebuffers(1, &fbo);
-}
-
-void RenderState::debugOverdraw(bool enable, bool clear) {
- // DEAD CODE
-}
-
-static void destroyLayerInUpdater(DeferredLayerUpdater* layerUpdater) {
- layerUpdater->destroyLayer();
-}
-
-void RenderState::destroyLayersInUpdater() {
- std::for_each(mActiveLayerUpdaters.begin(), mActiveLayerUpdaters.end(), destroyLayerInUpdater);
-}
-
void RenderState::postDecStrong(VirtualLightRefBase* object) {
if (pthread_equal(mThreadId, pthread_self())) {
object->decStrong(nullptr);
@@ -111,13 +48,5 @@
// Render
///////////////////////////////////////////////////////////////////////////////
-void RenderState::dump() {
- // DEAD CODE
-}
-
-renderthread::RenderThread& RenderState::getRenderThread() {
- return mRenderThread;
-}
-
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index f39aa4b..ff5d02f 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -18,29 +18,26 @@
#include "utils/Macros.h"
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <private/hwui/DrawGlInfo.h>
-#include <ui/Region.h>
-#include <utils/Functor.h>
-#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <set>
-class GrContext;
-
namespace android {
namespace uirenderer {
class Layer;
-class DeferredLayerUpdater;
namespace renderthread {
class CacheManager;
-class CanvasContext;
class RenderThread;
}
+class IGpuContextCallback {
+public:
+ virtual void onContextDestroyed() = 0;
+protected:
+ virtual ~IGpuContextCallback() {}
+};
+
// wrapper of Caches for users to migrate to.
class RenderState {
PREVENT_COPY_AND_ASSIGN(RenderState);
@@ -48,66 +45,30 @@
friend class renderthread::CacheManager;
public:
- void onContextCreated();
- void onContextDestroyed();
-
- void onBitmapDestroyed(uint32_t pixelRefId);
-
- void setViewport(GLsizei width, GLsizei height);
- void getViewport(GLsizei* outWidth, GLsizei* outHeight);
-
- void bindFramebuffer(GLuint fbo);
- GLuint getFramebuffer() { return mFramebuffer; }
- GLuint createFramebuffer();
- void deleteFramebuffer(GLuint fbo);
-
- void debugOverdraw(bool enable, bool clear);
+ void registerContextCallback(IGpuContextCallback* cb) { mContextCallbacks.insert(cb); }
+ void removeContextCallback(IGpuContextCallback* cb) { mContextCallbacks.erase(cb); }
void registerLayer(Layer* layer) { mActiveLayers.insert(layer); }
void unregisterLayer(Layer* layer) { mActiveLayers.erase(layer); }
- void registerCanvasContext(renderthread::CanvasContext* context) {
- mRegisteredContexts.insert(context);
- }
-
- void unregisterCanvasContext(renderthread::CanvasContext* context) {
- mRegisteredContexts.erase(context);
- }
-
- void registerDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) {
- mActiveLayerUpdaters.insert(layerUpdater);
- }
-
- void unregisterDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) {
- mActiveLayerUpdaters.erase(layerUpdater);
- }
-
// TODO: This system is a little clunky feeling, this could use some
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
- GrContext* getGrContext() const;
-
- void dump();
-
- renderthread::RenderThread& getRenderThread();
+ renderthread::RenderThread& getRenderThread() const { return mRenderThread; }
private:
- void destroyLayersInUpdater();
-
explicit RenderState(renderthread::RenderThread& thread);
- ~RenderState();
+ ~RenderState() {}
+
+ // Context notifications are only to be triggered by renderthread::RenderThread
+ void onContextCreated();
+ void onContextDestroyed();
+
+ std::set<IGpuContextCallback*> mContextCallbacks;
+ std::set<Layer*> mActiveLayers;
renderthread::RenderThread& mRenderThread;
-
- std::set<Layer*> mActiveLayers;
- std::set<DeferredLayerUpdater*> mActiveLayerUpdaters;
- std::set<renderthread::CanvasContext*> mRegisteredContexts;
-
- GLsizei mViewportWidth;
- GLsizei mViewportHeight;
- GLuint mFramebuffer;
-
pthread_t mThreadId;
};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 727cef3..c8c394a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -27,7 +27,6 @@
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
-#include "renderstate/RenderState.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
#include "../Properties.h"
@@ -76,10 +75,6 @@
return nullptr;
}
-void CanvasContext::destroyLayer(RenderNode* node) {
- skiapipeline::SkiaPipeline::destroyLayer(node);
-}
-
void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) {
ATRACE_CALL();
auto renderType = Properties::getRenderPipelineType();
@@ -113,13 +108,11 @@
, mRenderPipeline(std::move(renderPipeline)) {
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
- mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
CanvasContext::~CanvasContext() {
destroy();
- mRenderThread.renderState().unregisterCanvasContext(this);
for (auto& node : mRenderNodes) {
node->clearRoot();
}
@@ -151,7 +144,8 @@
mNativeSurface = std::move(surface);
- ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb;
+ // TODO(b/111436479) Introduce a way for app to specify DisplayColorGamut mode.
+ ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Legacy;
bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode);
mFrameNumber = -1;
@@ -416,7 +410,7 @@
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
- mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo,
+ mContentDrawBounds, mOpaque, mLightInfo,
mRenderNodes, &(profiler()));
int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1;
@@ -555,8 +549,7 @@
// purposes when the frame is actually drawn
node->setPropertyFieldsDirty(RenderNode::GENERIC);
- mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mWideColorGamut,
- mLightInfo);
+ mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo);
node->incStrong(nullptr);
mPrefetchedLayers.insert(node);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 02ee72f..2315cb9 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -75,8 +75,7 @@
*/
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator,
ErrorHandler* errorHandler) {
- return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut,
- errorHandler);
+ return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, errorHandler);
}
/**
@@ -97,12 +96,6 @@
*/
void unpinImages() { mRenderPipeline->unpinImages(); }
- /**
- * Destroy any layers that have been attached to the provided RenderNode removing
- * any state that may have been set during createOrUpdateLayer().
- */
- static void destroyLayer(RenderNode* node);
-
static void invokeFunctor(const RenderThread& thread, Functor* functor);
static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 5f8d7ad..0cb23e5 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -20,13 +20,14 @@
#include <log/log.h>
#include <private/gui/SyncFeatures.h>
#include <utils/Trace.h>
+#include "utils/Color.h"
#include "utils/StringUtils.h"
-#include "DeviceInfo.h"
#include "Frame.h"
#include "Properties.h"
#include <EGL/eglext.h>
+#include <GLES/gl.h>
#include <string>
#include <vector>
@@ -75,6 +76,7 @@
bool pixelFormatFloat = false;
bool glColorSpace = false;
bool scRGB = false;
+ bool displayP3 = false;
bool contextPriority = false;
bool surfacelessContext = false;
} EglExtensions;
@@ -124,7 +126,17 @@
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface, nullptr, /* force */ true);
- DeviceInfo::initialize();
+
+ mSurfaceColorGamut = DataSpaceToColorGamut(
+ static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace()));
+
+ LOG_ALWAYS_FATAL_IF(mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut &&
+ !EglExtensions.displayP3, "EGL doesn't support Display P3.");
+
+ mSurfaceColorType = PixelFormatToColorType(
+ static_cast<android_pixel_format>(DeviceInfo::get()->getTargetPixelFormat()));
+ mSurfaceColorSpace = DataSpaceToColorSpace(
+ static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace()));
}
void EglManager::initExtensions() {
@@ -148,6 +160,7 @@
#else
EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
#endif
+ EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3");
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
}
@@ -160,6 +173,10 @@
ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
EGLint swapBehavior =
(mSwapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+
+ // Note: The default pixel format is RGBA_8888, when other formats are
+ // available, we should check the target pixel format and configure the
+ // attributes list properly.
EGLint attribs[] = {EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE,
@@ -255,11 +272,12 @@
}
}
-EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) {
+EGLSurface EglManager::createSurface(EGLNativeWindowType window, ColorMode colorMode) {
LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
- wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB &&
- EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext;
+ bool wideColorGamut = colorMode == ColorMode::WideColorGamut && EglExtensions.glColorSpace &&
+ EglExtensions.scRGB && EglExtensions.pixelFormatFloat &&
+ EglExtensions.noConfigContext;
// The color space we want to use depends on whether linear blending is turned
// on and whether the app has requested wide color gamut rendering. When wide
@@ -269,9 +287,9 @@
// When wide gamut rendering is off:
// - Blending is done by default in gamma space, which requires using a
// linear EGL color space (the GPU uses the color values as is)
- // - If linear blending is on, we must use the sRGB EGL color space (the
- // GPU will perform sRGB to linear and linear to SRGB conversions before
- // and after blending)
+ // - If linear blending is on, we must use the non-linear EGL color space
+ // (the GPU will perform sRGB to linear and linear to SRGB conversions
+ // before and after blending)
//
// When wide gamut rendering is on we cannot rely on the GPU performing
// linear blending for us. We use two different color spaces to tag the
@@ -279,7 +297,7 @@
// - Gamma blending (default) requires the use of the scRGB-nl color space
// - Linear blending requires the use of the scRGB color space
- // Not all Android targets support the EGL_GL_COLOR_SPACE_KHR extension
+ // Not all Android targets support the EGL_GL_COLORSPACE_KHR extension
// We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value.
// According to section 3.4.1 of the EGL specification, the attributes
// list is considered empty if the first entry is EGL_NONE
@@ -291,13 +309,21 @@
if (wideColorGamut) {
attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
} else {
- attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
+ if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) {
+ attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT;
+ } else {
+ attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
+ }
}
#else
if (wideColorGamut) {
attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
} else {
- attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+ if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) {
+ attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT;
+ } else {
+ attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+ }
}
#endif
}
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 507673a..e97228c 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -18,11 +18,13 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <SkImageInfo.h>
#include <SkRect.h>
#include <cutils/compiler.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
+#include "IRenderPipeline.h"
namespace android {
namespace uirenderer {
@@ -45,7 +47,7 @@
bool hasEglContext();
- EGLSurface createSurface(EGLNativeWindowType window, bool wideColorGamut);
+ EGLSurface createSurface(EGLNativeWindowType window, ColorMode colorMode);
void destroySurface(EGLSurface surface);
void destroy();
@@ -76,6 +78,9 @@
// Depending on installed extensions, the result is either Android native fence or EGL fence.
status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence);
+ SkColorType getSurfaceColorType() const { return mSurfaceColorType; }
+ sk_sp<SkColorSpace> getSurfaceColorSpace() { return mSurfaceColorSpace; }
+
private:
void initExtensions();
@@ -89,8 +94,10 @@
EGLConfig mEglConfigWideGamut;
EGLContext mEglContext;
EGLSurface mPBufferSurface;
-
EGLSurface mCurrentSurface;
+ SkColorSpace::Gamut mSurfaceColorGamut;
+ SkColorType mSurfaceColorType;
+ sk_sp<SkColorSpace> mSurfaceColorSpace;
enum class SwapBehavior {
Discard,
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index b7b7853..0297c9c 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -16,11 +16,12 @@
#pragma once
+#include "DamageAccumulator.h"
#include "FrameInfoVisualizer.h"
#include "LayerUpdateQueue.h"
+#include "Lighting.h"
#include "SwapBehavior.h"
#include "hwui/Bitmap.h"
-#include "thread/TaskManager.h"
#include <SkRect.h>
#include <utils/RefBase.h>
@@ -35,13 +36,25 @@
class DeferredLayerUpdater;
class ErrorHandler;
+class TaskManager;
namespace renderthread {
enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded };
enum class ColorMode {
- Srgb,
+ // Legacy means HWUI will produce buffer with whatever platform prefers
+ // HWUI to produce, however, HWUI doesn't accurately convert color from
+ // source color space to destination color space, instead HWUI will take
+ // the pixel value directly and interpret it destination color space.
+ Legacy,
+ // DisplayColorGamut means HWUI will produce buffer with whatever platform
+ // prefers HWUI to produce and accurately convert color from source color
+ // space to destination color space.
+ DisplayColorGamut,
+ // WideColorGamut means HWUI would support rendering scRGB non-linear into
+ // a signed buffer with enough range to support the wide color gamut of the
+ // display.
WideColorGamut,
// Hdr
};
@@ -55,7 +68,7 @@
virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, bool wideColorGamut, const LightInfo& lightInfo,
+ bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) = 0;
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
@@ -67,15 +80,17 @@
virtual bool isContextReady() = 0;
virtual void onDestroyHardwareResources() = 0;
virtual void renderLayers(const LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
const LightInfo& lightInfo) = 0;
virtual TaskManager* getTaskManager() = 0;
virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
- bool wideColorGamut, ErrorHandler* errorHandler) = 0;
+ ErrorHandler* errorHandler) = 0;
virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
virtual void unpinImages() = 0;
virtual void onPrepareTree() = 0;
+ virtual SkColorType getSurfaceColorType() const = 0;
+ virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
virtual ~IRenderPipeline() {}
};
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 7a5348a..6106e24 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -345,13 +345,6 @@
}
}
-void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) {
- if (!RenderThread::hasInstance()) return;
- RenderThread& thread = RenderThread::getInstance();
- thread.queue().post(
- [&thread, pixelRefId]() { thread.renderState().onBitmapDestroyed(pixelRefId); });
-}
-
void RenderProxy::disableVsync() {
Properties::disableVsync = true;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 969ad00..d22f56e 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -127,8 +127,6 @@
static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
- static void onBitmapDestroyed(uint32_t pixelRefId);
-
ANDROID_API static void disableVsync();
static void repackVectorDrawableAtlas();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 7258a0a..207673c1 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -162,7 +162,7 @@
}
void RenderThread::initThreadLocals() {
- mDisplayInfo = DeviceInfo::queryDisplayInfo();
+ mDisplayInfo = DeviceInfo::get()->displayInfo();
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
mTimeLord.setFrameInterval(frameIntervalNanos);
initializeDisplayEventReceiver();
@@ -177,7 +177,6 @@
return;
}
mEglManager->initialize();
- renderState().onContextCreated();
#ifdef HWUI_GLES_WRAP_ENABLED
debug::GlesDriver* driver = debug::GlesDriver::get();
@@ -201,7 +200,6 @@
void RenderThread::destroyGlContext() {
if (mEglManager->hasEglContext()) {
setGrContext(nullptr);
- renderState().onContextDestroyed();
mEglManager->destroy();
}
}
@@ -243,9 +241,14 @@
void RenderThread::setGrContext(sk_sp<GrContext> context) {
mCacheManager->reset(context);
if (mGrContext) {
+ mRenderState->onContextDestroyed();
mGrContext->releaseResourcesAndAbandonContext();
}
mGrContext = std::move(context);
+ if (mGrContext) {
+ mRenderState->onContextCreated();
+ DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
+ }
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 038e13c..e60d43e 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,7 +16,6 @@
#include "VulkanManager.h"
-#include "DeviceInfo.h"
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
@@ -40,7 +39,6 @@
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
void VulkanManager::destroy() {
- mRenderThread.renderState().onContextDestroyed();
mRenderThread.setGrContext(nullptr);
if (VK_NULL_HANDLE != mCommandPool) {
@@ -399,13 +397,9 @@
free_features_extensions_structs(features);
- DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
-
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
}
-
- mRenderThread.renderState().onContextCreated();
}
// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index ebc11a5..7a539ae 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -118,6 +118,10 @@
// Creates a fence that is signaled, when all the pending Vulkan commands are flushed.
status_t createReleaseFence(sp<Fence>& nativeFence);
+ // TODO(b/115636873): Handle composition preference.
+ SkColorType getSurfaceColorType() const { return SkColorType::kN32_SkColorType; }
+ sk_sp<SkColorSpace> getSurfaceColorSpace() { return SkColorSpace::MakeSRGB(); }
+
private:
friend class RenderThread;
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0e6582c..a00b8db 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -16,7 +16,6 @@
#pragma once
-#include <DeviceInfo.h>
#include <DisplayList.h>
#include <Matrix.h>
#include <Properties.h>
@@ -25,6 +24,7 @@
#include <Snapshot.h>
#include <hwui/Bitmap.h>
#include <pipeline/skia/SkiaRecordingCanvas.h>
+#include <private/hwui/DrawGlInfo.h>
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
@@ -179,12 +179,6 @@
static sp<RenderNode> createNode(
int left, int top, int right, int bottom,
std::function<void(RenderProperties& props, Canvas& canvas)> setup) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
-
sp<RenderNode> node = new RenderNode();
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
@@ -202,12 +196,6 @@
static sp<RenderNode> createNode(
int left, int top, int right, int bottom,
std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
-
sp<RenderNode> node = new RenderNode();
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
@@ -233,11 +221,6 @@
std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)>
setup,
const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
sp<RenderNode> node = new RenderNode();
if (name) {
node->setName(name);
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 48bc8e4..2926ef3 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -537,7 +537,7 @@
layerUpdateQueue.enqueueLayerWithDamage(child.get(),
android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- pipeline->renderLayersImpl(layerUpdateQueue, true, false);
+ pipeline->renderLayersImpl(layerUpdateQueue, true);
EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer
RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
index 85655fc..3e8e057 100644
--- a/libs/hwui/tests/unit/RenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
@@ -22,8 +22,6 @@
using namespace android::uirenderer;
TEST(RenderProperties, layerValidity) {
- DeviceInfo::initialize();
-
const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
ASSERT_LE(2048, maxTextureSize);
ASSERT_GT(100000, maxTextureSize);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 6c398ee..415f9e8 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -20,6 +20,7 @@
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "IContextFactory.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
@@ -46,7 +47,8 @@
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
- skiaDL->mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
+ GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
+ skiaDL->mChildFunctors.push_back(&functorDrawable);
skiaDL->mMutableImages.push_back(nullptr);
skiaDL->mVectorDrawables.push_back(nullptr);
skiaDL->mProjectionReceiver = &drawable;
@@ -95,7 +97,8 @@
SkCanvas dummyCanvas;
TestUtils::MockFunctor functor;
- skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+ GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
+ skiaDL.mChildFunctors.push_back(&functorDrawable);
SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index a9124d9..680fcb3 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -50,7 +50,7 @@
auto surface = SkSurface::MakeRasterN32Premul(1, 1);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -83,7 +83,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -105,11 +105,11 @@
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
@@ -129,7 +129,7 @@
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
@@ -171,7 +171,7 @@
lightGeometry.center = {0.0f, 0.0f, 0.0f};
LightInfo lightInfo;
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, false, lightInfo);
+ pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo);
ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED);
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE);
@@ -202,37 +202,37 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// Single draw, should be white.
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
// 1 Overdraw, should be blue blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
// 2 Overdraw, should be green blended onto white
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
// 3 Overdraw, should be pink blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
// 4 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
// 5 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
}
@@ -318,7 +318,7 @@
SkRect dirty = SkRect::MakeWH(800, 600);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>());
- pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, contentDrawBounds, surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface);
EXPECT_EQ(4, surface->canvas()->mDrawCounter);
}
@@ -348,7 +348,7 @@
SkRect dirty = SkRect::MakeLTRB(10, 20, 30, 40);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>());
- pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false,
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
@@ -378,7 +378,17 @@
SkRect dirty = SkRect::MakeLTRB(10, 10, 40, 40);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>());
- pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false,
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ EXPECT_FALSE(pipeline->isSurfaceReady());
+ EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::Legacy));
+ EXPECT_TRUE(pipeline->isSurfaceReady());
+ renderThread.destroyGlContext();
+ EXPECT_FALSE(pipeline->isSurfaceReady());
+}
+
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index a3e7859..9f71e91 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -57,6 +57,65 @@
return false;
}
+SkColorType PixelFormatToColorType(android_pixel_format pixelFormat) {
+ switch (pixelFormat) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return SkColorType::kN32_SkColorType;
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ return SkColorType::kRGBA_F16_SkColorType;
+ case HAL_PIXEL_FORMAT_RGBA_1010102:
+ return SkColorType::kRGBA_1010102_SkColorType;
+ default:
+ ALOGW("Unsupported pixel format: %d, return kN32 by default", pixelFormat);
+ return SkColorType::kN32_SkColorType;
+ }
+}
+
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
+ switch (colorType) {
+ case kRGBA_8888_SkColorType:
+ return PIXEL_FORMAT_RGBA_8888;
+ case kRGBA_F16_SkColorType:
+ return PIXEL_FORMAT_RGBA_FP16;
+ case kRGB_565_SkColorType:
+ return PIXEL_FORMAT_RGB_565;
+ case kRGB_888x_SkColorType:
+ return PIXEL_FORMAT_RGBX_8888;
+ case kRGBA_1010102_SkColorType:
+ return PIXEL_FORMAT_RGBA_1010102;
+ case kARGB_4444_SkColorType:
+ return PIXEL_FORMAT_RGBA_4444;
+ default:
+ ALOGW("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
+ return PIXEL_FORMAT_RGBA_8888;
+ }
+}
+
+SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) {
+ switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return SkColorSpace::kSRGB_Gamut;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ return SkColorSpace::kRec2020_Gamut;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return SkColorSpace::kDCIP3_D65_Gamut;
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return SkColorSpace::kAdobeRGB_Gamut;
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ default:
+ ALOGW("Unsupported Gamut: %d, return SRGB gamut by default", dataSpace);
+ return SkColorSpace::kSRGB_Gamut;
+ }
+}
+
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
SkColorSpace::Gamut gamut;
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 3c13a54..e935a0d 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -18,9 +18,11 @@
#include <math.h>
#include <system/graphics.h>
+#include <ui/PixelFormat.h>
#include <SkColor.h>
#include <SkColorSpace.h>
+#include <SkImageInfo.h>
namespace android {
namespace uirenderer {
@@ -113,6 +115,12 @@
// returns true for sRGB, gamma 2.2 and Display P3 for instance
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
+SkColorType PixelFormatToColorType(android_pixel_format pixelFormat);
+
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
+
+SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace);
+
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
struct Lab {
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index bf27300..fcd036c 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -59,5 +59,22 @@
#endif
}
+const char* GLUtils::getGLFramebufferError() {
+ switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
+ case GL_FRAMEBUFFER_COMPLETE:
+ return "GL_FRAMEBUFFER_COMPLETE";
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ return "GL_FRAMEBUFFER_UNSUPPORTED";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ default:
+ return "Unknown error";
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index debfb5d..ca8810b 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -20,6 +20,12 @@
#include <log/log.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
namespace android {
namespace uirenderer {
@@ -43,8 +49,53 @@
*/
static bool dumpGLErrors();
+ static const char* getGLFramebufferError();
}; // class GLUtils
+class AutoEglImage {
+public:
+ AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
+ EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+ image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
+ imageAttrs);
+ }
+
+ ~AutoEglImage() {
+ if (image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mDisplay, image);
+ }
+ }
+
+ EGLImageKHR image = EGL_NO_IMAGE_KHR;
+
+private:
+ EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+class AutoSkiaGlTexture {
+public:
+ AutoSkiaGlTexture() {
+ glGenTextures(1, &mTexture);
+ glBindTexture(GL_TEXTURE_2D, mTexture);
+ }
+
+ ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
+
+ GLuint mTexture = 0;
+};
+
+class AutoGLFramebuffer {
+public:
+ AutoGLFramebuffer() {
+ glGenFramebuffers(1, &mFb);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFb);
+ }
+
+ ~AutoGLFramebuffer() { glDeleteFramebuffers(1, &mFb); }
+
+ GLuint mFb;
+};
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 255619c..52cb75e 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -25,6 +25,58 @@
namespace android {
namespace os {
+/**
+ * A wrapper for a union type to contain multiple types of values.
+ *
+ */
+struct StatsLogValue {
+ // Keep in sync with FieldValue.h
+ enum STATS_LOG_VALUE_TYPE {
+ UNKNOWN = 0,
+ INT = 1,
+ LONG = 2,
+ FLOAT = 3,
+ DOUBLE = 4,
+ STRING = 5,
+ STORAGE = 6
+ };
+
+ StatsLogValue() : type(UNKNOWN) {}
+
+ StatsLogValue(int32_t v) {
+ int_value = v;
+ type = INT;
+ }
+
+ StatsLogValue(int64_t v) {
+ long_value = v;
+ type = LONG;
+ }
+
+ StatsLogValue(float v) {
+ float_value = v;
+ type = FLOAT;
+ }
+
+ StatsLogValue(const std::string& v) {
+ str_value = v;
+ type = STRING;
+ }
+
+ void setType(STATS_LOG_VALUE_TYPE t) { type = t; }
+
+ union {
+ int32_t int_value;
+ int64_t long_value;
+ float float_value;
+ double double_value;
+ };
+ std::string str_value;
+ std::vector<uint8_t> storage_value;
+
+ STATS_LOG_VALUE_TYPE type;
+};
+
// Represents a parcelable object. Only used to send data from Android OS to statsd.
class StatsLogEventWrapper : public android::Parcelable {
public:
@@ -36,8 +88,22 @@
android::status_t readFromParcel(const android::Parcel* in);
- // These are public for ease of conversion.
- std::vector<uint8_t> bytes;
+ int getTagId() const { return mTagId; }
+
+ int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; }
+
+ int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
+
+ std::vector<StatsLogValue> getElements() const { return mElements; }
+
+ private:
+ int mTagId;
+
+ int64_t mElapsedRealTimeNs;
+
+ int64_t mWallClockTimeNs;
+
+ std::vector<StatsLogValue> mElements;
};
} // Namespace os
} // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 8b3aa9a..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -32,13 +32,70 @@
StatsLogEventWrapper::StatsLogEventWrapper(){};
status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
- out->writeByteVector(bytes);
- return ::android::NO_ERROR;
+ // Implement me if desired. We don't currently use this.
+ ALOGE(
+ "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not "
+ "implemented.");
+ (void)out; // To prevent compile error of unused parameter 'in'
+ return UNKNOWN_ERROR;
};
status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
- in->readByteVector(&bytes);
- return ::android::NO_ERROR;
+ status_t res = OK;
+ if (in == NULL) {
+ ALOGE("statsd received parcel argument was NULL.");
+ return BAD_VALUE;
+ }
+ if ((res = in->readInt32(&mTagId)) != OK) {
+ ALOGE("statsd could not read tagId from parcel");
+ return res;
+ }
+ if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) {
+ ALOGE("statsd could not read elapsed real time from parcel");
+ return res;
+ }
+ if ((res = in->readInt64(&mWallClockTimeNs)) != OK) {
+ ALOGE("statsd could not read wall clock time from parcel");
+ return res;
+ }
+ int dataSize = 0;
+ if ((res = in->readInt32(&dataSize)) != OK) {
+ ALOGE("statsd could not read data size from parcel");
+ return res;
+ }
+ if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 ||
+ dataSize <= 0) {
+ ALOGE("statsd received invalid parcel");
+ return BAD_VALUE;
+ }
+
+ for (int i = 0; i < dataSize; i++) {
+ int type = in->readInt32();
+ switch (type) {
+ case StatsLogValue::INT:
+ mElements.push_back(StatsLogValue(in->readInt32()));
+ break;
+ case StatsLogValue::LONG:
+ mElements.push_back(StatsLogValue(in->readInt64()));
+ break;
+ case StatsLogValue::STRING:
+ mElements.push_back(
+ StatsLogValue(std::string(String8(in->readString16()).string())));
+ break;
+ case StatsLogValue::FLOAT:
+ mElements.push_back(StatsLogValue(in->readFloat()));
+ break;
+ case StatsLogValue::STORAGE:
+ mElements.push_back(StatsLogValue());
+ mElements.back().setType(StatsLogValue::STORAGE);
+ in->readByteVector(&(mElements.back().storage_value));
+ break;
+ default:
+ ALOGE("unrecognized data type: %d", type);
+ return BAD_TYPE;
+ }
+ }
+ return NO_ERROR;
};
} // Namespace os
diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java
index 335ad5e..83f05c2 100644
--- a/location/java/android/location/Address.java
+++ b/location/java/android/location/Address.java
@@ -363,7 +363,7 @@
* or null if it is unknown.
*
* @throws IllegalStateException if this Address has not been assigned
- * a latitude.
+ * a phone number.
*/
public String getPhone() {
return mPhone;
diff --git a/location/java/android/location/CountryDetector.java b/location/java/android/location/CountryDetector.java
index 119d1e0..ae13949 100644
--- a/location/java/android/location/CountryDetector.java
+++ b/location/java/android/location/CountryDetector.java
@@ -21,6 +21,7 @@
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -88,7 +89,7 @@
* create an instance of this class is using the factory
* Context.getSystemService.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public CountryDetector(ICountryDetector service) {
mService = service;
mListeners = new HashMap<CountryListener, ListenerTransport>();
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 5ad7365..f1325ce 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -375,11 +376,11 @@
@UnsupportedAppUsage
private int mUsage = USAGE_UNKNOWN;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mContentType = CONTENT_TYPE_UNKNOWN;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mFlags = 0x0;
private HashSet<String> mTags;
@UnsupportedAppUsage
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index aaf7dd7..62e58ca 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1656,7 +1656,7 @@
* @param timestamp a reference to a non-null AudioTimestamp instance allocated
* and owned by caller.
* @return true if a timestamp is available, or false if no timestamp is available.
- * If a timestamp if available,
+ * If a timestamp is available,
* the AudioTimestamp instance is filled in with a position in frame units, together
* with the estimated time when that frame was presented or is committed to
* be presented.
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d45acdf..1d27c03 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -24,6 +24,7 @@
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.MediaCodecInfo.CodecCapabilities;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -42,6 +43,8 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/**
MediaCodec class can be used to access low-level media codecs, i.e. encoder/decoder components.
@@ -3580,8 +3583,19 @@
native_init();
}
- @UnsupportedAppUsage
- private long mNativeContext;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private long mNativeContext = 0;
+ private final Lock mNativeContextLock = new ReentrantLock();
+
+ private final long lockAndGetContext() {
+ mNativeContextLock.lock();
+ return mNativeContext;
+ }
+
+ private final void setAndUnlockContext(long context) {
+ mNativeContext = context;
+ mNativeContextLock.unlock();
+ }
/** @hide */
public static class MediaImage extends Image {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index ef312d1..995ebb2 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
@@ -1589,7 +1590,7 @@
private VideoCapabilities() { }
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static VideoCapabilities create(
MediaFormat info, CodecCapabilities parent) {
VideoCapabilities caps = new VideoCapabilities();
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 89827bc..7492aa6 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1108,12 +1108,9 @@
/**
* Sets playback rate using {@link PlaybackParams}. The object sets its internal
- * PlaybackParams to the input, except that the object remembers previous speed
- * when input speed is zero. This allows the object to resume at previous speed
- * when play() is called. Calling it before the object is prepared does not change
- * the object state. After the object is prepared, calling it with zero speed is
- * equivalent to calling pause(). After the object is prepared, calling it with
- * non-zero speed is equivalent to calling play().
+ * PlaybackParams to the input. This allows the object to resume at previous speed
+ * when play() is called. Speed of zero is not allowed. Calling it does not change
+ * the object state.
*
* @param params the playback params.
*/
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 6ae4d40..6187900 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -544,29 +544,11 @@
private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
/**
- * Create a request parcel which can be routed to the native media
- * player using {@link #invoke(Parcel, Parcel)}. The Parcel
- * returned has the proper InterfaceToken set. The caller should
- * not overwrite that token, i.e it can only append data to the
- * Parcel.
- *
- * @return A parcel suitable to hold a request for the native
- * player.
- * {@hide}
- */
- @Override
- public Parcel newRequest() {
- Parcel parcel = Parcel.obtain();
- return parcel;
- }
-
- /**
* Invoke a generic method on the native player using opaque protocol
* buffer message for the request and reply. Both payloads' format is a
* convention between the java caller and the native player.
*
- * @param request PlayerMessage for the extension. The
- * caller must use {@link #newRequest()} to get one.
+ * @param msg PlayerMessage for the extension.
*
* @return PlayerMessage with the data returned by the
* native player.
@@ -1536,91 +1518,6 @@
}
/**
- * Gets the media metadata.
- *
- * @param update_only controls whether the full set of available
- * metadata is returned or just the set that changed since the
- * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
- * #METADATA_ALL}.
- *
- * @param apply_filter if true only metadata that matches the
- * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
- * #BYPASS_METADATA_FILTER}.
- *
- * @return The metadata, possibly empty. null if an error occured.
- // FIXME: unhide.
- * {@hide}
- */
- @Override
- public Metadata getMetadata(final boolean update_only,
- final boolean apply_filter) {
- Parcel reply = Parcel.obtain();
- Metadata data = new Metadata();
-
- if (!native_getMetadata(update_only, apply_filter, reply)) {
- reply.recycle();
- return null;
- }
-
- // Metadata takes over the parcel, don't recycle it unless
- // there is an error.
- if (!data.parse(reply)) {
- reply.recycle();
- return null;
- }
- return data;
- }
-
- /**
- * Set a filter for the metadata update notification and update
- * retrieval. The caller provides 2 set of metadata keys, allowed
- * and blocked. The blocked set always takes precedence over the
- * allowed one.
- * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
- * shorthands to allow/block all or no metadata.
- *
- * By default, there is no filter set.
- *
- * @param allow Is the set of metadata the client is interested
- * in receiving new notifications for.
- * @param block Is the set of metadata the client is not interested
- * in receiving new notifications for.
- * @return The call status code.
- *
- // FIXME: unhide.
- * {@hide}
- */
- @Override
- public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
- // Do our serialization manually instead of calling
- // Parcel.writeArray since the sets are made of the same type
- // we avoid paying the price of calling writeValue (used by
- // writeArray) which burns an extra int per element to encode
- // the type.
- Parcel request = newRequest();
-
- // The parcel starts already with an interface token. There
- // are 2 filters. Each one starts with a 4bytes number to
- // store the len followed by a number of int (4 bytes as well)
- // representing the metadata type.
- int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size());
-
- if (request.dataCapacity() < capacity) {
- request.setDataCapacity(capacity);
- }
-
- request.writeInt(allow.size());
- for(Integer t: allow) {
- request.writeInt(t);
- }
- request.writeInt(block.size());
- for(Integer t: block) {
- request.writeInt(t);
- }
- return native_setMetadataFilter(request);
- }
-
- /**
* Resets the MediaPlayer2 to its uninitialized state. After calling
* this method, you will have to initialize it again by setting the
* data source and calling prepare().
@@ -1802,32 +1699,6 @@
private native void _setAuxEffectSendLevel(float level);
- /*
- * @param update_only If true fetch only the set of metadata that have
- * changed since the last invocation of getMetadata.
- * The set is built using the unfiltered
- * notifications the native player sent to the
- * MediaPlayer2Manager during that period of
- * time. If false, all the metadatas are considered.
- * @param apply_filter If true, once the metadata set has been built based on
- * the value update_only, the current filter is applied.
- * @param reply[out] On return contains the serialized
- * metadata. Valid only if the call was successful.
- * @return The status code.
- */
- private native final boolean native_getMetadata(boolean update_only,
- boolean apply_filter,
- Parcel reply);
-
- /*
- * @param request Parcel with the 2 serialized lists of allowed
- * metadata types followed by the one to be
- * dropped. Each list starts with an integer
- * indicating the number of metadata type elements.
- * @return The status code.
- */
- private native final int native_setMetadataFilter(Parcel request);
-
private static native final void native_init();
private native final void native_setup(Object mediaplayer2_this);
private native final void native_finalize();
@@ -1903,25 +1774,6 @@
mFormat = format;
}
- /**
- * Flatten this object in to a Parcel.
- *
- * @param dest The Parcel in which the object should be written.
- * @param flags Additional flags about how the object should be written.
- * May be 0 or {@link android.os.Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
- */
- /* package private */ void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mTrackType);
- dest.writeString(getLanguage());
-
- if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
- dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
- dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
- dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
- }
- }
-
@Override
public String toString() {
StringBuilder out = new StringBuilder(128);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 43bee85..ffeff4d 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -34,6 +34,7 @@
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.media.session.MediaSession;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -2064,7 +2065,7 @@
}
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean isDefault() {
return this == sStatic.mDefaultAudioVideo;
}
diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java
index 716e0cb..f704acd 100644
--- a/media/java/android/media/MiniThumbFile.java
+++ b/media/java/android/media/MiniThumbFile.java
@@ -18,9 +18,12 @@
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
import android.util.Log;
+import dalvik.system.VMRuntime;
+
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
@@ -42,7 +45,10 @@
*
* @hide This file is shared between MediaStore and MediaProvider and should remained internal use
* only.
+ * @deprecated thumbnails are now maintained in separate files, and this file
+ * format is no longer used.
*/
+@Deprecated
public class MiniThumbFile {
private static final String TAG = "MiniThumbFile";
private static final int MINI_THUMB_DATA_FILE_VERSION = 4;
@@ -69,6 +75,9 @@
}
public static synchronized MiniThumbFile instance(Uri uri) {
+ if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
+ throw new UnsupportedOperationException();
+ }
String type = uri.getPathSegments().get(1);
MiniThumbFile file = sThumbFiles.get(type);
// Log.v(TAG, "get minithumbfile for type: "+type);
diff --git a/media/java/android/media/PlaybackParams.java b/media/java/android/media/PlaybackParams.java
index 6594dd7..3fe1a32 100644
--- a/media/java/android/media/PlaybackParams.java
+++ b/media/java/android/media/PlaybackParams.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -94,7 +95,7 @@
private static final int SET_AUDIO_FALLBACK_MODE = 1 << 2;
@UnsupportedAppUsage
private static final int SET_AUDIO_STRETCH_MODE = 1 << 3;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mSet = 0;
// params
@@ -104,7 +105,7 @@
private int mAudioStretchMode = AUDIO_STRETCH_MODE_DEFAULT;
@UnsupportedAppUsage
private float mPitch = 1.0f;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private float mSpeed = 1.0f;
public PlaybackParams() {
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 1a456f6..9746842 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -21,6 +21,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -872,7 +873,7 @@
* In case of failure, the returned value is negative and implementation specific.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int command(int cmdCode, byte[] command, byte[] reply)
throws IllegalStateException {
checkState("command()");
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 7681cc3..3870124 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,8 +88,6 @@
name: "libmedia2_jni",
srcs: [
- "android_media_Media2HTTPConnection.cpp",
- "android_media_Media2HTTPService.cpp",
"android_media_Media2DataSource.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MediaPlayer2.cpp",
@@ -142,6 +140,7 @@
"libstagefright_player2",
"libstagefright_rtsp",
"libstagefright_timedtext2",
+ "libmedia2_jni_core",
],
group_static_libs: true,
diff --git a/media/jni/android_media_Media2HTTPConnection.cpp b/media/jni/android_media_Media2HTTPConnection.cpp
deleted file mode 100644
index d02ee06..0000000
--- a/media/jni/android_media_Media2HTTPConnection.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "Media2HTTPConnection-JNI"
-#include <utils/Log.h>
-
-#include <mediaplayer2/JavaVMHelper.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-#include "android_media_Media2HTTPConnection.h"
-#include "android_util_Binder.h"
-
-#include "log/log.h"
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-namespace android {
-
-static const size_t kBufferSize = 32768;
-
-JMedia2HTTPConnection::JMedia2HTTPConnection(JNIEnv *env, jobject thiz) {
- mMedia2HTTPConnectionObj = env->NewGlobalRef(thiz);
- CHECK(mMedia2HTTPConnectionObj != NULL);
-
- ScopedLocalRef<jclass> media2HTTPConnectionClass(
- env, env->GetObjectClass(mMedia2HTTPConnectionObj));
- CHECK(media2HTTPConnectionClass.get() != NULL);
-
- mConnectMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "connect",
- "(Ljava/lang/String;Ljava/lang/String;)Z");
- CHECK(mConnectMethod != NULL);
-
- mDisconnectMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "disconnect",
- "()V");
- CHECK(mDisconnectMethod != NULL);
-
- mReadAtMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "readAt",
- "(J[BI)I");
- CHECK(mReadAtMethod != NULL);
-
- mGetSizeMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "getSize",
- "()J");
- CHECK(mGetSizeMethod != NULL);
-
- mGetMIMETypeMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "getMIMEType",
- "()Ljava/lang/String;");
- CHECK(mGetMIMETypeMethod != NULL);
-
- mGetUriMethod = env->GetMethodID(
- media2HTTPConnectionClass.get(),
- "getUri",
- "()Ljava/lang/String;");
- CHECK(mGetUriMethod != NULL);
-
- ScopedLocalRef<jbyteArray> tmp(
- env, env->NewByteArray(kBufferSize));
- mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
- CHECK(mByteArrayObj != NULL);
-}
-
-JMedia2HTTPConnection::~JMedia2HTTPConnection() {
- JNIEnv *env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mMedia2HTTPConnectionObj);
- env->DeleteGlobalRef(mByteArrayObj);
-}
-
-bool JMedia2HTTPConnection::connect(
- const char *uri, const KeyedVector<String8, String8> *headers) {
- String8 tmp("");
- if (headers != NULL) {
- for (size_t i = 0; i < headers->size(); ++i) {
- tmp.append(headers->keyAt(i));
- tmp.append(String8(": "));
- tmp.append(headers->valueAt(i));
- tmp.append(String8("\r\n"));
- }
- }
-
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jstring juri = env->NewStringUTF(uri);
- jstring jheaders = env->NewStringUTF(tmp.string());
-
- jboolean ret =
- env->CallBooleanMethod(mMedia2HTTPConnectionObj, mConnectMethod, juri, jheaders);
-
- env->DeleteLocalRef(juri);
- env->DeleteLocalRef(jheaders);
-
- return (bool)ret;
-}
-
-void JMedia2HTTPConnection::disconnect() {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->CallVoidMethod(mMedia2HTTPConnectionObj, mDisconnectMethod);
-}
-
-ssize_t JMedia2HTTPConnection::readAt(off64_t offset, void *data, size_t size) {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
-
- if (size > kBufferSize) {
- size = kBufferSize;
- }
-
- jint n = env->CallIntMethod(
- mMedia2HTTPConnectionObj, mReadAtMethod, (jlong)offset, mByteArrayObj, (jint)size);
-
- if (n > 0) {
- env->GetByteArrayRegion(
- mByteArrayObj,
- 0,
- n,
- (jbyte *)data);
- }
-
- return n;
-}
-
-off64_t JMedia2HTTPConnection::getSize() {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- return (off64_t)(env->CallLongMethod(mMedia2HTTPConnectionObj, mGetSizeMethod));
-}
-
-status_t JMedia2HTTPConnection::getMIMEType(String8 *mimeType) {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jstring jmime = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetMIMETypeMethod);
- jboolean flag = env->ExceptionCheck();
- if (flag) {
- env->ExceptionClear();
- return UNKNOWN_ERROR;
- }
-
- const char *str = env->GetStringUTFChars(jmime, 0);
- if (str != NULL) {
- *mimeType = String8(str);
- } else {
- *mimeType = "application/octet-stream";
- }
- env->ReleaseStringUTFChars(jmime, str);
- return OK;
-}
-
-status_t JMedia2HTTPConnection::getUri(String8 *uri) {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jstring juri = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetUriMethod);
- jboolean flag = env->ExceptionCheck();
- if (flag) {
- env->ExceptionClear();
- return UNKNOWN_ERROR;
- }
-
- const char *str = env->GetStringUTFChars(juri, 0);
- *uri = String8(str);
- env->ReleaseStringUTFChars(juri, str);
- return OK;
-}
-
-} // namespace android
diff --git a/media/jni/android_media_Media2HTTPConnection.h b/media/jni/android_media_Media2HTTPConnection.h
deleted file mode 100644
index 14bc677..0000000
--- a/media/jni/android_media_Media2HTTPConnection.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
-#define _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
-
-#include "jni.h"
-
-#include <media/MediaHTTPConnection.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-struct JMedia2HTTPConnection : public MediaHTTPConnection {
- JMedia2HTTPConnection(JNIEnv *env, jobject thiz);
-
- virtual bool connect(
- const char *uri, const KeyedVector<String8, String8> *headers) override;
-
- virtual void disconnect() override;
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
- virtual off64_t getSize() override;
- virtual status_t getMIMEType(String8 *mimeType) override;
- virtual status_t getUri(String8 *uri) override;
-
-protected:
- virtual ~JMedia2HTTPConnection();
-
-private:
- jobject mMedia2HTTPConnectionObj;
- jmethodID mConnectMethod;
- jmethodID mDisconnectMethod;
- jmethodID mReadAtMethod;
- jmethodID mGetSizeMethod;
- jmethodID mGetMIMETypeMethod;
- jmethodID mGetUriMethod;
-
- jbyteArray mByteArrayObj;
-
- DISALLOW_EVIL_CONSTRUCTORS(JMedia2HTTPConnection);
-};
-
-} // namespace android
-
-#endif // _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
diff --git a/media/jni/android_media_Media2HTTPService.cpp b/media/jni/android_media_Media2HTTPService.cpp
deleted file mode 100644
index 1c63889..0000000
--- a/media/jni/android_media_Media2HTTPService.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "Media2HTTPService-JNI"
-#include <utils/Log.h>
-
-#include "android_media_Media2HTTPConnection.h"
-#include "android_media_Media2HTTPService.h"
-
-#include "log/log.h"
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-#include <mediaplayer2/JavaVMHelper.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-namespace android {
-
-JMedia2HTTPService::JMedia2HTTPService(JNIEnv *env, jobject thiz) {
- mMedia2HTTPServiceObj = env->NewGlobalRef(thiz);
- CHECK(mMedia2HTTPServiceObj != NULL);
-
- ScopedLocalRef<jclass> media2HTTPServiceClass(env, env->GetObjectClass(mMedia2HTTPServiceObj));
- CHECK(media2HTTPServiceClass.get() != NULL);
-
- mMakeHTTPConnectionMethod = env->GetMethodID(
- media2HTTPServiceClass.get(),
- "makeHTTPConnection",
- "()Landroid/media/Media2HTTPConnection;");
- CHECK(mMakeHTTPConnectionMethod != NULL);
-}
-
-JMedia2HTTPService::~JMedia2HTTPService() {
- JNIEnv *env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mMedia2HTTPServiceObj);
-}
-
-sp<MediaHTTPConnection> JMedia2HTTPService::makeHTTPConnection() {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jobject media2HTTPConnectionObj =
- env->CallObjectMethod(mMedia2HTTPServiceObj, mMakeHTTPConnectionMethod);
-
- return new JMedia2HTTPConnection(env, media2HTTPConnectionObj);
-}
-
-} // namespace android
diff --git a/media/jni/android_media_Media2HTTPService.h b/media/jni/android_media_Media2HTTPService.h
deleted file mode 100644
index 30d03f5..0000000
--- a/media/jni/android_media_Media2HTTPService.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
-#define _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
-
-#include "jni.h"
-
-#include <media/MediaHTTPService.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-struct JMedia2HTTPService : public MediaHTTPService {
- JMedia2HTTPService(JNIEnv *env, jobject thiz);
-
- virtual sp<MediaHTTPConnection> makeHTTPConnection() override;
-
-protected:
- virtual ~JMedia2HTTPService();
-
-private:
- jobject mMedia2HTTPServiceObj;
-
- jmethodID mMakeHTTPConnectionMethod;
-
- DISALLOW_EVIL_CONSTRUCTORS(JMedia2HTTPService);
-};
-
-} // namespace android
-
-#endif // _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 3490ff8..5037209 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -108,8 +108,9 @@
} gCodecInfo;
struct fields_t {
- jfieldID context;
jmethodID postEventFromNativeID;
+ jmethodID lockAndGetContextID;
+ jmethodID setAndUnlockContextID;
jfieldID cryptoInfoNumSubSamplesID;
jfieldID cryptoInfoNumBytesOfClearDataID;
jfieldID cryptoInfoNumBytesOfEncryptedDataID;
@@ -931,7 +932,7 @@
static sp<JMediaCodec> setMediaCodec(
JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
- sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
+ sp<JMediaCodec> old = (JMediaCodec *)env->CallLongMethod(thiz, gFields.lockAndGetContextID);
if (codec != NULL) {
codec->incStrong(thiz);
}
@@ -944,13 +945,15 @@
old->release();
old->decStrong(thiz);
}
- env->SetLongField(thiz, gFields.context, (jlong)codec.get());
+ env->CallVoidMethod(thiz, gFields.setAndUnlockContextID, (jlong)codec.get());
return old;
}
static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
- return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
+ sp<JMediaCodec> codec = (JMediaCodec *)env->CallLongMethod(thiz, gFields.lockAndGetContextID);
+ env->CallVoidMethod(thiz, gFields.setAndUnlockContextID, (jlong)codec.get());
+ return codec;
}
static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
@@ -1876,15 +1879,21 @@
env, env->FindClass("android/media/MediaCodec"));
CHECK(clazz.get() != NULL);
- gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
- CHECK(gFields.context != NULL);
-
gFields.postEventFromNativeID =
env->GetMethodID(
clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
-
CHECK(gFields.postEventFromNativeID != NULL);
+ gFields.lockAndGetContextID =
+ env->GetMethodID(
+ clazz.get(), "lockAndGetContext", "()J");
+ CHECK(gFields.lockAndGetContextID != NULL);
+
+ gFields.setAndUnlockContextID =
+ env->GetMethodID(
+ clazz.get(), "setAndUnlockContext", "(J)V");
+ CHECK(gFields.setAndUnlockContextID != NULL);
+
jfieldID field;
field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_UNENCRYPTED", "I");
CHECK(field != NULL);
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index d4c84b5..1a844cc 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
#include <mediaplayer2/JAudioTrack.h>
#include <mediaplayer2/JavaVMHelper.h>
+#include <mediaplayer2/JMedia2HTTPService.h>
#include <mediaplayer2/mediaplayer2.h>
#include <stdio.h>
#include <assert.h>
@@ -45,7 +46,6 @@
#include "utils/KeyedVector.h"
#include "utils/String8.h"
#include "android_media_BufferingParams.h"
-#include "android_media_Media2HTTPService.h"
#include "android_media_Media2DataSource.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_PlaybackParams.h"
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 43847cc..24d003b 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -49,6 +49,7 @@
"sharedmem.cpp",
"storage_manager.cpp",
"surface_texture.cpp",
+ "system_fonts.cpp",
"trace.cpp",
],
@@ -65,6 +66,7 @@
"libandroid_runtime",
"libnetd_client",
"libhwui",
+ "libxml2",
],
static_libs: [
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index ac5ded6..9f48bc9 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -212,6 +212,18 @@
ASurfaceTexture_getTransformMatrix; # introduced=28
ASurfaceTexture_release; # introduced=28
ASurfaceTexture_updateTexImage; # introduced=28
+ ASystemFontIterator_open; # introduced=29
+ ASystemFontIterator_close; # introduced=29
+ ASystemFontIterator_next; # introduced=29
+ ASystemFont_close; # introduced=29
+ ASystemFont_getFontFilePath; # introduced=29
+ ASystemFont_getWeight; # introduced=29
+ ASystemFont_isItalic; # introduced=29
+ ASystemFont_getLocale; # introduced=29
+ ASystemFont_getCollectionIndex; # introduced=29
+ ASystemFont_getAxisCount; # introduced=29
+ ASystemFont_getAxisTag; # introduced=29
+ ASystemFont_getAxisValue; # introduced=29
ATrace_beginSection; # introduced=23
ATrace_endSection; # introduced=23
ATrace_isEnabled; # introduced=23
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
new file mode 100644
index 0000000..b95adad
--- /dev/null
+++ b/native/android/system_fonts.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+#include <android/system_fonts.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libxml/tree.h>
+#include <log/log.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+struct XmlCharDeleter {
+ void operator()(xmlChar* b) { xmlFree(b); }
+};
+
+struct XmlDocDeleter {
+ void operator()(xmlDoc* d) { xmlFreeDoc(d); }
+};
+
+using XmlCharUniquePtr = std::unique_ptr<xmlChar, XmlCharDeleter>;
+using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>;
+
+struct ASystemFontIterator {
+ XmlDocUniquePtr mXmlDoc;
+ xmlNode* mFontNode;
+};
+
+struct ASystemFont {
+ std::string mFilePath;
+ std::unique_ptr<std::string> mLocale;
+ uint16_t mWeight;
+ bool mItalic;
+ uint32_t mCollectionIndex;
+ std::vector<std::pair<uint32_t, float>> mAxes;
+};
+
+namespace {
+
+std::string xmlTrim(const std::string& in) {
+ if (in.empty()) {
+ return in;
+ }
+ const char XML_SPACES[] = "\u0020\u000D\u000A\u0009";
+ const size_t start = in.find_first_not_of(XML_SPACES); // inclusive
+ if (start == std::string::npos) {
+ return "";
+ }
+ const size_t end = in.find_last_not_of(XML_SPACES); // inclusive
+ if (end == std::string::npos) {
+ return "";
+ }
+ return in.substr(start, end - start + 1 /* +1 since end is inclusive */);
+}
+
+const xmlChar* FAMILY_TAG = BAD_CAST("family");
+const xmlChar* FONT_TAG = BAD_CAST("font");
+
+xmlNode* firstElement(xmlNode* node, const xmlChar* tag) {
+ for (xmlNode* child = node->children; child; child = child->next) {
+ if (xmlStrEqual(child->name, tag)) {
+ return child;
+ }
+ }
+ return nullptr;
+}
+
+xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) {
+ while ((node = node->next) != nullptr) {
+ if (xmlStrEqual(node->name, tag)) {
+ return node;
+ }
+ }
+ return nullptr;
+}
+
+void copyFont(ASystemFontIterator* ite, ASystemFont* out) {
+ const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang");
+ XmlCharUniquePtr filePathStr(
+ xmlNodeListGetString(ite->mXmlDoc.get(), ite->mFontNode->xmlChildrenNode, 1));
+ out->mFilePath = "/system/fonts/" + xmlTrim(
+ std::string(filePathStr.get(), filePathStr.get() + xmlStrlen(filePathStr.get())));
+
+ const xmlChar* WEIGHT_ATTR_NAME = BAD_CAST("weight");
+ XmlCharUniquePtr weightStr(xmlGetProp(ite->mFontNode, WEIGHT_ATTR_NAME));
+ out->mWeight = weightStr ?
+ strtol(reinterpret_cast<const char*>(weightStr.get()), nullptr, 10) : 400;
+
+ const xmlChar* STYLE_ATTR_NAME = BAD_CAST("style");
+ const xmlChar* ITALIC_ATTR_VALUE = BAD_CAST("italic");
+ XmlCharUniquePtr styleStr(xmlGetProp(ite->mFontNode, STYLE_ATTR_NAME));
+ out->mItalic = styleStr ? xmlStrEqual(styleStr.get(), ITALIC_ATTR_VALUE) : false;
+
+ const xmlChar* INDEX_ATTR_NAME = BAD_CAST("index");
+ XmlCharUniquePtr indexStr(xmlGetProp(ite->mFontNode, INDEX_ATTR_NAME));
+ out->mCollectionIndex = indexStr ?
+ strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
+
+ XmlCharUniquePtr localeStr(xmlGetProp(ite->mXmlDoc->parent, LOCALE_ATTR_NAME));
+ out->mLocale.reset(
+ localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr);
+
+ const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag");
+ const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
+ const xmlChar* AXIS_TAG = BAD_CAST("axis");
+ out->mAxes.clear();
+ for (xmlNode* axis = firstElement(ite->mFontNode, AXIS_TAG); axis;
+ axis = nextSibling(axis, AXIS_TAG)) {
+ XmlCharUniquePtr tagStr(xmlGetProp(axis, TAG_ATTR_NAME));
+ if (!tagStr || xmlStrlen(tagStr.get()) != 4) {
+ continue; // Tag value must be 4 char string
+ }
+
+ XmlCharUniquePtr styleValueStr(xmlGetProp(axis, STYLEVALUE_ATTR_NAME));
+ if (!styleValueStr) {
+ continue;
+ }
+
+ uint32_t tag =
+ static_cast<uint32_t>(tagStr.get()[0] << 24) |
+ static_cast<uint32_t>(tagStr.get()[1] << 16) |
+ static_cast<uint32_t>(tagStr.get()[2] << 8) |
+ static_cast<uint32_t>(tagStr.get()[3]);
+ float styleValue = strtod(reinterpret_cast<const char*>(styleValueStr.get()), nullptr);
+ out->mAxes.push_back(std::make_pair(tag, styleValue));
+ }
+}
+
+bool isFontFileAvailable(const std::string& filePath) {
+ std::string fullPath = filePath;
+ struct stat st = {};
+ if (stat(fullPath.c_str(), &st) != 0) {
+ return false;
+ }
+ return S_ISREG(st.st_mode);
+}
+
+xmlNode* findFirstFontNode(xmlDoc* doc) {
+ xmlNode* familySet = xmlDocGetRootElement(doc);
+ if (familySet == nullptr) {
+ return nullptr;
+ }
+ xmlNode* family = firstElement(familySet, FAMILY_TAG);
+ if (family == nullptr) {
+ return nullptr;
+ }
+
+ xmlNode* font = firstElement(family, FONT_TAG);
+ while (font == nullptr) {
+ family = nextSibling(family, FAMILY_TAG);
+ if (family == nullptr) {
+ return nullptr;
+ }
+ font = firstElement(family, FONT_TAG);
+ }
+ return font;
+}
+
+} // namespace
+
+ASystemFontIterator* ASystemFontIterator_open() {
+ std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
+ ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
+ return ite.release();
+}
+
+void ASystemFontIterator_close(ASystemFontIterator* ite) {
+ delete ite;
+}
+
+ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
+ LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
+ if (ite->mFontNode == nullptr) {
+ if (ite->mXmlDoc == nullptr) {
+ return nullptr; // Already at the end.
+ } else {
+ // First time to query font.
+ ite->mFontNode = findFirstFontNode(ite->mXmlDoc.get());
+ if (ite->mFontNode == nullptr) {
+ ite->mXmlDoc.reset();
+ return nullptr; // No font node found.
+ }
+ std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
+ copyFont(ite, font.get());
+ return font.release();
+ }
+ } else {
+ xmlNode* nextNode = nextSibling(ite->mFontNode, FONT_TAG);
+ while (nextNode == nullptr) {
+ xmlNode* family = nextSibling(ite->mFontNode->parent, FAMILY_TAG);
+ if (family == nullptr) {
+ break;
+ }
+ nextNode = firstElement(family, FONT_TAG);
+ }
+ ite->mFontNode = nextNode;
+ if (nextNode == nullptr) {
+ ite->mXmlDoc.reset();
+ return nullptr;
+ }
+
+ std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
+ copyFont(ite, font.get());
+ if (!isFontFileAvailable(font->mFilePath)) {
+ // fonts.xml intentionally contains missing font configuration. Skip it.
+ return ASystemFontIterator_next(ite);
+ }
+ return font.release();
+ }
+}
+
+void ASystemFont_close(ASystemFont* font) {
+ delete font;
+}
+
+const char* ASystemFont_getFontFilePath(const ASystemFont* font) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
+ return font->mFilePath.c_str();
+}
+
+uint16_t ASystemFont_getWeight(const ASystemFont* font) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
+ return font->mWeight;
+}
+
+bool ASystemFont_isItalic(const ASystemFont* font) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
+ return font->mItalic;
+}
+
+const char* ASystemFont_getLocale(const ASystemFont* font) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
+ return font->mLocale ? nullptr : font->mLocale->c_str();
+}
+
+size_t ASystemFont_getCollectionIndex(const ASystemFont* font) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
+ return font->mCollectionIndex;
+}
+
+size_t ASystemFont_getAxisCount(const ASystemFont* font) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
+ return font->mAxes.size();
+}
+
+uint32_t ASystemFont_getAxisTag(const ASystemFont* font, uint32_t axisIndex) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
+ LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
+ "given axis index is out of bounds. (< %zd", font->mAxes.size());
+ return font->mAxes[axisIndex].first;
+}
+
+float ASystemFont_getAxisValue(const ASystemFont* font, uint32_t axisIndex) {
+ LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
+ LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
+ "given axis index is out of bounds. (< %zd", font->mAxes.size());
+ return font->mAxes[axisIndex].second;
+}
diff --git a/native/webview/loader/loader.cpp b/native/webview/loader/loader.cpp
index fee2a25..7f71f63 100644
--- a/native/webview/loader/loader.cpp
+++ b/native/webview/loader/loader.cpp
@@ -26,6 +26,7 @@
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -58,6 +59,7 @@
vsize, strerror(errno));
return JNI_FALSE;
}
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, vsize, "libwebview reservation");
gReservedAddress = addr;
gReservedSize = vsize;
ALOGV("Reserved %zd bytes at %p", vsize, addr);
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 149de13..0ba37ae 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -130,6 +130,22 @@
mProbeSpec = null;
}
+ mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onLost(Network lostNetwork) {
+ // If the network disappears while the app is up, exit.
+ if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
+ }
+ };
+ mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), mNetworkCallback);
+
+ // If the network has disappeared, exit.
+ final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
+ if (networkCapabilities == null) {
+ finishAndRemoveTask();
+ return;
+ }
+
// Also initializes proxy system properties.
mNetwork = mNetwork.getPrivateDnsBypassingCopy();
mCm.bindProcessToNetwork(mNetwork);
@@ -139,24 +155,6 @@
// setContentView initializes the WebView logic which in turn reads the system properties.
setContentView(R.layout.activity_captive_portal_login);
- // Exit app if Network disappears.
- final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
- if (networkCapabilities == null) {
- finishAndRemoveTask();
- return;
- }
- mNetworkCallback = new NetworkCallback() {
- @Override
- public void onLost(Network lostNetwork) {
- if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
- }
- };
- final NetworkRequest.Builder builder = new NetworkRequest.Builder();
- for (int transportType : networkCapabilities.getTransportTypes()) {
- builder.addTransportType(transportType);
- }
- mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
-
getActionBar().setDisplayShowHomeEnabled(false);
getActionBar().setElevation(0); // remove shadow
getActionBar().setTitle(getHeaderTitle());
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index deb6163..9f38d48 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -137,6 +137,7 @@
UID_UNKNOWN);
params.installerPackageName =
getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ params.installReason = PackageManager.INSTALL_REASON_USER;
File file = new File(mPackageURI.getPath());
try {
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 212f398..0ccf13e2 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -107,6 +107,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:elevation="@dimen/preview_controls_elevation"
+ android:tint="?android:attr/textColorPrimaryInverse"
android:background="@drawable/print_button">
</ImageButton>
diff --git a/packages/SettingsLib/HelpUtils/res/values/strings.xml b/packages/SettingsLib/HelpUtils/res/values/strings.xml
index ae07f5d..3e882bc 100644
--- a/packages/SettingsLib/HelpUtils/res/values/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Label for Help and feedback menu item -->
+ <!-- Label for Help and feedback menu item [CHAR LIMIT=45]-->
<string name="help_feedback_label">Help & feedback</string>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index d4c9794..b2f0882 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -2,6 +2,7 @@
name: "SettingsLibRestrictedLockUtils",
srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
libs: [
"androidx.annotation_annotation",
diff --git a/packages/SettingsLib/res/layout/restricted_icon.xml b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
similarity index 76%
rename from packages/SettingsLib/res/layout/restricted_icon.xml
rename to packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
index 724a524..0f02abd 100644
--- a/packages/SettingsLib/res/layout/restricted_icon.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
@@ -15,6 +15,7 @@
-->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/restricted_icon"
- android:layout_width="@dimen/restricted_icon_size"
- android:layout_height="@dimen/restricted_icon_size"
- android:src="@drawable/ic_info" />
\ No newline at end of file
+ android:layout_width="@*android:dimen/config_restricted_icon_size"
+ android:layout_height="@*android:dimen/config_restricted_icon_size"
+ android:tint="?android:attr/colorAccent"
+ android:src="@*android:drawable/ic_info" />
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
new file mode 100644
index 0000000..7e4460b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+ <string name="enabled_by_admin">Enabled by admin</string>
+ <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
+ <string name="disabled_by_admin">Disabled by admin</string>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
index b286df0..e1f6cdf 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_widget.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
@@ -16,9 +16,10 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/restricted_icon"
- android:layout_width="@dimen/restricted_icon_size"
- android:layout_height="@dimen/restricted_icon_size"
- android:src="@drawable/ic_info"
+ android:layout_width="@*android:dimen/config_restricted_icon_size"
+ android:layout_height="@*android:dimen/config_restricted_icon_size"
+ android:tint="?android:attr/colorAccent"
+ android:src="@*android:drawable/ic_info"
android:gravity="end|center_vertical" />
<!-- Based off frameworks/base/core/res/res/layout/preference_widget_switch.xml -->
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
@@ -28,4 +29,4 @@
android:focusable="false"
android:clickable="false"
android:background="@null" />
-</merge>
\ No newline at end of file
+</merge>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 8094b02..a9c5061 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -36,7 +36,6 @@
<dimen name="two_target_pref_medium_icon_size">32dp</dimen>
<!-- Lock icon for preferences locked by admin -->
- <dimen name="restricted_icon_size">16dp</dimen>
<dimen name="restricted_icon_padding">4dp</dimen>
<dimen name="wifi_preference_badge_padding">8dip</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ea6844e..332ced6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -965,11 +965,6 @@
<!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
<string name="disabled_by_admin_summary_text">Controlled by admin</string>
- <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
- <string name="enabled_by_admin">Enabled by admin</string>
- <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
- <string name="disabled_by_admin">Disabled by admin</string>
-
<!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
<string name="disabled">Disabled</string>
<!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
index 3bade25..192a40c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
@@ -26,6 +26,12 @@
import androidx.preference.DialogPreference;
import androidx.preference.PreferenceDialogFragment;
+/**
+ * Framework version is deprecated, use the compat version instead.
+ *
+ * @deprecated
+ */
+@Deprecated
public class CustomDialogPreference extends DialogPreference {
private CustomPreferenceDialogFragment mFragment;
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
index dfaff61..3caa0bb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
@@ -31,6 +31,12 @@
import androidx.preference.EditTextPreference;
import androidx.preference.EditTextPreferenceDialogFragment;
+/**
+ * Framework version is deprecated, use the compat version instead.
+ *
+ * @deprecated
+ */
+@Deprecated
public class CustomEditTextPreference extends EditTextPreference {
private CustomPreferenceDialogFragment mFragment;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 0094c2c..c03ba9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -29,6 +29,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -55,9 +56,15 @@
* @return drawables for displaying with settings that are locked by a device admin.
*/
public static Drawable getRestrictedPadlock(Context context) {
- Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_info);
+ Drawable restrictedPadlock = context.getDrawable(android.R.drawable.ic_info);
final int iconSize = context.getResources().getDimensionPixelSize(
- R.dimen.restricted_icon_size);
+ android.R.dimen.config_restricted_icon_size);
+
+ TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ restrictedPadlock.setTint(colorAccent);
+
restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
return restrictedPadlock;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index e8f47e1..5a64e02 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -211,6 +211,26 @@
}
}
+ /**
+ * Attempts to get the name of a remote device, otherwise returns the address.
+ *
+ * @param device The remote device.
+ * @return The name, or if unavailable, the address.
+ */
+ public String getName(BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = findDevice(device);
+ if (cachedDevice != null && cachedDevice.getName() != null) {
+ return cachedDevice.getName();
+ }
+
+ String name = device.getAliasName();
+ if (name != null) {
+ return name;
+ }
+
+ return device.getAddress();
+ }
+
public synchronized void clearNonBondedDevices() {
mCachedDevicesMapForHearingAids.entrySet().removeIf(entries
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 8c4bff5..c8d4fc8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -29,7 +29,7 @@
import java.util.List;
/**
- * HidProfile handles Bluetooth HID profile.
+ * HidDeviceProfile handles Bluetooth HID Device role
*/
public class HidDeviceProfile implements LocalBluetoothProfile {
private static final String TAG = "HidDeviceProfile";
@@ -37,7 +37,6 @@
private static final int ORDINAL = 18;
// HID Device Profile is always preferred.
private static final int PREFERRED_VALUE = -1;
- private static final boolean DEBUG = true;
private final CachedBluetoothDeviceManager mDeviceManager;
private final LocalBluetoothProfileManager mProfileManager;
@@ -59,9 +58,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (DEBUG) {
- Log.d(TAG,"Bluetooth service connected :-)");
- }
+ Log.d(TAG, "Bluetooth service connected :-), profile:" + profile);
mService = (BluetoothHidDevice) proxy;
// We just bound to the service, so refresh the UI for any connected HID devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,9 +78,7 @@
}
public void onServiceDisconnected(int profile) {
- if (DEBUG) {
- Log.d(TAG, "Bluetooth service disconnected");
- }
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mIsProfileReady = false;
}
}
@@ -110,6 +105,7 @@
@Override
public boolean connect(BluetoothDevice device) {
+ // Don't invoke method in service because settings is not allowed to connect this profile.
return false;
}
@@ -126,11 +122,7 @@
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
}
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
- return !deviceList.isEmpty() && deviceList.contains(device)
- ? mService.getConnectionState(device)
- : BluetoothProfile.STATE_DISCONNECTED;
+ return mService.getConnectionState(device);
}
@Override
@@ -185,9 +177,7 @@
}
protected void finalize() {
- if (DEBUG) {
- Log.d(TAG, "finalize()");
- }
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 7d334eb..7ad2e28c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -32,11 +32,10 @@
import java.util.List;
/**
- * MapClientProfile handles Bluetooth MAP profile.
+ * MapClientProfile handles the Bluetooth MAP MCE role.
*/
public final class MapClientProfile implements LocalBluetoothProfile {
private static final String TAG = "MapClientProfile";
- private static boolean V = false;
private BluetoothMapClient mService;
private boolean mIsProfileReady;
@@ -60,7 +59,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) Log.d(TAG,"Bluetooth service connected");
+ Log.d(TAG, "Bluetooth service connected, profile:" + profile);
mService = (BluetoothMapClient) proxy;
// We just bound to the service, so refresh the UI for any connected MAP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -82,14 +81,14 @@
}
public void onServiceDisconnected(int profile) {
- if (V) Log.d(TAG,"Bluetooth service disconnected");
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mProfileManager.callServiceDisconnectedListeners();
mIsProfileReady=false;
}
}
public boolean isProfileReady() {
- if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+ Log.d(TAG, "isProfileReady(): "+ mIsProfileReady);
return mIsProfileReady;
}
@@ -115,18 +114,16 @@
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> connectedDevices = getConnectedDevices();
- if (connectedDevices != null && connectedDevices.contains(device)) {
- // Connect to same device, Ignore it
- Log.d(TAG,"Ignoring Connect");
- return true;
+ if (mService == null) {
+ return false;
}
return mService.connect(device);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
// Downgrade priority as user is disconnecting.
if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -135,23 +132,30 @@
}
public int getConnectionStatus(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
-
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
return mService.getConnectionState(device);
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return BluetoothProfile.PRIORITY_OFF;
+ }
return mService.getPriority(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
+ if (mService == null) {
+ return;
+ }
if (preferred) {
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -162,7 +166,9 @@
}
public List<BluetoothDevice> getConnectedDevices() {
- if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
return mService.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
@@ -200,11 +206,11 @@
}
protected void finalize() {
- if (V) Log.d(TAG, "finalize()");
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT,
- mService);
+ mService);
mService = null;
}catch (Throwable t) {
Log.w(TAG, "Error cleaning up MAP Client proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index b295f24..8fefb2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -34,7 +34,6 @@
public final class PbapClientProfile implements LocalBluetoothProfile {
private static final String TAG = "PbapClientProfile";
- private static boolean V = false;
private BluetoothPbapClient mService;
private boolean mIsProfileReady;
@@ -56,9 +55,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) {
- Log.d(TAG,"Bluetooth service connected");
- }
+ Log.d(TAG, "Bluetooth service connected, profile:" + profile);
mService = (BluetoothPbapClient) proxy;
// We just bound to the service, so refresh the UI for any connected PBAP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -77,9 +74,7 @@
}
public void onServiceDisconnected(int profile) {
- if (V) {
- Log.d(TAG,"Bluetooth service disconnected");
- }
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mIsProfileReady = false;
}
}
@@ -131,31 +126,16 @@
}
public boolean connect(BluetoothDevice device) {
- if (V) {
- Log.d(TAG,"PBAPClientProfile got connect request");
- }
+ Log.d(TAG,"PBAPClientProfile got connect request");
if (mService == null) {
return false;
}
- List<BluetoothDevice> srcs = getConnectedDevices();
- if (srcs != null) {
- for (BluetoothDevice src : srcs) {
- if (src.equals(device)) {
- // Connect to same device, Ignore it
- Log.d(TAG,"Ignoring Connect");
- return true;
- }
- }
- }
Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
-
return mService.connect(device);
}
public boolean disconnect(BluetoothDevice device) {
- if (V) {
- Log.d(TAG,"PBAPClientProfile got disconnect request");
- }
+ Log.d(TAG,"PBAPClientProfile got disconnect request");
if (mService == null) {
return false;
}
@@ -218,9 +198,7 @@
}
protected void finalize() {
- if (V) {
- Log.d(TAG, "finalize()");
- }
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
index 223c055..60d22a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
@@ -34,8 +34,11 @@
import com.android.settingslib.AppItem;
/**
- * Loader for historical chart data for both network and UID details.
+ * Framework loader is deprecated, use the compat version instead.
+ *
+ * @deprecated
*/
+@Deprecated
public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
private static final String KEY_TEMPLATE = "template";
private static final String KEY_APP = "app";
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
new file mode 100644
index 0000000..a070b2a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.app.usage.NetworkStatsManager;
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * Loader for retrieving the network stats details for all UIDs.
+ */
+public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> {
+
+ private static final String TAG = "NetworkDetailLoader";
+ private final NetworkStatsManager mNetworkStatsManager;
+ private final TelephonyManager mTelephonyManager;
+ private final long mStart;
+ private final long mEnd;
+ private final int mSubId;
+ private final int mNetworkType;
+
+ private NetworkStatsDetailLoader(Builder builder) {
+ super(builder.mContext);
+ mStart = builder.mStart;
+ mEnd = builder.mEnd;
+ mSubId = builder.mSubId;
+ mNetworkType = builder.mNetworkType;
+ mNetworkStatsManager = (NetworkStatsManager)
+ builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
+ mTelephonyManager =
+ (TelephonyManager) builder.mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ public NetworkStats loadInBackground() {
+ try {
+ return mNetworkStatsManager.queryDetails(
+ mNetworkType, mTelephonyManager.getSubscriberId(mSubId), mStart, mEnd);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+
+ public static class Builder {
+ private final Context mContext;
+ private long mStart;
+ private long mEnd;
+ private int mSubId;
+ private int mNetworkType;
+
+ public Builder(Context context) {
+ mContext = context;
+ }
+
+ public Builder setStartTime(long start) {
+ mStart = start;
+ return this;
+ }
+
+ public Builder setEndTime(long end) {
+ mEnd = end;
+ return this;
+ }
+
+ public Builder setSubscriptionId(int subId) {
+ mSubId = subId;
+ return this;
+ }
+
+ public Builder setNetworkType(int networkType) {
+ mNetworkType = networkType;
+ return this;
+ }
+
+ public NetworkStatsDetailLoader build() {
+ return new NetworkStatsDetailLoader(this);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
index 572bae1..649aeff 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
@@ -24,6 +24,12 @@
import android.os.Bundle;
import android.os.RemoteException;
+/**
+ * Framework loader is deprecated, use the compat version instead.
+ *
+ * @deprecated
+ */
+@Deprecated
public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
private static final String KEY_TEMPLATE = "template";
private static final String KEY_START = "start";
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
index c311337..82bb011 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
@@ -25,6 +25,12 @@
import androidx.loader.content.AsyncTaskLoader;
+/**
+ * Deprecated in favor of {@link NetworkStatsDetailLoader}
+ *
+ * @deprecated
+ */
+@Deprecated
public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
private static final String KEY_TEMPLATE = "template";
private static final String KEY_START = "start";
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixin.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixin.java
index 6597daa..b0e987e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixin.java
@@ -32,8 +32,11 @@
import java.util.List;
/**
- * Manages IPC communication to SettingsIntelligence for suggestion related services.
+ * Framework mixin is deprecated, use the compat version instead.
+ *
+ * @deprecated
*/
+@Deprecated
public class SuggestionControllerMixin implements SuggestionController.ServiceConnectionListener,
androidx.lifecycle.LifecycleObserver, LoaderManager.LoaderCallbacks<List<Suggestion>> {
@@ -65,7 +68,7 @@
mContext = context.getApplicationContext();
mHost = host;
mSuggestionController = new SuggestionController(mContext, componentName,
- this /* serviceConnectionListener */);
+ this /* serviceConnectionListener */);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoader.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoader.java
index 9c1af1e..8011424 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoader.java
@@ -24,6 +24,12 @@
import java.util.List;
+/**
+ * Framework loader is deprecated, use the compat version instead.
+ *
+ * @deprecated
+ */
+@Deprecated
public class SuggestionLoader extends AsyncLoader<List<Suggestion>> {
public static final int LOADER_ID_SUGGESTIONS = 42;
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java
index 06770ac..64b9ffe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java
@@ -29,7 +29,9 @@
* This loader is based on the MailAsyncTaskLoader from the AOSP EmailUnified repo.
*
* @param <T> the data type to be loaded.
+ * @deprecated Framework loader is deprecated, use the compat version instead.
*/
+@Deprecated
public abstract class AsyncLoader<T> extends AsyncTaskLoader<T> {
private T mResult;
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixin.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixin.java
index 2987c15..fcf2363 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixin.java
@@ -25,6 +25,12 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.SetPreferenceScreen;
+/**
+ * Framework mixin is deprecated, use the compat version instead.
+ *
+ * @deprecated
+ */
+@Deprecated
public class FooterPreferenceMixin implements LifecycleObserver, SetPreferenceScreen {
private final PreferenceFragment mFragment;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 9050b4b..0dbc037 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -81,7 +81,7 @@
private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS;
/** Maximum age of scan results to hold onto while actively scanning. **/
- private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000;
+ private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
private static final String TAG = "WifiTracker";
private static final boolean DBG() {
@@ -134,8 +134,8 @@
/**
* Tracks whether fresh scan results have been received since scanning start.
*
- * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks
- * so that we do not update the UI with stale data / clear out existing UI elements prematurely.
+ * <p>If this variable is false, we will not invoke callbacks so that we do not
+ * update the UI with stale data / clear out existing UI elements prematurely.
*/
private boolean mStaleScanResults = true;
@@ -327,7 +327,8 @@
* <p>Intended to only be invoked within {@link #onStart()}.
*/
@MainThread
- private void forceUpdate() {
+ @VisibleForTesting
+ void forceUpdate() {
mLastInfo = mWifiManager.getConnectionInfo();
mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
@@ -443,10 +444,8 @@
mScanResultCache.put(newResult.BSSID, newResult);
}
- // Don't evict old results if no new scan results
- if (!mStaleScanResults) {
- evictOldScans();
- }
+ // Evict old results in all conditions
+ evictOldScans();
ArrayMap<String, List<ScanResult>> scanResultsByApKey = new ArrayMap<>();
for (ScanResult result : mScanResultCache.values()) {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 517db78..1860b31 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
@@ -104,6 +103,10 @@
private static final byte SCORE_2 = 15;
private static final int BADGE_2 = AccessPoint.Speed.FAST;
+ private static final String SSID_3 = "ssid3";
+ private static final String BSSID_3 = "CC:00:00:00:00:00";
+ private static final int RSSI_3 = -40;
+
// TODO(b/65594609): Convert mutable Data objects to instance variables / builder pattern
private static final int NETWORK_ID_1 = 123;
private static final int CONNECTED_RSSI = -50;
@@ -255,6 +258,19 @@
SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
}
+ private static ScanResult buildStaleScanResult() {
+ return new ScanResult(
+ WifiSsid.createFromAsciiEncoded(SSID_3),
+ BSSID_3,
+ 0, // hessid
+ 0, //anqpDomainId
+ null, // osuProviders
+ "", // capabilities
+ RSSI_3,
+ 0, // frequency
+ 0 /* microsecond timestamp */);
+ }
+
private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(
Intent ... intents)
throws InterruptedException {
@@ -896,4 +912,18 @@
assertThat(aps.get(0).isReachable()).isTrue();
assertThat(aps.get(1).isReachable()).isTrue();
}
+
+ @Test
+ public void onStart_updateScanResults_evictOldScanResult() {
+ when(mockWifiManager.getScanResults()).thenReturn(
+ Arrays.asList(buildScanResult1(), buildScanResult2(), buildStaleScanResult()));
+ WifiTracker tracker = createMockedWifiTracker();
+
+ tracker.forceUpdate();
+
+ // Only has scanResult1 and scanResult2
+ assertThat(tracker.getAccessPoints()).hasSize(2);
+ assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+ assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index dde1746..0ca2e87 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -52,6 +52,8 @@
@Override
public List<ResourcePath> getIncludedResourcePaths() {
final List<ResourcePath> paths = super.getIncludedResourcePaths();
+ paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
+ paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
paths.add(resourcePath("file:frameworks/base/core/res/res"));
paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
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 8ac611f..7baded8 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
@@ -139,7 +139,7 @@
public void testGetName_validCachedDevice_nameFound() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
- assertThat(mCachedDeviceManager.findDevice(mDevice1).getName()).isEqualTo(DEVICE_ALIAS_1);
+ assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
new file mode 100644
index 0000000..c91ee22
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class HidDeviceProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothHidDevice mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private HidDeviceProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new HidDeviceProfile(RuntimeEnvironment.application,
+ mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, mService);
+ }
+
+ @Test
+ public void connect_shouldReturnFalse() {
+ assertThat(mProfile.connect(mBluetoothDevice)).isFalse();
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothHidDevice() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
new file mode 100644
index 0000000..c4c48a8
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class MapClientProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothMapClient mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private MapClientProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new MapClientProfile(RuntimeEnvironment.application,
+ mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, mService);
+ }
+
+ @Test
+ public void connect_shouldConnectBluetoothMapClient() {
+ mProfile.connect(mBluetoothDevice);
+ verify(mService).connect(mBluetoothDevice);
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothMapClient() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
new file mode 100644
index 0000000..e4a444c
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class PbapClientProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothPbapClient mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private PbapClientProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new PbapClientProfile(RuntimeEnvironment.application,
+ mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, mService);
+ }
+
+ @Test
+ public void connect_shouldConnectBluetoothPbapClient() {
+ mProfile.connect(mBluetoothDevice);
+ verify(mService).connect(mBluetoothDevice);
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothPbapClient() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
\ No newline at end of file
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 a501ffa..40e7386 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
@@ -77,10 +77,10 @@
@Test
public void getIcon_hasIconMetadata_returnIcon() {
- mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_info);
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
- .isEqualTo(R.drawable.ic_info);
+ .isEqualTo(android.R.drawable.ic_info);
}
@Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 290a4f8..63978ba 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2948,7 +2948,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 171;
+ private static final int SETTINGS_VERSION = 172;
private final int mUserId;
@@ -3900,6 +3900,37 @@
currentVersion = 171;
}
+ if (currentVersion == 171) {
+ // Version 171: by default, add STREAM_VOICE_CALL to list of streams that can
+ // be muted.
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ final Setting currentSetting = systemSettings.getSettingLocked(
+ Settings.System.MUTE_STREAMS_AFFECTED);
+ if (!currentSetting.isNull()) {
+ try {
+ int currentSettingIntegerValue = Integer.parseInt(
+ currentSetting.getValue());
+ if ((currentSettingIntegerValue
+ & (1 << AudioManager.STREAM_VOICE_CALL)) == 0) {
+ systemSettings.insertSettingLocked(
+ Settings.System.MUTE_STREAMS_AFFECTED,
+ Integer.toString(
+ currentSettingIntegerValue
+ | (1 << AudioManager.STREAM_VOICE_CALL)),
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ } catch (NumberFormatException e) {
+ // remove the setting in case it is not a valid integer
+ Slog.w("Failed to parse integer value of MUTE_STREAMS_AFFECTED"
+ + "setting, removing setting", e);
+ systemSettings.deleteSettingLocked(
+ Settings.System.MUTE_STREAMS_AFFECTED);
+ }
+
+ }
+ currentVersion = 172;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index b6f51bc..95569dc 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -613,7 +613,7 @@
final String name, final String value, final int userId) throws Exception {
ContentResolver contentResolver = getContext().getContentResolver();
- final Uri settingUri = getBaseUriForType(type);
+ final Uri settingUri = getBaseUriForType(type).buildUpon().appendPath(name).build();
final AtomicBoolean success = new AtomicBoolean();
@@ -640,20 +640,22 @@
final long startTimeMillis = SystemClock.uptimeMillis();
synchronized (mLock) {
- if (success.get()) {
- return;
- }
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- if (elapsedTimeMillis > WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) {
- fail("Could not change setting for "
- + WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + " ms");
- }
- final long remainingTimeMillis = WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS
- - elapsedTimeMillis;
- try {
- mLock.wait(remainingTimeMillis);
- } catch (InterruptedException ie) {
- /* ignore */
+ while (true) {
+ if (success.get()) {
+ return;
+ }
+ final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+ if (elapsedTimeMillis > WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) {
+ fail("Could not change setting for "
+ + WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + " ms");
+ }
+ final long remainingTimeMillis = WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS
+ - elapsedTimeMillis;
+ try {
+ mLock.wait(remainingTimeMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
}
}
} finally {
@@ -689,39 +691,50 @@
}
@Test
- public void testUpdateLocationProvidersAllowedLocked_enableProviders() throws Exception {
- setSettingViaFrontEndApiAndAssertSuccessfulChange(
+ public void testLocationModeChanges_viaFrontEndApi() throws Exception {
+ setStringViaFrontEndApiSetting(
SETTING_TYPE_SECURE,
Settings.Secure.LOCATION_MODE,
String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
UserHandle.USER_SYSTEM);
-
- // Enable one provider
- updateStringViaProviderApiSetting(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+gps");
-
assertEquals(
"Wrong location providers",
- "gps",
- queryStringViaProviderApi(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ "",
+ getStringViaFrontEndApiSetting(
+ SETTING_TYPE_SECURE,
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ UserHandle.USER_SYSTEM));
- // Enable a list of providers, including the one that is already enabled
- updateStringViaProviderApiSetting(
+ setStringViaFrontEndApiSetting(
SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "+gps,+network,+network");
+ Settings.Secure.LOCATION_MODE,
+ String.valueOf(Settings.Secure.LOCATION_MODE_BATTERY_SAVING),
+ UserHandle.USER_SYSTEM);
+ assertEquals(
+ "Wrong location providers",
+ "network",
+ getStringViaFrontEndApiSetting(
+ SETTING_TYPE_SECURE,
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ UserHandle.USER_SYSTEM));
+ setStringViaFrontEndApiSetting(
+ SETTING_TYPE_SECURE,
+ Settings.Secure.LOCATION_MODE,
+ String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY),
+ UserHandle.USER_SYSTEM);
assertEquals(
"Wrong location providers",
"gps,network",
- queryStringViaProviderApi(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+ getStringViaFrontEndApiSetting(
+ SETTING_TYPE_SECURE,
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ UserHandle.USER_SYSTEM));
}
@Test
- public void testUpdateLocationProvidersAllowedLocked_disableProviders() throws Exception {
- setSettingViaFrontEndApiAndAssertSuccessfulChange(
+ public void testLocationProvidersAllowed_disableProviders() throws Exception {
+ setStringViaFrontEndApiSetting(
SETTING_TYPE_SECURE,
Settings.Secure.LOCATION_MODE,
String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY),
@@ -732,7 +745,6 @@
SETTING_TYPE_SECURE,
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
"-gps,-network");
-
assertEquals(
"Wrong location providers",
"",
@@ -744,7 +756,6 @@
SETTING_TYPE_SECURE,
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
"-test");
-
assertEquals(
"Wrong location providers",
"",
@@ -753,8 +764,8 @@
}
@Test
- public void testUpdateLocationProvidersAllowedLocked_enableAndDisable() throws Exception {
- setSettingViaFrontEndApiAndAssertSuccessfulChange(
+ public void testLocationProvidersAllowed_enableAndDisable() throws Exception {
+ setStringViaFrontEndApiSetting(
SETTING_TYPE_SECURE,
Settings.Secure.LOCATION_MODE,
String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
@@ -775,8 +786,8 @@
}
@Test
- public void testUpdateLocationProvidersAllowedLocked_invalidInput() throws Exception {
- setSettingViaFrontEndApiAndAssertSuccessfulChange(
+ public void testLocationProvidersAllowedLocked_invalidInput() throws Exception {
+ setStringViaFrontEndApiSetting(
SETTING_TYPE_SECURE,
Settings.Secure.LOCATION_MODE,
String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e31dd1e..3725940 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -216,6 +216,7 @@
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
+ <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
<application
android:name=".SystemUIApplication"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 680f14b..0b1dab1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -38,11 +38,12 @@
public interface NotificationMenuRowPlugin extends Plugin {
public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_MENU_ROW";
- public static final int VERSION = 4;
+ public static final int VERSION = 5;
@ProvidesInterface(version = OnMenuEventListener.VERSION)
public interface OnMenuEventListener {
public static final int VERSION = 1;
+
public void onMenuClicked(View row, int x, int y, MenuItem menu);
public void onMenuReset(View row);
@@ -53,6 +54,7 @@
@ProvidesInterface(version = MenuItem.VERSION)
public interface MenuItem {
public static final int VERSION = 1;
+
public View getMenuView();
public View getGutsView();
@@ -84,34 +86,136 @@
public void setMenuClickListener(OnMenuEventListener listener);
- public void setSwipeActionHelper(NotificationSwipeActionHelper listener);
-
public void setAppName(String appName);
public void createMenu(ViewGroup parent, StatusBarNotification sbn);
- public View getMenuView();
-
- public boolean isMenuVisible();
-
public void resetMenu();
- public void onTranslationUpdate(float translation);
+ public View getMenuView();
- public void onHeightUpdate();
+ /**
+ * Get the target position that a notification row should be snapped open to in order to reveal
+ * the menu. This is generally determined by the number of icons in the notification menu and the
+ * size of each icon. This method accounts for whether the menu appears on the left or ride side
+ * of the parent notification row.
+ *
- public void onNotificationUpdated(StatusBarNotification sbn);
+ * @return an int representing the x-offset in pixels that the notification should snap open to.
+ * Positive values imply that the notification should be offset to the right to reveal the menu,
+ * and negative alues imply that the notification should be offset to the right.
+ */
+ public int getMenuSnapTarget();
- public boolean onTouchEvent(View view, MotionEvent ev, float velocity);
+ /**
+ * Determines whether or not the menu should be shown in response to user input.
+ * @return true if the menu should be shown, false otherwise.
+ */
+ public boolean shouldShowMenu();
+
+ /**
+ * Determines whether the menu is currently visible.
+ * @return true if the menu is visible, false otherwise.
+ */
+ public boolean isMenuVisible();
+
+ /**
+ * Determines whether a given movement is towards or away from the current location of the menu.
+ * @param movement
+ * @return true if the movement is towards the menu, false otherwise.
+ */
+ public boolean isTowardsMenu(float movement);
+
+ /**
+ * Determines whether the menu should snap closed instead of dismissing the
+ * parent notification, as a function of its current state.
+ *
+ * @return true if the menu should snap closed, false otherwise.
+ */
+ public boolean shouldSnapBack();
+
+ /**
+ * Determines whether the menu was previously snapped open to the same side that it is currently
+ * being shown on.
+ * @return true if the menu is snapped open to the same side on which it currently appears,
+ * false otherwise.
+ */
+ public boolean isSnappedAndOnSameSide();
+
+ /**
+ * Determines whether the notification the menu is attached to is able to be dismissed.
+ * @return true if the menu's parent notification is dismissable, false otherwise.
+ */
+ public boolean canBeDismissed();
+
+ /**
+ * Determines whether the menu should remain open given its current state, or snap closed.
+ * @return true if the menu should remain open, false otherwise.
+ */
+ public boolean isWithinSnapMenuThreshold();
+
+ /**
+ * Determines whether the menu has been swiped far enough to snap open.
+ * @return true if the menu has been swiped far enough to open, false otherwise.
+ */
+ public boolean isSwipedEnoughToShowMenu();
public default boolean onInterceptTouchEvent(View view, MotionEvent ev) {
return false;
}
- public default boolean useDefaultMenuItems() {
+ public default boolean shouldUseDefaultMenuItems() {
return false;
}
- public default void onConfigurationChanged() {
- }
+ /**
+ * Callback used to signal the menu that its parent's translation has changed.
+ * @param translation The new x-translation of the menu as a position (not an offset).
+ */
+ public void onParentTranslationUpdate(float translation);
+
+ /**
+ * Callback used to signal the menu that its parent's height has changed.
+ */
+ public void onParentHeightUpdate();
+
+ /**
+ * Callback used to signal the menu that its parent notification has been updated.
+ * @param sbn
+ */
+ public void onNotificationUpdated(StatusBarNotification sbn);
+
+ /**
+ * Callback used to signal the menu that a user is moving the parent notification.
+ * @param delta The change in the parent notification's position.
+ */
+ public void onTouchMove(float delta);
+
+ /**
+ * Callback used to signal the menu that a user has begun touching its parent notification.
+ */
+ public void onTouchStart();
+
+ /**
+ * Callback used to signal the menu that a user has finished touching its parent notification.
+ */
+ public void onTouchEnd();
+
+ /**
+ * Callback used to signal the menu that it has been snapped closed.
+ */
+ public void onSnapClosed();
+
+ /**
+ * Callback used to signal the menu that it has been snapped open.
+ */
+ public void onSnapOpen();
+
+ /**
+ * Callback used to signal the menu that its parent notification has been dismissed.
+ */
+ public void onDismiss();
+
+ public default void onConfigurationChanged() { }
+
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
index f6cf035..8db0d02 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
@@ -39,7 +39,7 @@
/**
* Call this to snap a notification to provided {@code targetLeft}.
*/
- public void snap(View animView, float velocity, float targetLeft);
+ public void snapOpen(View animView, int targetLeft, float velocity);
/**
* Call this to snooze a notification based on the provided {@link SnoozeOption}.
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
similarity index 80%
rename from packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml
rename to packages/SystemUI/res/drawable/biometric_dialog_bg.xml
index 221f170..335448d 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml
+++ b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
@@ -17,10 +17,10 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/fingerprint_dialog_bg_color" />
+ <solid android:color="@color/biometric_dialog_bg_color" />
<corners android:radius="1dp"
- android:topLeftRadius="@dimen/fingerprint_dialog_corner_size"
- android:topRightRadius="@dimen/fingerprint_dialog_corner_size"
+ android:topLeftRadius="@dimen/biometric_dialog_corner_size"
+ android:topRightRadius="@dimen/biometric_dialog_corner_size"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"/>
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_icon.xml b/packages/SystemUI/res/drawable/face_dialog_icon.xml
new file mode 100644
index 0000000..6d28b5a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/face_dialog_icon.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
index 83c1949..05fd467 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
@@ -36,7 +36,7 @@
android:name="_R_G_L_2_G_D_0_P_0"
android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -47,7 +47,7 @@
android:name="_R_G_L_2_G_D_1_P_0"
android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -58,7 +58,7 @@
android:name="_R_G_L_2_G_D_2_P_0"
android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -69,7 +69,7 @@
android:name="_R_G_L_2_G_D_3_P_0"
android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -80,7 +80,7 @@
android:name="_R_G_L_2_G_D_4_P_0"
android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -97,7 +97,7 @@
android:name="_R_G_L_1_G_D_0_P_0"
android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="2"
@@ -118,7 +118,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_0"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
</group>
@@ -131,7 +131,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_1"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
</group>
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
index f682f87..fd0ab22 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
@@ -36,7 +36,7 @@
android:name="_R_G_L_3_G_D_0_P_0"
android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -47,7 +47,7 @@
android:name="_R_G_L_3_G_D_1_P_0"
android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -58,7 +58,7 @@
android:name="_R_G_L_3_G_D_2_P_0"
android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -69,7 +69,7 @@
android:name="_R_G_L_3_G_D_3_P_0"
android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -80,7 +80,7 @@
android:name="_R_G_L_3_G_D_4_P_0"
android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_fingerprint_color"
+ android:strokeColor="@color/biometric_dialog_biometric_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -101,7 +101,7 @@
android:name="_R_G_L_2_G_D_0_P_0"
android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -112,7 +112,7 @@
android:name="_R_G_L_2_G_D_1_P_0"
android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -123,7 +123,7 @@
android:name="_R_G_L_2_G_D_2_P_0"
android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -134,7 +134,7 @@
android:name="_R_G_L_2_G_D_3_P_0"
android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -145,7 +145,7 @@
android:name="_R_G_L_2_G_D_4_P_0"
android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="1.45"
@@ -162,7 +162,7 @@
android:name="_R_G_L_1_G_D_0_P_0"
android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
android:strokeAlpha="1"
- android:strokeColor="@color/fingerprint_dialog_error_color"
+ android:strokeColor="@color/biometric_dialog_error_color"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:strokeWidth="2"
@@ -183,7 +183,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_0"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
</group>
@@ -196,7 +196,7 @@
<path
android:name="_R_G_L_0_G_D_0_P_1"
android:fillAlpha="1"
- android:fillColor="@color/fingerprint_dialog_error_color"
+ android:fillColor="@color/biometric_dialog_error_color"
android:fillType="nonZero"
android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
</group>
diff --git a/packages/SystemUI/res/layout/fingerprint_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
similarity index 78%
rename from packages/SystemUI/res/layout/fingerprint_dialog.xml
rename to packages/SystemUI/res/layout/biometric_dialog.xml
index 1bdaf6e..0417e2e 100644
--- a/packages/SystemUI/res/layout/fingerprint_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -19,7 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
- android:background="@color/fingerprint_dialog_dim_color"
+ android:background="@color/biometric_dialog_dim_color"
android:orientation="vertical">
<!-- This is not a Space since Spaces cannot be clicked -->
@@ -47,7 +47,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:elevation="2dp"
- android:background="@drawable/fingerprint_dialog_bg">
+ android:background="@drawable/biometric_dialog_bg">
<TextView
android:id="@+id/title"
@@ -57,13 +57,13 @@
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
- android:gravity="@integer/fingerprint_dialog_text_gravity"
+ android:gravity="@integer/biometric_dialog_text_gravity"
android:textSize="20sp"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
- android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+ android:textColor="@color/biometric_dialog_text_dark_color"/>
<TextView
android:id="@+id/subtitle"
@@ -72,13 +72,13 @@
android:layout_marginTop="8dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
- android:gravity="@integer/fingerprint_dialog_text_gravity"
+ android:gravity="@integer/biometric_dialog_text_gravity"
android:textSize="16sp"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
- android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+ android:textColor="@color/biometric_dialog_text_dark_color"/>
<TextView
android:id="@+id/description"
@@ -86,20 +86,19 @@
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
- android:gravity="@integer/fingerprint_dialog_text_gravity"
+ android:gravity="@integer/biometric_dialog_text_gravity"
android:paddingTop="8dp"
android:textSize="16sp"
android:maxLines="4"
- android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+ android:textColor="@color/biometric_dialog_text_dark_color"/>
<ImageView
- android:id="@+id/fingerprint_icon"
- android:layout_width="@dimen/fingerprint_dialog_fp_icon_size"
- android:layout_height="@dimen/fingerprint_dialog_fp_icon_size"
+ android:id="@+id/biometric_icon"
+ android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
+ android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
android:layout_gravity="center_horizontal"
android:layout_marginTop="48dp"
- android:scaleType="fitXY"
- android:contentDescription="@string/accessibility_fingerprint_dialog_fingerprint_icon" />
+ android:scaleType="fitXY" />
<TextView
android:id="@+id/error"
@@ -112,9 +111,8 @@
android:textSize="12sp"
android:gravity="center_horizontal"
android:accessibilityLiveRegion="polite"
- android:text="@string/fingerprint_dialog_touch_sensor"
- android:contentDescription="@string/accessibility_fingerprint_dialog_help_area"
- android:textColor="@color/fingerprint_dialog_text_light_color"/>
+ android:contentDescription="@string/accessibility_biometric_dialog_help_area"
+ android:textColor="@color/biometric_dialog_text_light_color"/>
<LinearLayout
android:layout_width="match_parent"
@@ -125,7 +123,7 @@
android:orientation="horizontal"
android:measureWithLargestChild="true">
<Space android:id="@+id/leftSpacer"
- android:layout_width="24dp"
+ android:layout_width="12dp"
android:layout_height="match_parent"
android:visibility="visible" />
<!-- Negative Button -->
@@ -133,20 +131,26 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_marginStart="-12dp"
- android:gravity="start|center_vertical"
+ android:gravity="center"
android:maxLines="2" />
+ <Space android:id="@+id/middleSpacer"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="visible" />
<!-- Positive Button -->
<Button android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_marginEnd="12dp"
- android:maxLines="2" />
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:gravity="center"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_confirm"
+ android:visibility="gone"/>
<Space android:id="@+id/rightSpacer"
- android:layout_width="24dip"
+ android:layout_width="12dip"
android:layout_height="match_parent"
- android:visibility="gone" />
+ android:visibility="visible" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index b047efb..8a3a0b1 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -19,7 +19,6 @@
android:id="@+id/menu_container"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
- android:focusable="false"
android:importantForAccessibility="no"
>
<!-- Use nav button width & height=match_parent for parent FrameLayout and buttons because they
diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index 07f0c83..a8960d9 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tile_page"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:paddingStart="@dimen/notification_side_paddings"
android:paddingEnd="@dimen/notification_side_paddings"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index e96a09b..11a0187 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -19,6 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom">
diff --git a/packages/SystemUI/res/layout/recents_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
index adf1e74..2538612 100644
--- a/packages/SystemUI/res/layout/recents_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -37,7 +37,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
- android:textColor="@android:color/white"
+ android:textColor="?attr/wallpaperTextColor"
android:textSize="16sp"/>
<ImageView
android:id="@+id/dismiss"
@@ -49,6 +49,7 @@
android:layout_marginEnd="2dp"
android:alpha="0.7"
android:src="@drawable/ic_close_white"
+ android:tint="?attr/wallpaperTextColor"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/accessibility_desc_close"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-h600dp/config.xml b/packages/SystemUI/res/values-h600dp/config.xml
deleted file mode 100644
index 8616e3e..0000000
--- a/packages/SystemUI/res/values-h600dp/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
--->
-
-<resources>
- <!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">3</integer>
-</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4920fb2..d1320a3 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -160,13 +160,13 @@
<color name="smart_reply_button_background">#ffffffff</color>
<color name="smart_reply_button_stroke">#ffdadce0</color>
- <!-- Fingerprint dialog colors -->
- <color name="fingerprint_dialog_bg_color">#ffffffff</color> <!-- 100% white -->
- <color name="fingerprint_dialog_text_dark_color">#dd000000</color> <!-- 87% black -->
- <color name="fingerprint_dialog_text_light_color">#89000000</color> <!-- 54% black -->
- <color name="fingerprint_dialog_dim_color">#80000000</color> <!-- 50% black -->
- <color name="fingerprint_dialog_error_color">#fff44336</color> <!-- red -->
- <color name="fingerprint_dialog_fingerprint_color">#ff008577</color> <!-- teal -->
+ <!-- Biometric dialog colors -->
+ <color name="biometric_dialog_bg_color">#ffffffff</color> <!-- 100% white -->
+ <color name="biometric_dialog_text_dark_color">#dd000000</color> <!-- 87% black -->
+ <color name="biometric_dialog_text_light_color">#89000000</color> <!-- 54% black -->
+ <color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black -->
+ <color name="biometric_dialog_error_color">#fff44336</color> <!-- red -->
+ <color name="biometric_dialog_biometric_color">#ff008577</color> <!-- teal -->
<!-- Logout button -->
<color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 11bd392..24dcd3e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
<integer name="quick_settings_num_columns">3</integer>
<!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_num_rows">1</integer>
+ <integer name="quick_settings_max_rows">3</integer>
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
@@ -146,19 +146,29 @@
<!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
<bool name="config_hideLtePlus">false</bool>
- <!-- milliseconds before the heads up notification auto-dismisses. -->
+ <!-- The number of milliseconds before the heads up notification auto-dismisses. -->
<integer name="heads_up_notification_decay">5000</integer>
- <!-- milliseconds after a heads up notification is pushed back
+ <!-- The number of milliseconds after a heads up notification is pushed back
before the app can interrupt again. -->
<integer name="heads_up_default_snooze_length_ms">60000</integer>
<!-- Minimum display time for a heads up notification, in milliseconds. -->
<integer name="heads_up_notification_minimum_time">2000</integer>
- <!-- milliseconds before the heads up notification accepts touches. -->
+ <!-- The number of milliseconds before the heads up notification accepts touches. -->
<integer name="touch_acceptance_delay">700</integer>
+ <!-- The number of milliseconds before the ambient notification auto-dismisses. This will
+ override the default pulse length. -->
+ <integer name="ambient_notification_decay">6000</integer>
+
+ <!-- Minimum display time for a heads up notification, in milliseconds. -->
+ <integer name="ambient_notification_minimum_time">2000</integer>
+
+ <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) -->
+ <integer name="ambient_notification_extension_time">6000</integer>
+
<!-- The duration in seconds to wait before the dismiss buttons are shown. -->
<integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 26eadb5..8ac3aa1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -955,7 +955,7 @@
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
<dimen name="nav_quick_scrub_track_thickness">10dp</dimen>
- <dimen name="nav_home_back_gesture_drag_limit">60dp</dimen>
+ <dimen name="nav_home_back_gesture_drag_limit">40dp</dimen>
<!-- Navigation bar shadow params. -->
<dimen name="nav_key_button_shadow_offset_x">0dp</dimen>
@@ -982,10 +982,10 @@
the regular notification, when we have remote input history texts present. -->
<dimen name="remote_input_history_extra_height">60dp</dimen>
- <!-- Fingerprint Dialog values -->
- <dimen name="fingerprint_dialog_fp_icon_size">64dp</dimen>
- <dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
- <dimen name="fingerprint_dialog_corner_size">4dp</dimen>
+ <!-- Biometric Dialog values -->
+ <dimen name="biometric_dialog_biometric_icon_size">64dp</dimen>
+ <dimen name="biometric_dialog_corner_size">4dp</dimen>
+ <dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
<!-- Wireless Charging Animation values -->
<dimen name="wireless_charging_dots_radius_start">0dp</dimen>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index 87c4bbb..fd7a105 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
<resources>
- <integer name="fingerprint_dialog_text_gravity">8388611</integer> <!-- gravity start -->
+ <integer name="biometric_dialog_text_gravity">8388611</integer> <!-- gravity start -->
<!-- Action footer width used for layout_width to indicate WRAP_CONTENT (along with a weight of
0) as we can allow the carrier text to stretch as far as needed in the QS footer. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 36f97cd..a3306d3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -257,14 +257,20 @@
<!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
<string name="cancel">Cancel</string>
+ <!-- Content description for the error/help message are when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_biometric_dialog_help_area">Help message area</string>
+ <!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
+ <string name="biometric_dialog_confirm">Confirm</string>
+
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string>
- <!-- Content description of the application icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_fingerprint_dialog_app_icon">Application icon</string>
- <!-- Content description for the error/help message are when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_fingerprint_dialog_help_area">Help message area</string>
+
+ <!-- Message shown when the system-provided face dialog is shown, asking for authentication [CHAR LIMIT=30] -->
+ <string name="face_dialog_looking_for_face">Looking for you\u2026</string>
+ <!-- Content description of the face icon when the system-provided face dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_face_dialog_face_icon">Face icon</string>
<!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 04d72ce..258b6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,6 +30,7 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -137,6 +138,7 @@
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
+ providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
providers.put(NotificationBlockingHelperManager.class,
() -> new NotificationBlockingHelperManager(context));
providers.put(NotificationRemoteInputManager.class,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 8fe577a..8fc4689 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.os.Bundle;
@@ -31,9 +32,12 @@
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.CommandQueue;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
- * FingerprintDialogView).
+ * BiometricDialogView).
*/
public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
private static final String TAG = "BiometricDialogImpl";
@@ -48,7 +52,8 @@
private static final int MSG_USER_CANCELED = 7;
private static final int MSG_BUTTON_POSITIVE = 8;
- private FingerprintDialogView mDialogView;
+ private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
+ private BiometricDialogView mCurrentDialog;
private WindowManager mWindowManager;
private IBiometricPromptReceiver mReceiver;
private boolean mDialogShowing;
@@ -111,16 +116,25 @@
@Override
public void start() {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- return;
+ final PackageManager pm = mContext.getPackageManager();
+ mDialogs = new HashMap<>();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ mDialogs.put(BiometricAuthenticator.TYPE_FACE, new FaceDialogView(mContext, mCallback));
}
- getComponent(CommandQueue.class).addCallbacks(this);
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mDialogView = new FingerprintDialogView(mContext, mCallback);
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ mDialogs.put(BiometricAuthenticator.TYPE_FINGERPRINT,
+ new FingerprintDialogView(mContext, mCallback));
+ }
+
+ if (!mDialogs.isEmpty()) {
+ getComponent(CommandQueue.class).addCallbacks(this);
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation) {
if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
// Remove these messages as they are part of the previous client
mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
@@ -129,6 +143,8 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
+ args.argi1 = type;
+ args.arg3 = requireConfirmation;
mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
}
@@ -157,33 +173,41 @@
}
private void handleShowDialog(SomeArgs args) {
+ final int type = args.argi1;
+ mCurrentDialog = mDialogs.get(type);
+
if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
- + mDialogView.isAnimatingAway());
- if (mDialogView.isAnimatingAway()) {
- mDialogView.forceRemove();
+ + mCurrentDialog.isAnimatingAway() + " type: " + type);
+
+ if (mCurrentDialog.isAnimatingAway()) {
+ mCurrentDialog.forceRemove();
} else if (mDialogShowing) {
Log.w(TAG, "Dialog already showing");
return;
}
mReceiver = (IBiometricPromptReceiver) args.arg2;
- mDialogView.setBundle((Bundle)args.arg1);
- mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
+ mCurrentDialog.setBundle((Bundle)args.arg1);
+ mCurrentDialog.setRequireConfirmation((boolean)args.arg3);
+ mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
mDialogShowing = true;
}
private void handleBiometricAuthenticated() {
if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
- // TODO: announce correct string depending on modality
- mDialogView.announceForAccessibility(
- mContext.getResources().getText(
- com.android.internal.R.string.fingerprint_authenticated));
- handleHideDialog(false /* userCanceled */);
+ mCurrentDialog.announceForAccessibility(
+ mContext.getResources()
+ .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
+ if (mCurrentDialog.requiresConfirmation()) {
+ mCurrentDialog.showConfirmationButton();
+ } else {
+ handleHideDialog(false /* userCanceled */);
+ }
}
private void handleBiometricHelp(String message) {
if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
- mDialogView.showHelpMessage(message);
+ mCurrentDialog.showHelpMessage(message);
}
private void handleBiometricError(String error) {
@@ -192,7 +216,7 @@
if (DEBUG) Log.d(TAG, "Dialog already dismissed");
return;
}
- mDialogView.showErrorMessage(error);
+ mCurrentDialog.showErrorMessage(error);
}
private void handleHideDialog(boolean userCanceled) {
@@ -212,7 +236,7 @@
}
mReceiver = null;
mDialogShowing = false;
- mDialogView.startDismiss();
+ mCurrentDialog.startDismiss();
}
private void handleButtonNegative() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
new file mode 100644
index 0000000..c90861e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+/**
+ * Abstract base class. Shows a dialog for BiometricPrompt.
+ */
+public abstract class BiometricDialogView extends LinearLayout {
+
+ private static final String TAG = "BiometricDialogView";
+
+ private static final int ANIMATION_DURATION_SHOW = 250; // ms
+ private static final int ANIMATION_DURATION_AWAY = 350; // ms
+
+ private static final int MSG_CLEAR_MESSAGE = 1;
+
+ protected static final int STATE_NONE = 0;
+ protected static final int STATE_AUTHENTICATING = 1;
+ protected static final int STATE_ERROR = 2;
+ protected static final int STATE_AUTHENTICATED = 3;
+
+ private final IBinder mWindowToken = new Binder();
+ private final Interpolator mLinearOutSlowIn;
+ private final WindowManager mWindowManager;
+ private final float mAnimationTranslationOffset;
+ private final int mErrorColor;
+ private final int mTextColor;
+ private final float mDisplayWidth;
+ private final DialogViewCallback mCallback;
+
+ private ViewGroup mLayout;
+ private final TextView mErrorText;
+ private Bundle mBundle;
+ private final LinearLayout mDialog;
+ private int mLastState;
+ private boolean mAnimatingAway;
+ private boolean mWasForceRemoved;
+ protected boolean mRequireConfirmation;
+
+ protected abstract void updateIcon(int lastState, int newState);
+ protected abstract int getHintStringResourceId();
+ protected abstract int getAuthenticatedAccessibilityResourceId();
+ protected abstract int getIconDescriptionResourceId();
+
+ private final Runnable mShowAnimationRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLayout.animate()
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_SHOW)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ mDialog.animate()
+ .translationY(0)
+ .setDuration(ANIMATION_DURATION_SHOW)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
+ };
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_CLEAR_MESSAGE:
+ handleClearMessage();
+ break;
+ default:
+ Log.e(TAG, "Unhandled message: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ public BiometricDialogView(Context context, DialogViewCallback callback) {
+ super(context);
+ mCallback = callback;
+ mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mAnimationTranslationOffset = getResources()
+ .getDimension(R.dimen.biometric_dialog_animation_translation_offset);
+ mErrorColor = Color.parseColor(
+ getResources().getString(R.color.biometric_dialog_error_color));
+ mTextColor = Color.parseColor(
+ getResources().getString(R.color.biometric_dialog_text_light_color));
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mWindowManager.getDefaultDisplay().getMetrics(metrics);
+ mDisplayWidth = metrics.widthPixels;
+
+ // Create the dialog
+ LayoutInflater factory = LayoutInflater.from(getContext());
+ mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false);
+ addView(mLayout);
+
+ mDialog = mLayout.findViewById(R.id.dialog);
+
+ mErrorText = mLayout.findViewById(R.id.error);
+
+ mLayout.setOnKeyListener(new View.OnKeyListener() {
+ boolean downPressed = false;
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode != KeyEvent.KEYCODE_BACK) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
+ downPressed = true;
+ } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ downPressed = false;
+ } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
+ downPressed = false;
+ mCallback.onUserCanceled();
+ }
+ return true;
+ }
+ });
+
+ final View space = mLayout.findViewById(R.id.space);
+ final View leftSpace = mLayout.findViewById(R.id.left_space);
+ final View rightSpace = mLayout.findViewById(R.id.right_space);
+ final Button negative = mLayout.findViewById(R.id.button2);
+ final Button positive = mLayout.findViewById(R.id.button1);
+ final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
+
+ icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
+ mErrorText.setText(getResources().getString(getHintStringResourceId()));
+
+ setDismissesDialog(space);
+ setDismissesDialog(leftSpace);
+ setDismissesDialog(rightSpace);
+
+ negative.setOnClickListener((View v) -> {
+ mCallback.onNegativePressed();
+ });
+
+ positive.setOnClickListener((View v) -> {
+ mCallback.onPositivePressed();
+ });
+
+ mLayout.setFocusableInTouchMode(true);
+ mLayout.requestFocus();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ final TextView title = mLayout.findViewById(R.id.title);
+ final TextView subtitle = mLayout.findViewById(R.id.subtitle);
+ final TextView description = mLayout.findViewById(R.id.description);
+ final Button negative = mLayout.findViewById(R.id.button2);
+ final Button positive = mLayout.findViewById(R.id.button1);
+
+ mDialog.getLayoutParams().width = (int) mDisplayWidth;
+
+ mLastState = STATE_NONE;
+ updateState(STATE_AUTHENTICATING);
+
+ title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
+ title.setSelected(true);
+
+ positive.setVisibility(View.INVISIBLE);
+
+ final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
+ if (TextUtils.isEmpty(subtitleText)) {
+ subtitle.setVisibility(View.GONE);
+ } else {
+ subtitle.setVisibility(View.VISIBLE);
+ subtitle.setText(subtitleText);
+ }
+
+ final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+ if (TextUtils.isEmpty(descriptionText)) {
+ description.setVisibility(View.GONE);
+ } else {
+ description.setVisibility(View.VISIBLE);
+ description.setText(descriptionText);
+ }
+
+ negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+
+ if (!mWasForceRemoved) {
+ // Dim the background and slide the dialog up
+ mDialog.setTranslationY(mAnimationTranslationOffset);
+ mLayout.setAlpha(0f);
+ postOnAnimation(mShowAnimationRunnable);
+ } else {
+ // Show the dialog immediately
+ mLayout.animate().cancel();
+ mDialog.animate().cancel();
+ mDialog.setAlpha(1.0f);
+ mDialog.setTranslationY(0);
+ mLayout.setAlpha(1.0f);
+ }
+ mWasForceRemoved = false;
+ }
+
+ private void setDismissesDialog(View v) {
+ v.setClickable(true);
+ v.setOnTouchListener((View view, MotionEvent event) -> {
+ mCallback.onUserCanceled();
+ return true;
+ });
+ }
+
+ public void startDismiss() {
+ mAnimatingAway = true;
+
+ final Runnable endActionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mWindowManager.removeView(BiometricDialogView.this);
+ mAnimatingAway = false;
+ }
+ };
+
+ postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mLayout.animate()
+ .alpha(0f)
+ .setDuration(ANIMATION_DURATION_AWAY)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ mDialog.animate()
+ .translationY(mAnimationTranslationOffset)
+ .setDuration(ANIMATION_DURATION_AWAY)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .withEndAction(endActionRunnable)
+ .start();
+ }
+ });
+ }
+
+ /**
+ * Force remove the window, cancelling any animation that's happening. This should only be
+ * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
+ * will cause the dialog to show without an animation the next time it's attached.
+ */
+ public void forceRemove() {
+ mLayout.animate().cancel();
+ mDialog.animate().cancel();
+ mWindowManager.removeView(BiometricDialogView.this);
+ mAnimatingAway = false;
+ mWasForceRemoved = true;
+ }
+
+ public boolean isAnimatingAway() {
+ return mAnimatingAway;
+ }
+
+ public void setBundle(Bundle bundle) {
+ mBundle = bundle;
+ }
+
+ public void setRequireConfirmation(boolean requireConfirmation) {
+ mRequireConfirmation = requireConfirmation;
+ }
+
+ public boolean requiresConfirmation() {
+ return mRequireConfirmation;
+ }
+
+ public void showConfirmationButton() {
+ final Button positive = mLayout.findViewById(R.id.button1);
+ positive.setVisibility(View.VISIBLE);
+ }
+
+ public ViewGroup getLayout() {
+ return mLayout;
+ }
+
+ // Clears the temporary message and shows the help message.
+ private void handleClearMessage() {
+ updateState(STATE_AUTHENTICATING);
+ mErrorText.setText(getHintStringResourceId());
+ mErrorText.setTextColor(mTextColor);
+ }
+
+ // Shows an error/help message
+ private void showTemporaryMessage(String message) {
+ mHandler.removeMessages(MSG_CLEAR_MESSAGE);
+ updateState(STATE_ERROR);
+ mErrorText.setText(message);
+ mErrorText.setTextColor(mErrorColor);
+ mErrorText.setContentDescription(message);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
+ BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+
+ public void showHelpMessage(String message) {
+ showTemporaryMessage(message);
+ }
+
+ public void showErrorMessage(String error) {
+ showTemporaryMessage(error);
+ mCallback.onErrorShown();
+ }
+
+ private void updateState(int newState) {
+ updateIcon(mLastState, newState);
+ mLastState = newState;
+ }
+
+ public WindowManager.LayoutParams getLayoutParams() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("BiometricDialogView");
+ lp.token = mWindowToken;
+ return lp;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
new file mode 100644
index 0000000..feef3a6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * This class loads the view for the system-provided dialog. The view consists of:
+ * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * and positive/negative buttons.
+ */
+public class FaceDialogView extends BiometricDialogView {
+ public FaceDialogView(Context context,
+ DialogViewCallback callback) {
+ super(context, callback);
+ }
+
+ @Override
+ protected int getHintStringResourceId() {
+ return R.string.face_dialog_looking_for_face;
+ }
+
+ @Override
+ protected int getAuthenticatedAccessibilityResourceId() {
+ if (mRequireConfirmation) {
+ return com.android.internal.R.string.face_authenticated_confirmation_required;
+ } else {
+ return com.android.internal.R.string.face_authenticated_no_confirmation_required;
+ }
+ }
+
+ @Override
+ protected int getIconDescriptionResourceId() {
+ return R.string.accessibility_face_dialog_face_icon;
+ }
+
+ @Override
+ protected void updateIcon(int lastState, int newState) {
+ Drawable icon = mContext.getDrawable(R.drawable.face_dialog_icon);
+
+ final ImageView faceIcon = getLayout().findViewById(R.id.biometric_icon);
+ faceIcon.setImageDrawable(icon);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 68c2c42..38a69a9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -17,32 +17,11 @@
package com.android.systemui.biometrics;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.widget.Button;
import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
@@ -50,288 +29,27 @@
* Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
* and positive/negative buttons.
*/
-public class FingerprintDialogView extends LinearLayout {
-
+public class FingerprintDialogView extends BiometricDialogView {
private static final String TAG = "FingerprintDialogView";
- private static final int ANIMATION_DURATION_SHOW = 250; // ms
- private static final int ANIMATION_DURATION_AWAY = 350; // ms
-
- private static final int MSG_CLEAR_MESSAGE = 1;
-
- private static final int STATE_NONE = 0;
- private static final int STATE_FINGERPRINT = 1;
- private static final int STATE_FINGERPRINT_ERROR = 2;
- private static final int STATE_FINGERPRINT_AUTHENTICATED = 3;
-
- private final IBinder mWindowToken = new Binder();
- private final Interpolator mLinearOutSlowIn;
- private final WindowManager mWindowManager;
- private final float mAnimationTranslationOffset;
- private final int mErrorColor;
- private final int mTextColor;
- private final int mFingerprintColor;
- private final float mDisplayWidth;
- private final DialogViewCallback mCallback;
-
- private ViewGroup mLayout;
- private final TextView mErrorText;
- private Bundle mBundle;
- private final LinearLayout mDialog;
- private int mLastState;
- private boolean mAnimatingAway;
- private boolean mWasForceRemoved;
-
- private final Runnable mShowAnimationRunnable = new Runnable() {
- @Override
- public void run() {
- mLayout.animate()
- .alpha(1f)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- mDialog.animate()
- .translationY(0)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- }
- };
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_CLEAR_MESSAGE:
- handleClearMessage();
- break;
- default:
- Log.e(TAG, "Unhandled message: " + msg.what);
- break;
- }
- }
- };
-
- public FingerprintDialogView(Context context, DialogViewCallback callback) {
- super(context);
- mCallback = callback;
- mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mAnimationTranslationOffset = getResources()
- .getDimension(R.dimen.fingerprint_dialog_animation_translation_offset);
- mErrorColor = Color.parseColor(
- getResources().getString(R.color.fingerprint_dialog_error_color));
- mTextColor = Color.parseColor(
- getResources().getString(R.color.fingerprint_dialog_text_light_color));
- mFingerprintColor = Color.parseColor(
- getResources().getString(R.color.fingerprint_dialog_fingerprint_color));
-
- DisplayMetrics metrics = new DisplayMetrics();
- mWindowManager.getDefaultDisplay().getMetrics(metrics);
- mDisplayWidth = metrics.widthPixels;
-
- // Create the dialog
- LayoutInflater factory = LayoutInflater.from(getContext());
- mLayout = (ViewGroup) factory.inflate(R.layout.fingerprint_dialog, this, false);
- addView(mLayout);
-
- mDialog = mLayout.findViewById(R.id.dialog);
-
- mErrorText = mLayout.findViewById(R.id.error);
-
- mLayout.setOnKeyListener(new View.OnKeyListener() {
- boolean downPressed = false;
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode != KeyEvent.KEYCODE_BACK) {
- return false;
- }
- if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
- downPressed = true;
- } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
- downPressed = false;
- } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
- downPressed = false;
- mCallback.onUserCanceled();
- }
- return true;
- }
- });
-
- final View space = mLayout.findViewById(R.id.space);
- final View leftSpace = mLayout.findViewById(R.id.left_space);
- final View rightSpace = mLayout.findViewById(R.id.right_space);
- final Button negative = mLayout.findViewById(R.id.button2);
- final Button positive = mLayout.findViewById(R.id.button1);
-
- setDismissesDialog(space);
- setDismissesDialog(leftSpace);
- setDismissesDialog(rightSpace);
-
- negative.setOnClickListener((View v) -> {
- mCallback.onNegativePressed();
- });
-
- positive.setOnClickListener((View v) -> {
- mCallback.onPositivePressed();
- });
-
- mLayout.setFocusableInTouchMode(true);
- mLayout.requestFocus();
+ @Override
+ protected int getHintStringResourceId() {
+ return R.string.fingerprint_dialog_touch_sensor;
}
@Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- final TextView title = mLayout.findViewById(R.id.title);
- final TextView subtitle = mLayout.findViewById(R.id.subtitle);
- final TextView description = mLayout.findViewById(R.id.description);
- final Button negative = mLayout.findViewById(R.id.button2);
- final Button positive = mLayout.findViewById(R.id.button1);
-
- mDialog.getLayoutParams().width = (int) mDisplayWidth;
-
- mLastState = STATE_NONE;
- updateFingerprintIcon(STATE_FINGERPRINT);
-
- title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
- title.setSelected(true);
-
- final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
- if (TextUtils.isEmpty(subtitleText)) {
- subtitle.setVisibility(View.GONE);
- } else {
- subtitle.setVisibility(View.VISIBLE);
- subtitle.setText(subtitleText);
- }
-
- final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
- if (TextUtils.isEmpty(descriptionText)) {
- description.setVisibility(View.GONE);
- } else {
- description.setVisibility(View.VISIBLE);
- description.setText(descriptionText);
- }
-
- negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
-
- final CharSequence positiveText =
- mBundle.getCharSequence(BiometricPrompt.KEY_POSITIVE_TEXT);
- positive.setText(positiveText); // needs to be set for marquee to work
- if (positiveText != null) {
- positive.setVisibility(View.VISIBLE);
- } else {
- positive.setVisibility(View.GONE);
- }
-
- if (!mWasForceRemoved) {
- // Dim the background and slide the dialog up
- mDialog.setTranslationY(mAnimationTranslationOffset);
- mLayout.setAlpha(0f);
- postOnAnimation(mShowAnimationRunnable);
- } else {
- // Show the dialog immediately
- mLayout.animate().cancel();
- mDialog.animate().cancel();
- mDialog.setAlpha(1.0f);
- mDialog.setTranslationY(0);
- mLayout.setAlpha(1.0f);
- }
- mWasForceRemoved = false;
+ protected int getAuthenticatedAccessibilityResourceId() {
+ return com.android.internal.R.string.fingerprint_authenticated;
}
- private void setDismissesDialog(View v) {
- v.setClickable(true);
- v.setOnTouchListener((View view, MotionEvent event) -> {
- mCallback.onUserCanceled();
- return true;
- });
+ @Override
+ protected int getIconDescriptionResourceId() {
+ return R.string.accessibility_fingerprint_dialog_fingerprint_icon;
}
- public void startDismiss() {
- mAnimatingAway = true;
-
- final Runnable endActionRunnable = new Runnable() {
- @Override
- public void run() {
- mWindowManager.removeView(FingerprintDialogView.this);
- mAnimatingAway = false;
- }
- };
-
- postOnAnimation(new Runnable() {
- @Override
- public void run() {
- mLayout.animate()
- .alpha(0f)
- .setDuration(ANIMATION_DURATION_AWAY)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- mDialog.animate()
- .translationY(mAnimationTranslationOffset)
- .setDuration(ANIMATION_DURATION_AWAY)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .withEndAction(endActionRunnable)
- .start();
- }
- });
- }
-
- /**
- * Force remove the window, cancelling any animation that's happening. This should only be
- * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
- * will cause the dialog to show without an animation the next time it's attached.
- */
- public void forceRemove() {
- mLayout.animate().cancel();
- mDialog.animate().cancel();
- mWindowManager.removeView(FingerprintDialogView.this);
- mAnimatingAway = false;
- mWasForceRemoved = true;
- }
-
- public boolean isAnimatingAway() {
- return mAnimatingAway;
- }
-
- public void setBundle(Bundle bundle) {
- mBundle = bundle;
- }
-
- // Clears the temporary message and shows the help message.
- private void handleClearMessage() {
- updateFingerprintIcon(STATE_FINGERPRINT);
- mErrorText.setText(R.string.fingerprint_dialog_touch_sensor);
- mErrorText.setTextColor(mTextColor);
- }
-
- // Shows an error/help message
- private void showTemporaryMessage(String message) {
- mHandler.removeMessages(MSG_CLEAR_MESSAGE);
- updateFingerprintIcon(STATE_FINGERPRINT_ERROR);
- mErrorText.setText(message);
- mErrorText.setTextColor(mErrorColor);
- mErrorText.setContentDescription(message);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
- BiometricPrompt.HIDE_DIALOG_DELAY);
- }
-
- public void showHelpMessage(String message) {
- showTemporaryMessage(message);
- }
-
- public void showErrorMessage(String error) {
- showTemporaryMessage(error);
- mCallback.onErrorShown();
- }
-
- private void updateFingerprintIcon(int newState) {
- Drawable icon = getAnimationForTransition(mLastState, newState);
+ @Override
+ protected void updateIcon(int lastState, int newState) {
+ Drawable icon = getAnimationForTransition(lastState, newState);
if (icon == null) {
Log.e(TAG, "Animation not found");
@@ -342,25 +60,28 @@
? (AnimatedVectorDrawable) icon
: null;
- final ImageView fingerprint_icon = mLayout.findViewById(R.id.fingerprint_icon);
- fingerprint_icon.setImageDrawable(icon);
+ final ImageView fingerprintIcon = getLayout().findViewById(R.id.biometric_icon);
+ fingerprintIcon.setImageDrawable(icon);
- if (animation != null && shouldAnimateForTransition(mLastState, newState)) {
+ if (animation != null && shouldAnimateForTransition(lastState, newState)) {
animation.forceAnimationOnUI();
animation.start();
}
+ }
- mLastState = newState;
+ public FingerprintDialogView(Context context,
+ DialogViewCallback callback) {
+ super(context, callback);
}
private boolean shouldAnimateForTransition(int oldState, int newState) {
- if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
+ if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
return false;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
return true;
- } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
+ } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
return true;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
// TODO(b/77328470): add animation when fingerprint is authenticated
return false;
}
@@ -369,32 +90,18 @@
private Drawable getAnimationForTransition(int oldState, int newState) {
int iconRes;
- if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
+ if (oldState == STATE_NONE && newState == STATE_AUTHENTICATING) {
iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_ERROR) {
iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
+ } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
iconRes = R.drawable.fingerprint_dialog_error_to_fp;
- } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
+ } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
// TODO(b/77328470): add animation when fingerprint is authenticated
iconRes = R.drawable.fingerprint_dialog_error_to_fp;
- }
- else {
+ } else {
return null;
}
return mContext.getDrawable(iconRes);
}
-
- public WindowManager.LayoutParams getLayoutParams() {
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- lp.setTitle("FingerprintDialogView");
- lp.token = mWindowToken;
- return lp;
- }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index d6a1cf0..5739c99 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.car;
import android.content.Context;
-import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -41,7 +40,7 @@
}
@Override
- public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by not pinning any
// notification if the shade is already opened.
@@ -49,6 +48,6 @@
return false;
}
- return super.shouldPeek(entry, sbn);
+ return super.shouldHeadsUp(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 6a29299..bb05980 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -48,7 +48,15 @@
void onIgnoreTouchWhilePulsing(boolean ignore);
interface Callback {
- default void onNotificationHeadsUp() {}
+ /**
+ * Called when a high priority notification is added.
+ */
+ default void onNotificationAlerted() {}
+
+ /**
+ * Called when battery state or power save mode changes.
+ * @param active whether power save is active or not
+ */
default void onPowerSaveChanged(boolean active) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1589969..31548b9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -409,7 +409,7 @@
private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
- public void onNotificationHeadsUp() {
+ public void onNotificationAlerted() {
onNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index cc1b9e8..bde7f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -55,6 +55,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -318,8 +319,8 @@
ArraySet<String> addedKeys = new ArraySet<String>();
mHasLogoutButton = false;
mHasLockdownButton = false;
- mSeparatedEmergencyButtonEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0;
+ mSeparatedEmergencyButtonEnabled = FeatureFlagUtils
+ .isEnabled(mContext, FeatureFlagUtils.EMERGENCY_DIAL_SHORTCUTS);
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1655c01..757c821 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -20,6 +20,9 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
+import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.READY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -30,7 +33,6 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -38,6 +40,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricSourceType;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
@@ -58,6 +61,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -70,21 +74,21 @@
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.internal.util.LatencyTracker;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -274,6 +278,12 @@
private KeyguardUpdateMonitor mUpdateMonitor;
+ /**
+ * Last SIM state reported by the telephony system.
+ * Index is the slotId - in case of multiple SIM cards.
+ */
+ private final SparseArray<IccCardConstants.State> mLastSimStates = new SparseArray<>();
+
private boolean mDeviceInteractive;
private boolean mGoingToSleep;
@@ -450,6 +460,14 @@
}
}
+ boolean simWasLocked;
+ synchronized (KeyguardViewMediator.this) {
+ IccCardConstants.State lastState = mLastSimStates.get(slotId);
+ simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED)
+ && simState == READY;
+ mLastSimStates.append(slotId, simState);
+ }
+
switch (simState) {
case NOT_READY:
case ABSENT:
@@ -503,6 +521,9 @@
case READY:
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+ if (mShowing && simWasLocked) {
+ resetStateLocked();
+ }
mLockWhenSimRemoved = true;
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ca1b489..fcd479c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,14 +6,12 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
+
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
@@ -41,14 +39,12 @@
return t * t * t + 1.0f;
};
-
private final ArrayList<TileRecord> mTiles = new ArrayList<>();
private final ArrayList<TilePage> mPages = new ArrayList<>();
private PageIndicator mPageIndicator;
private float mPageIndicatorPosition;
- private int mNumPages;
private PageListener mPageListener;
private boolean mListening;
@@ -56,6 +52,7 @@
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
+ private boolean mDistributeTiles = false;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -122,7 +119,7 @@
public void setPageIndicator(PageIndicator indicator) {
mPageIndicator = indicator;
- mPageIndicator.setNumPages(mNumPages);
+ mPageIndicator.setNumPages(mPages.size());
mPageIndicator.setLocation(mPageIndicatorPosition);
}
@@ -136,13 +133,15 @@
@Override
public void addTile(TileRecord tile) {
mTiles.add(tile);
- postDistributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
@Override
public void removeTile(TileRecord tile) {
if (mTiles.remove(tile)) {
- postDistributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
}
@@ -175,44 +174,50 @@
mPageListener = listener;
}
- private void postDistributeTiles() {
- removeCallbacks(mDistribute);
- post(mDistribute);
- }
-
private void distributeTiles() {
+ emptyAndInflateOrRemovePages();
+
+ final int tileCount = mPages.get(0).maxTiles();
if (DEBUG) Log.d(TAG, "Distributing tiles");
- final int NP = mPages.size();
- for (int i = 0; i < NP; i++) {
- mPages.get(i).removeAllViews();
- }
int index = 0;
final int NT = mTiles.size();
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
- if (mPages.get(index).isFull()) {
- if (++index == mPages.size()) {
- if (DEBUG) Log.d(TAG, "Adding page for "
- + tile.tile.getClass().getSimpleName());
- mPages.add((TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false));
- }
+ if (mPages.get(index).mRecords.size() == tileCount) index++;
+ if (DEBUG) {
+ Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+ + index);
}
- if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
- + index);
mPages.get(index).addTile(tile);
}
- if (mNumPages != index + 1) {
- mNumPages = index + 1;
- while (mPages.size() > mNumPages) {
- mPages.remove(mPages.size() - 1);
- }
- if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
- mPageIndicator.setNumPages(mNumPages);
- setAdapter(mAdapter);
- mAdapter.notifyDataSetChanged();
- setCurrentItem(0, false);
+ }
+
+ private void emptyAndInflateOrRemovePages() {
+ final int nTiles = mTiles.size();
+ int numPages = nTiles / mPages.get(0).maxTiles();
+ // Add one more not full page if needed
+ numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1);
+
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ mPages.get(i).removeAllViews();
}
+ if (NP == numPages) {
+ return;
+ }
+ while (mPages.size() < numPages) {
+ if (DEBUG) Log.d(TAG, "Adding page");
+ mPages.add((TilePage) LayoutInflater.from(getContext())
+ .inflate(R.layout.qs_paged_page, this, false));
+ }
+ while (mPages.size() > numPages) {
+ if (DEBUG) Log.d(TAG, "Removing page");
+ mPages.remove(mPages.size() - 1);
+ }
+ mPageIndicator.setNumPages(mPages.size());
+ setAdapter(mAdapter);
+ mAdapter.notifyDataSetChanged();
+ setCurrentItem(0, false);
}
@Override
@@ -222,20 +227,39 @@
setPadding(0, 0, 0,
getContext().getResources().getDimensionPixelSize(
R.dimen.qs_paged_tile_layout_padding_bottom));
-
boolean changed = false;
for (int i = 0; i < mPages.size(); i++) {
changed |= mPages.get(i).updateResources();
}
if (changed) {
- distributeTiles();
+ mDistributeTiles = true;
+ requestLayout();
}
return changed;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ final int nTiles = mTiles.size();
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+
+ // Only change the pages if the number of rows or columns (from updateResources) has
+ // changed or the tiles have changed
+ if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) {
+ mDistributeTiles = false;
+ distributeTiles();
+ }
+
+ final int nRows = mPages.get(0).mRows;
+ for (int i = 0; i < mPages.size(); i++) {
+ TilePage t = mPages.get(i);
+ t.mRows = nRows;
+ }
+ }
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
// The ViewPager likes to eat all of the space, instead force it to wrap to the max height
// of the pages.
int maxHeight = 0;
@@ -249,13 +273,6 @@
setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom());
}
- private final Runnable mDistribute = new Runnable() {
- @Override
- public void run() {
- distributeTiles();
- }
- };
-
public int getColumnCount() {
if (mPages.size() == 0) return 0;
return mPages.get(0).mColumns;
@@ -346,33 +363,17 @@
};
public static class TilePage extends TileLayout {
- private int mMaxRows = 3;
+
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
- updateResources();
- }
-
- @Override
- public boolean updateResources() {
- final int rows = getRows();
- boolean changed = rows != mMaxRows;
- if (changed) {
- mMaxRows = rows;
- requestLayout();
- }
- return super.updateResources() || changed;
- }
-
- private int getRows() {
- return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows));
- }
-
- public void setMaxRows(int maxRows) {
- mMaxRows = maxRows;
}
public boolean isFull() {
- return mRecords.size() >= mColumns * mMaxRows;
+ return mRecords.size() >= mColumns * mRows;
+ }
+
+ public int maxTiles() {
+ return mColumns * mRows;
}
}
@@ -391,6 +392,9 @@
position = mPages.size() - 1 - position;
}
ViewGroup view = mPages.get(position);
+ if (view.getParent() != null) {
+ container.removeView(view);
+ }
container.addView(view);
updateListening();
return view;
@@ -398,7 +402,7 @@
@Override
public int getCount() {
- return mNumPages;
+ return mPages.size();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index feff5d4..1451e71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
@@ -91,15 +92,24 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // Since we control our own bottom, be whatever size we want.
- // Otherwise the QSPanel ends up with 0 height when the window is only the
- // size of the status bar.
- mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
+ // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
+ // bottom and footer are inside the screen.
+ Configuration config = getResources().getConfiguration();
+ boolean navBelow = config.smallestScreenWidthDp >= 600
+ || config.orientation != Configuration.ORIENTATION_LANDSCAPE;
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams();
+
+ // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
+ // subtract its height. We do not care if the collapsed notifications fit in the screen.
+ int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+ - getPaddingBottom();
+ if (navBelow) {
+ maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
+ }
+ mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanel.getMeasuredWidth();
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanel.getMeasuredHeight();
+ + mQSPanel.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c67165e..01ff72e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -24,10 +24,12 @@
protected int mCellMarginHorizontal;
protected int mCellMarginVertical;
protected int mSidePadding;
+ protected int mRows = 1;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mCellMarginTop;
private boolean mListening;
+ protected int mMaxAllowedRows = 3;
public TileLayout(Context context) {
this(context, null);
@@ -86,6 +88,7 @@
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
+ mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
if (mColumns != columns) {
mColumns = columns;
requestLayout();
@@ -96,10 +99,16 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
+ // it will show all its tiles. In this case, the tiles have to be entered before the
+ // container is measured. Any change in the tiles, should trigger a remeasure.
final int numTiles = mRecords.size();
final int width = MeasureSpec.getSize(widthMeasureSpec)
- getPaddingStart() - getPaddingEnd();
- final int numRows = (numTiles + mColumns - 1) / mColumns;
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ mRows = (numTiles + mColumns - 1) / mColumns;
+ }
mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
// Measure each QS tile.
@@ -112,13 +121,35 @@
// Only include the top margin in our measurement if we have more than 1 row to show.
// Otherwise, don't add the extra margin buffer at top.
- int height = (mCellHeight + mCellMarginVertical) * numRows +
- (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
+ int height = (mCellHeight + mCellMarginVertical) * mRows +
+ (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
if (height < 0) height = 0;
setMeasuredDimension(width, height);
}
+ /**
+ * Determines the maximum number of rows that can be shown based on height. Clips at a minimum
+ * of 1 and a maximum of mMaxAllowedRows.
+ *
+ * @param heightMeasureSpec Available height.
+ * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
+ */
+ public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
+ final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop;
+ final int previousRows = mRows;
+ mRows = availableHeight / (mCellHeight + mCellMarginVertical);
+ if (mRows >= mMaxAllowedRows) {
+ mRows = mMaxAllowedRows;
+ } else if (mRows <= 1) {
+ mRows = 1;
+ }
+ if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+ mRows = (tilesCount + mColumns - 1) / mColumns;
+ }
+ return previousRows != mRows;
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -135,7 +166,8 @@
int column = 0;
// Layout each QS tile.
- for (int i = 0; i < numRecords; i++, column++) {
+ final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
+ for (int i = 0; i < tilesToLayout; i++, column++) {
// If we reached the last column available to layout a tile, wrap back to the next row.
if (column == mColumns) {
column = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index d7abb38..177362c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -16,23 +16,16 @@
package com.android.systemui.recents.events;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
-import android.util.MutableBoolean;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -117,11 +110,7 @@
* <p>
* Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
* on the main application thread. Publishers can send() events to synchronously call subscribers
- * of that event, or post() events to be processed in the next run of the {@link Looper}. In
- * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
- * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
- * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
- * correctly across processes.
+ * of that event, or post() events to be processed in the next run of the {@link Looper}.
*
* <p>
* Subscribers must be registered with a particular EventBus before they will receive events, and
@@ -135,26 +124,12 @@
* <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
* </ul>
*
- * <p>
- * Interprocess-Event method signature:<ul>
- * <li>Methods must be public final
- * <li>Methods must return void
- * <li>Methods must be called "onInterprocessBusEvent"
- * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
- * </ul>
- * </p>
- *
* </p>
* Each subscriber can be registered with a given priority (default 1), and events will be dispatch
* in decreasing order of priority. For subscribers with the same priority, events will be
* dispatched by latest registration time to earliest.
*
* <p>
- * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
- * takes a {@link Bundle} and implement toBundle(). This allows us to serialize events to be sent
- * across processes.
- *
- * <p>
* Caveats:<ul>
* <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
* there must be another strong reference to the publisher for it to not get garbage-collected and
@@ -201,7 +176,7 @@
* result?
* </p>
*/
-public class EventBus extends BroadcastReceiver {
+public class EventBus {
private static final String TAG = "EventBus";
private static final boolean DEBUG_TRACE_ALL = false;
@@ -314,35 +289,6 @@
}
/**
- * An inter-process event super class that allows us to track user state across subscriber
- * invocations.
- */
- public static class InterprocessEvent extends Event {
- private static final String EXTRA_USER = "_user";
-
- // The user which this event originated from
- public final int user;
-
- // Only accessible from derived events
- protected InterprocessEvent(int user) {
- this.user = user;
- }
-
- /**
- * Called from the event bus
- */
- protected InterprocessEvent(Bundle b) {
- user = b.getInt(EXTRA_USER);
- }
-
- protected Bundle toBundle() {
- Bundle b = new Bundle();
- b.putInt(EXTRA_USER, user);
- return b;
- }
- }
-
- /**
* Proguard must also know, and keep, all methods matching this signature.
*
* -keepclassmembers class ** {
@@ -351,13 +297,6 @@
* }
*/
private static final String METHOD_PREFIX = "onBusEvent";
- private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
-
- // Ensures that interprocess events can only be sent from a process holding this permission. */
- private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
-
- // Used for passing event data across process boundaries
- private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
// The default priority of all subscribers
private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
@@ -383,10 +322,6 @@
// The handler to post all events
private Handler mHandler;
- // Keep track of whether we have registered a broadcast receiver already, so that we can
- // unregister ourselves before re-registering again with a new IntentFilter.
- private boolean mHasRegisteredReceiver;
-
/**
* Map from event class -> event handler list. Keeps track of the actual mapping from event
* to subscriber method.
@@ -401,13 +336,6 @@
private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
/**
- * Map from interprocess event name -> interprocess event class. Used for mapping the event
- * name after receiving the broadcast, to the event type. After which a new instance is created
- * and posted in the local process.
- */
- private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
-
- /**
* Set of all currently registered subscribers
*/
private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
@@ -447,7 +375,7 @@
* be scanned for appropriate event handler methods.
*/
public void register(Object subscriber) {
- registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
+ registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
}
/**
@@ -460,44 +388,7 @@
* subscribers
*/
public void register(Object subscriber, int priority) {
- registerSubscriber(subscriber, priority, null);
- }
-
- /**
- * Explicitly registers a subscriber to receive interprocess events with the default priority.
- *
- * @param subscriber the subscriber to handle events. If this is the first instance of the
- * subscriber's class type that has been registered, the class's methods will
- * be scanned for appropriate event handler methods.
- */
- public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
- registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
- }
-
- /**
- * Registers a subscriber to receive interprocess events with the given priority.
- *
- * @param subscriber the subscriber to handle events. If this is the first instance of the
- * subscriber's class type that has been registered, the class's methods will
- * be scanned for appropriate event handler methods.
- * @param priority the priority that this subscriber will receive events relative to other
- * subscribers
- */
- public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
- }
-
- // Register the subscriber normally, and update the broadcast receiver filter if this is
- // a new subscriber type with interprocess events
- MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
- registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
- if (DEBUG_TRACE_ALL) {
- logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
- }
- if (hasInterprocessEventsChanged.value) {
- registerReceiverForInterprocessEvents(context);
- }
+ registerSubscriber(subscriber, priority);
}
/**
@@ -538,18 +429,6 @@
}
/**
- * Explicit unregistration for interprocess event subscribers. This actually behaves exactly
- * the same as unregister() since we also do not want to stop listening for specific
- * inter-process messages in case new instances of that subscriber is registered.
- */
- public void unregisterInterprocess(Context context, Object subscriber) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("unregisterInterprocess()");
- }
- unregister(subscriber);
- }
-
- /**
* Sends an event to the subscribers of the given event type immediately. This can only be
* called from the same thread as the EventBus's looper thread (for the default EventBus, this
* is the main application thread).
@@ -599,57 +478,6 @@
}
}
- /** Prevent post()ing an InterprocessEvent */
- @Deprecated
- public void post(InterprocessEvent event) {
- throw new RuntimeException("Not supported, use postInterprocess");
- }
-
- /** Prevent send()ing an InterprocessEvent */
- @Deprecated
- public void send(InterprocessEvent event) {
- throw new RuntimeException("Not supported, use postInterprocess");
- }
-
- /**
- * Posts an interprocess event.
- */
- public void postInterprocess(Context context, final InterprocessEvent event) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
- }
- String eventType = event.getClass().getName();
- Bundle eventBundle = event.toBundle();
- Intent intent = new Intent(eventType);
- intent.setPackage(context.getPackageName());
- intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_FOREGROUND);
- context.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- /**
- * Receiver for interprocess events.
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
- }
-
- Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
- Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
- try {
- Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
- send((Event) ctor.newInstance(eventBundle));
- } catch (NoSuchMethodException|
- InvocationTargetException|
- InstantiationException|
- IllegalAccessException e) {
- Log.e(TAG, "Failed to create InterprocessEvent", e.getCause());
- }
- }
-
/**
* @return a dump of the current state of the EventBus
*/
@@ -711,8 +539,7 @@
/**
* Registers a new subscriber.
*/
- private void registerSubscriber(Object subscriber, int priority,
- MutableBoolean hasInterprocessEventsChangedOut) {
+ private void registerSubscriber(Object subscriber, int priority) {
// Fail immediately if we are being called from the non-main thread
long callingThreadId = Thread.currentThread().getId();
if (callingThreadId != mHandler.getLooper().getThread().getId()) {
@@ -759,32 +586,16 @@
}
// Find all the valid event bus handler methods of the subscriber
- MutableBoolean isInterprocessEvent = new MutableBoolean(false);
Method[] methods = subscriberType.getDeclaredMethods();
for (Method m : methods) {
Class<?>[] parameterTypes = m.getParameterTypes();
- isInterprocessEvent.value = false;
- if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
+ if (isValidEventBusHandlerMethod(m, parameterTypes)) {
Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
if (eventTypeHandlers == null) {
eventTypeHandlers = new ArrayList<>();
mEventTypeMap.put(eventType, eventTypeHandlers);
}
- if (isInterprocessEvent.value) {
- try {
- // Enforce that the event must have a Bundle constructor
- eventType.getConstructor(Bundle.class);
-
- mInterprocessEventNameMap.put(eventType.getName(),
- (Class<? extends InterprocessEvent>) eventType);
- if (hasInterprocessEventsChangedOut != null) {
- hasInterprocessEventsChangedOut.value = true;
- }
- } catch (NoSuchMethodException e) {
- throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
- }
- }
EventHandlerMethod method = new EventHandlerMethod(m, eventType);
EventHandler handler = new EventHandler(sub, method, priority);
eventTypeHandlers.add(handler);
@@ -793,8 +604,7 @@
if (DEBUG_TRACE_ALL) {
logWithPid(" * Method: " + m.getName() +
- " event: " + parameterTypes[0].getSimpleName() +
- " interprocess? " + isInterprocessEvent.value);
+ " event: " + parameterTypes[0].getSimpleName());
}
}
}
@@ -830,12 +640,7 @@
final EventHandler eventHandler = eventHandlers.get(i);
if (eventHandler.subscriber.getReference() != null) {
if (event.requiresPost) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- processEvent(eventHandler, event);
- }
- });
+ mHandler.post(() -> processEvent(eventHandler, event));
hasPostedEvent = true;
} else {
processEvent(eventHandler, event);
@@ -845,12 +650,7 @@
// Clean up after this event, deferring until all subscribers have been called
if (hasPostedEvent) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- event.onPostDispatch();
- }
- });
+ mHandler.post(event::onPostDispatch);
} else {
event.onPostDispatch();
}
@@ -898,29 +698,6 @@
}
/**
- * Re-registers the broadcast receiver for any new messages that we want to listen for.
- */
- private void registerReceiverForInterprocessEvents(Context context) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("registerReceiverForInterprocessEvents()");
- }
- // Rebuild the receiver filter with the new interprocess events
- IntentFilter filter = new IntentFilter();
- for (String eventName : mInterprocessEventNameMap.keySet()) {
- filter.addAction(eventName);
- if (DEBUG_TRACE_ALL) {
- logWithPid(" filter: " + eventName);
- }
- }
- // Re-register the receiver with the new filter
- if (mHasRegisteredReceiver) {
- context.unregisterReceiver(this);
- }
- context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
- mHasRegisteredReceiver = true;
- }
-
- /**
* Returns whether this subscriber is currently registered. If {@param removeFoundSubscriber}
* is true, then remove the subscriber before returning.
*/
@@ -940,28 +717,19 @@
/**
* @return whether {@param method} is a valid (normal or interprocess) event bus handler method
*/
- private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
- MutableBoolean isInterprocessEventOut) {
+ private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) &&
Modifier.isFinal(modifiers) &&
method.getReturnType().equals(Void.TYPE) &&
parameterTypes.length == 1) {
- if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
- method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
- isInterprocessEventOut.value = true;
- return true;
- } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
+ if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
method.getName().startsWith(METHOD_PREFIX)) {
- isInterprocessEventOut.value = false;
return true;
} else {
if (DEBUG_TRACE_ALL) {
if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
logWithPid(" Expected method take an Event-based parameter: " + method.getName());
- } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
- !method.getName().startsWith(METHOD_PREFIX)) {
- logWithPid(" Expected method start with method prefix: " + method.getName());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 4516518..b6e88d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -165,7 +165,7 @@
* Whether or not the given notification is alerting and managed by this manager.
* @return true if the notification is alerting
*/
- public boolean contains(@NonNull String key) {
+ public boolean isAlerting(@NonNull String key) {
return mAlertEntries.containsKey(key);
}
@@ -294,7 +294,7 @@
removeAutoRemovalCallbacks();
if (!isSticky()) {
- long finishTime = mPostTime + mAutoDismissNotificationDecay;
+ long finishTime = calculateFinishTime();
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
mHandler.postDelayed(mRemoveAlertRunnable, removeDelay);
}
@@ -357,6 +357,14 @@
protected long calculatePostTime() {
return mClock.currentTimeMillis();
}
+
+ /**
+ * Calculate when the notification should auto-dismiss itself.
+ * @return the finish time
+ */
+ protected long calculateFinishTime() {
+ return mPostTime + mAutoDismissNotificationDecay;
+ }
}
protected final static class Clock {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
new file mode 100644
index 0000000..2c384d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+
+/**
+ * Manager which handles high priority notifications that should "pulse" in when the device is
+ * dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly
+ * before automatically dismissing the alert.
+ */
+public final class AmbientPulseManager extends AlertingNotificationManager {
+
+ protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
+ @VisibleForTesting
+ protected long mExtensionTime;
+
+ public AmbientPulseManager(@NonNull final Context context) {
+ Resources resources = context.getResources();
+ mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
+ mMinimumDisplayTime = resources.getInteger(R.integer.ambient_notification_minimum_time);
+ mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
+ }
+
+ /**
+ * Adds an OnAmbientChangedListener to observe events.
+ */
+ public void addListener(@NonNull OnAmbientChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes the OnAmbientChangedListener from the observer list.
+ */
+ public void removeListener(@NonNull OnAmbientChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
+ * longer.
+ */
+ public void extendPulse() {
+ AmbientEntry topEntry = getTopEntry();
+ if (topEntry == null) {
+ return;
+ }
+ topEntry.extendPulse();
+ }
+
+ @Override
+ protected void onAlertEntryAdded(AlertEntry alertEntry) {
+ NotificationData.Entry entry = alertEntry.mEntry;
+ entry.row.setAmbientPulsing(true);
+ for (OnAmbientChangedListener listener : mListeners) {
+ listener.onAmbientStateChanged(entry, true);
+ }
+ }
+
+ @Override
+ protected void onAlertEntryRemoved(AlertEntry alertEntry) {
+ NotificationData.Entry entry = alertEntry.mEntry;
+ entry.row.setAmbientPulsing(false);
+ for (OnAmbientChangedListener listener : mListeners) {
+ listener.onAmbientStateChanged(entry, false);
+ }
+ }
+
+ @Override
+ protected AlertEntry createAlertEntry() {
+ return new AmbientEntry();
+ }
+
+ /**
+ * Get the top pulsing entry. This should be the currently showing one if there are multiple.
+ * @return the currently showing entry
+ */
+ private AmbientEntry getTopEntry() {
+ if (mAlertEntries.isEmpty()) {
+ return null;
+ }
+ AlertEntry topEntry = null;
+ for (AlertEntry entry : mAlertEntries.values()) {
+ if (topEntry == null || entry.compareTo(topEntry) < 0) {
+ topEntry = entry;
+ }
+ }
+ return (AmbientEntry) topEntry;
+ }
+
+ /**
+ * Observer interface for any changes in the ambient entries.
+ */
+ public interface OnAmbientChangedListener {
+ /**
+ * Called when an entry starts or stops pulsing.
+ * @param entry the entry that changed
+ * @param isPulsing true if the entry is now pulsing, false otherwise
+ */
+ void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+ }
+
+ private final class AmbientEntry extends AlertEntry {
+ private boolean extended;
+
+ /**
+ * Extend the lifetime of the alertEntry so that it auto-removes later. Can only be
+ * extended once.
+ */
+ private void extendPulse() {
+ if (!extended) {
+ extended = true;
+ updateEntry(false);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ extended = false;
+ }
+
+ @Override
+ protected long calculateFinishTime() {
+ return super.calculateFinishTime() + (extended ? mExtensionTime : 0);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e19c844..5c0b328 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -161,7 +161,7 @@
default void onRotationProposal(int rotation, boolean isValid) { }
default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver,
- int type) { }
+ int type, boolean requireConfirmation) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
@@ -514,12 +514,14 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
args.argi1 = type;
+ args.arg3 = requireConfirmation;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
}
@@ -763,7 +765,8 @@
mCallbacks.get(i).showBiometricDialog(
(Bundle) someArgs.arg1,
(IBiometricPromptReceiver) someArgs.arg2,
- someArgs.argi1);
+ someArgs.argi1,
+ (boolean) someArgs.arg3);
}
someArgs.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f7615e6..d8f7b61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -121,7 +121,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class)
+ .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index d479838..f69ad43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -321,7 +321,7 @@
&& !row.isLowPriority()));
}
- entry.row.setShowAmbient(mPresenter.isDozing());
+ entry.row.setOnAmbient(mPresenter.isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.row.isRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 7a69f2e..d6719f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -16,42 +16,70 @@
package com.android.systemui.statusbar;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.util.ArraySet;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.Comparator;
/**
* Tracks and reports on {@link StatusBarState}.
*/
public class StatusBarStateController {
+ private static final String TAG = "SbStateController";
private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
private static final int MIN_STATE = StatusBarState.SHADE;
- private final ArraySet<StateListener> mListeners = new ArraySet<>();
+ private static final Comparator <RankedListener> mComparator
+ = (o1, o2) -> Integer.compare(o1.rank, o2.rank);
+
+ private final ArrayList<RankedListener> mListeners = new ArrayList<>();
private int mState;
private int mLastState;
private boolean mLeaveOpenOnKeyguardHide;
+ // TODO: b/115739177 (remove this explicit ordering if we can)
+ @Retention(SOURCE)
+ @IntDef({RANK_STATUS_BAR, RANK_STATUS_BAR_WINDOW_CONTROLLER, RANK_STACK_SCROLLER, RANK_SHELF})
+ public @interface SbStateListenerRank {}
+ // This is the set of known dependencies when updating StatusBarState
+ public static final int RANK_STATUS_BAR = 0;
+ public static final int RANK_STATUS_BAR_WINDOW_CONTROLLER = 1;
+ public static final int RANK_STACK_SCROLLER = 2;
+ public static final int RANK_SHELF = 3;
+
public int getState() {
return mState;
}
- public void setState(int state) {
+ public boolean setState(int state) {
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
}
if (state == mState) {
- return;
+ return false;
}
synchronized (mListeners) {
- for (StateListener listener : new ArraySet<>(mListeners)) {
- listener.onStatePreChange(mState, state);
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onStatePreChange(mState, state);
}
mLastState = mState;
mState = state;
- for (StateListener listener : new ArraySet<>(mListeners)) {
- listener.onStateChanged(mState);
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onStateChanged(mState);
+ }
+
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onStatePostChange();
}
}
+
+ return true;
}
public boolean goingToFullShade() {
@@ -72,20 +100,67 @@
public void addListener(StateListener listener) {
synchronized (mListeners) {
- mListeners.add(listener);
+ addListenerInternalLocked(listener, Integer.MAX_VALUE);
}
}
+ /**
+ * Add a listener and a rank based on the priority of this message
+ * @param listener the listener
+ * @param rank the order in which you'd like to be called. Ranked listeners will be
+ * notified before unranked, and we will sort ranked listeners from low to high
+ *
+ * @deprecated This method exists only to solve latent inter-dependencies from refactoring
+ * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
+ * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+ */
+ public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+ synchronized (mListeners) {
+ addListenerInternalLocked(listener, rank);
+ }
+ }
+
+ @GuardedBy("mListeners")
+ private void addListenerInternalLocked(StateListener listener, int rank) {
+ // Protect against double-subscribe
+ for (RankedListener rl : mListeners) {
+ if (rl.listener.equals(listener)) {
+ return;
+ }
+ }
+
+ RankedListener rl = new RankedListener(listener, rank);
+ mListeners.add(rl);
+ mListeners.sort(mComparator);
+ }
+
public void removeListener(StateListener listener) {
synchronized (mListeners) {
- mListeners.remove(listener);
+ mListeners.removeIf((it) -> it.listener.equals(listener));
}
}
+ public static String describe(int state) {
+ return StatusBarState.toShortString(state);
+ }
+
public interface StateListener {
public default void onStatePreChange(int oldState, int newState) {
}
+ public default void onStatePostChange() {
+ }
+
public void onStateChanged(int newState);
}
+
+ private class RankedListener {
+ private final StateListener listener;
+ private final int rank;
+
+ private RankedListener(StateListener l, int r) {
+ listener = l;
+ rank = r;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 804e842..d097c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -440,6 +440,8 @@
} else if (isHeadsUp) {
// Provide consistent ranking with headsUpManager
return mHeadsUpManager.compare(a, b);
+ } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
+ return a.row.isAmbientPulsing() ? -1 : 1;
} else if (aMedia != bMedia) {
// Upsort current media notification.
return aMedia ? -1 : 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index ac01fa3..935eaac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -58,6 +58,8 @@
import com.android.systemui.UiOffloadThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -91,7 +93,7 @@
ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
VisualStabilityManager.Callback {
private static final String TAG = "NotificationEntryMgr";
- protected static final boolean DEBUG = false;
+ protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
protected static final boolean ENABLE_HEADS_UP = true;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
@@ -121,6 +123,7 @@
Dependency.get(ForegroundServiceController.class);
protected final NotificationListener mNotificationListener =
Dependency.get(NotificationListener.class);
+ protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
protected IStatusBarService mBarService;
protected NotificationPresenter mPresenter;
@@ -264,6 +267,7 @@
}
mNotificationLifetimeExtenders.add(mHeadsUpManager);
+ mNotificationLifetimeExtenders.add(mAmbientPulseManager);
mNotificationLifetimeExtenders.add(mGutsManager);
mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders());
@@ -381,7 +385,7 @@
final int userId = n.getUserId();
try {
int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (isHeadsUp(n.getKey())) {
+ if (mHeadsUpManager.isAlerting(n.getKey())) {
dismissalSurface = NotificationStats.DISMISSAL_PEEK;
} else if (mListContainer.hasPulsingNotifications()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
@@ -432,12 +436,14 @@
}
private void addEntry(NotificationData.Entry shadeEntry) {
- boolean isHeadsUped = shouldPeek(shadeEntry);
- if (isHeadsUped) {
+ if (shouldHeadsUp(shadeEntry)) {
mHeadsUpManager.showNotification(shadeEntry);
// Mark as seen immediately
setNotificationShown(shadeEntry.notification);
}
+ if (shouldPulse(shadeEntry)) {
+ mAmbientPulseManager.showNotification(shadeEntry);
+ }
addNotificationViews(shadeEntry);
mCallback.onNotificationAdded(shadeEntry);
}
@@ -465,7 +471,11 @@
private void removeNotificationInternal(String key,
@Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) {
abortExistingInflation(key);
- if (mHeadsUpManager.contains(key)) {
+
+ // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
+ // Though the remove itself may fail, it lets the manager know to remove as soon as
+ // possible.
+ if (mHeadsUpManager.isAlerting(key)) {
// A cancel() in response to a remote input shouldn't be delayed, as it makes the
// sending look longer than it takes.
// Also we should not defer the removal if reordering isn't allowed since otherwise
@@ -473,10 +483,11 @@
boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
&& !FORCE_REMOTE_INPUT_HISTORY
|| !mVisualStabilityManager.isReorderingAllowed();
-
- // Attempt to remove notification.
mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
}
+ if (mAmbientPulseManager.isAlerting(key)) {
+ mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+ }
NotificationData.Entry entry = mNotificationData.get(key);
@@ -651,13 +662,15 @@
private void addNotificationInternal(StatusBarNotification notification,
NotificationListenerService.RankingMap rankingMap) throws InflationException {
String key = notification.getKey();
- if (DEBUG) Log.d(TAG, "addNotification key=" + key);
+ if (DEBUG) {
+ Log.d(TAG, "addNotification key=" + key);
+ }
mNotificationData.updateRanking(rankingMap);
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
- boolean isHeadsUped = shouldPeek(shadeEntry);
+ boolean isHeadsUped = shouldHeadsUp(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(shadeEntry)) {
if (DEBUG) {
@@ -750,7 +763,6 @@
extender.setShouldManageLifetime(entry, false /* shouldManage */);
}
- Notification n = notification.getNotification();
mNotificationData.updateRanking(ranking);
final StatusBarNotification oldNotification = entry.notification;
@@ -763,10 +775,12 @@
mForegroundServiceController.updateNotification(notification,
mNotificationData.getImportance(key));
- boolean shouldPeek = shouldPeek(entry, notification);
- boolean alertAgain = alertAgain(entry, n);
-
- updateHeadsUp(key, entry, shouldPeek, alertAgain);
+ boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
+ if (mPresenter.isDozing()) {
+ updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+ } else {
+ updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+ }
updateNotifications();
if (!notification.isClearable()) {
@@ -851,66 +865,147 @@
}
}
- protected boolean shouldPeek(NotificationData.Entry entry) {
- return shouldPeek(entry, entry.notification);
- }
+ /**
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
- public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
- if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
- if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
+ if (mPresenter.isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+ }
return false;
}
- if (mNotificationData.shouldFilterOut(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: no huns or vr mode");
+ }
return false;
}
boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
- if (!inUse && !mPresenter.isDozing()) {
+ if (!inUse) {
if (DEBUG) {
- Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+ Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
}
return false;
}
- if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
- return false;
- }
-
- // Peeking triggers an ambient display pulse, so disable peek is ambient is active
- if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(entry)) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
- return false;
- }
-
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+ if (mNotificationData.shouldSuppressPeek(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+ }
return false;
}
if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+ }
return false;
}
- // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
- int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
- : NotificationManager.IMPORTANCE_HIGH;
- if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
- if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+ }
return false;
}
- // Don't peek notifications that are suppressed due to group alert behavior
+ if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!mCallback.canHeadsUp(entry, sbn)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether or not the notification should "pulse" on the user's display when the phone is
+ * dozing. This displays the ambient view of the notification.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should ambient pulse, false otherwise
+ */
+ protected boolean shouldPulse(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (!mPresenter.isDozing()) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (!canAlertCommon(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.shouldSuppressAmbient(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ if (mNotificationData.getImportance(sbn.getKey())
+ < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (DEBUG) {
+ Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Common checks between heads up alerting and ambient pulse alerting. See
+ * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
+ * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that
+ * fail any of these checks should not alert at all.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ protected boolean canAlertCommon(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ if (mNotificationData.shouldFilterOut(entry)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+ }
+ return false;
+ }
+
+ // Don't alert notifications that are suppressed due to group alert behavior
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
- return false;
- }
-
- if (!mCallback.shouldPeek(entry, sbn)) {
+ if (DEBUG) {
+ Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+ }
return false;
}
@@ -933,26 +1028,31 @@
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
- protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
- boolean alertAgain) {
- final boolean wasHeadsUp = isHeadsUp(key);
- if (wasHeadsUp) {
- if (!shouldPeek) {
+ /**
+ * Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
+ * method.
+ * @param entry entry to update
+ * @param shouldAlert whether or not it should be alerting
+ * @param alertAgain whether or not an alert should actually come in as if it were new
+ * @param alertManager the alerting notification manager that manages the alert state
+ */
+ private void updateAlertState(NotificationData.Entry entry, boolean shouldAlert,
+ boolean alertAgain, AlertingNotificationManager alertManager) {
+ final boolean wasAlerting = alertManager.isAlerting(entry.key);
+ if (wasAlerting) {
+ if (!shouldAlert) {
// We don't want this to be interrupting anymore, lets remove it
- mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+ alertManager.removeNotification(entry.key,
+ false /* ignoreEarliestRemovalTime */);
} else {
- mHeadsUpManager.updateNotification(entry.key, alertAgain);
+ alertManager.updateNotification(entry.key, alertAgain);
}
- } else if (shouldPeek && alertAgain) {
- // This notification was updated to be a heads-up, show it!
- mHeadsUpManager.showNotification(entry);
+ } else if (shouldAlert && alertAgain) {
+ // This notification was updated to be alerting, show it!
+ alertManager.showNotification(entry);
}
}
- protected boolean isHeadsUp(String key) {
- return mHeadsUpManager.contains(key);
- }
-
/**
* Callback for NotificationEntryManager.
*/
@@ -1008,12 +1108,12 @@
void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
/**
- * Returns true if NotificationEntryManager should peek this notification.
+ * Returns true if NotificationEntryManager can heads up this notification.
*
- * @param entry entry of the notification that might be peeked
- * @param sbn notification that might be peeked
- * @return true if the notification should be peeked
+ * @param entry entry of the notification that might be heads upped
+ * @param sbn notification that might be heads upped
+ * @return true if the notification can be heads upped
*/
- boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn);
+ boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
index f204c42..5ad2ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
@@ -14,7 +14,10 @@
* limitations under the License
*/
-package java.lang.annotation;
+package com.android.systemui.statusbar.notification;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE)
public @interface ShadeViewRefactor {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 216ed68..9b1d334 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -174,6 +174,11 @@
*/
private boolean mOnKeyguard;
+ /**
+ * Whether or not the row is currently on the doze screen.
+ */
+ private boolean mOnAmbient;
+
private Animator mTranslateAnim;
private ArrayList<View> mTranslateableViews;
private NotificationContentView mPublicLayout;
@@ -186,7 +191,18 @@
private NotificationData.Entry mEntry;
private StatusBarNotification mStatusBarNotification;
private String mAppName;
+
+ /**
+ * Whether or not the notification is using the heads up view and should peek from the top.
+ */
private boolean mIsHeadsUp;
+
+ /**
+ * Whether or not the notification is using the ambient display view and is pulsing. This
+ * occurs when a high priority notification alerts while the phone is dozing or is on AOD.
+ */
+ private boolean mIsAmbientPulsing;
+
private boolean mLastChronometerRunning = true;
private ViewStub mChildrenContainerStub;
private NotificationGroupManager mGroupManager;
@@ -289,7 +305,6 @@
private float mContentTransformationAmount;
private boolean mIconsVisible = true;
private boolean mAboveShelf;
- private boolean mShowAmbient;
private boolean mIsLastChild;
private Runnable mOnDismissRunnable;
private boolean mIsLowPriority;
@@ -606,6 +621,14 @@
}
}
+ public boolean isAmbientPulsing() {
+ return mIsAmbientPulsing;
+ }
+
+ public void setAmbientPulsing(boolean isAmbientPulsing) {
+ mIsAmbientPulsing = isAmbientPulsing;
+ }
+
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
mPrivateLayout.setGroupManager(groupManager);
@@ -1014,7 +1037,7 @@
removeView(mMenuRow.getMenuView());
}
mMenuRow = plugin;
- if (mMenuRow.useDefaultMenuItems()) {
+ if (mMenuRow.shouldUseDefaultMenuItems()) {
ArrayList<MenuItem> items = new ArrayList<>();
items.add(NotificationMenuRow.createInfoItem(mContext));
items.add(NotificationMenuRow.createSnoozeItem(mContext));
@@ -1764,7 +1787,7 @@
getEntry().expandedIcon.setScrollX((int) -translationX);
}
if (mMenuRow.getMenuView() != null) {
- mMenuRow.onTranslationUpdate(translationX);
+ mMenuRow.onParentTranslationUpdate(translationX);
}
}
@@ -1854,7 +1877,7 @@
public void setDark(boolean dark, boolean fade, long delay) {
super.setDark(dark, fade, delay);
mDark = dark;
- if (!mIsHeadsUp) {
+ if (!mIsAmbientPulsing) {
// Only fade the showing view of the pulsing notification.
fade = false;
}
@@ -2155,7 +2178,7 @@
return mPrivateLayout.getMinHeight();
} else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
return getMinHeight();
- } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
+ } else if (mIsSummaryWithChildren && (!mOnKeyguard || mOnAmbient)) {
return mChildrenContainer.getIntrinsicHeight();
} else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
if (isPinned() || mHeadsupDisappearRunning) {
@@ -2173,7 +2196,7 @@
}
private boolean isHeadsUpAllowed() {
- return !mOnKeyguard && !mShowAmbient;
+ return !mOnKeyguard && !mOnAmbient;
}
@Override
@@ -2269,7 +2292,7 @@
notifyHeightChanged(true /* needsAnimation */);
}
if (mMenuRow.getMenuView() != null) {
- mMenuRow.onHeightUpdate();
+ mMenuRow.onParentHeightUpdate();
}
updateContentShiftHeight();
if (mLayoutListener != null) {
@@ -2520,7 +2543,7 @@
mGuts.setActualHeight(height);
}
if (mMenuRow.getMenuView() != null) {
- mMenuRow.onHeightUpdate();
+ mMenuRow.onParentHeightUpdate();
}
}
@@ -2818,11 +2841,11 @@
|| mExpandAnimationRunning || mChildIsExpanding);
}
- public void setShowAmbient(boolean showAmbient) {
- if (showAmbient != mShowAmbient) {
- mShowAmbient = showAmbient;
+ public void setOnAmbient(boolean onAmbient) {
+ if (onAmbient != mOnAmbient) {
+ mOnAmbient = onAmbient;
if (mChildrenContainer != null) {
- mChildrenContainer.notifyShowAmbientChanged();
+ mChildrenContainer.notifyDozingStateChanged();
}
notifyHeightChanged(false /* needsAnimation */);
}
@@ -2891,8 +2914,8 @@
return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
}
- public boolean isShowingAmbient() {
- return mShowAmbient;
+ public boolean isOnAmbient() {
+ return mOnAmbient;
}
public void setAboveShelf(boolean aboveShelf) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0110610..4963a0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -725,7 +725,7 @@
}
public int getMaxHeight() {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return getShowingAmbientView().getHeight();
} else if (mExpandedChild != null) {
return getViewHeight(VISIBLE_TYPE_EXPANDED)
@@ -752,7 +752,7 @@
}
public int getMinHeight(boolean likeGroupExpanded) {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return getShowingAmbientView().getHeight();
} else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
return getViewHeight(VISIBLE_TYPE_CONTRACTED);
@@ -1039,7 +1039,7 @@
* @return one of the static enum types in this view, calculated form the current state
*/
public int calculateVisibleType() {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
if (mIsChildInGroup && mAmbientSingleLineChild != null) {
return VISIBLE_TYPE_AMBIENT_SINGLELINE;
} else if (mAmbientChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index dec88d4..7e60c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -16,14 +16,13 @@
package com.android.systemui.statusbar.notification.row;
-import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-
import java.util.ArrayList;
+import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -38,25 +37,21 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
import android.service.notification.StatusBarNotification;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
+import com.android.internal.annotations.VisibleForTesting;
+
public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
ExpandableNotificationRow.LayoutListener {
private static final boolean DEBUG = false;
private static final String TAG = "swipe";
- private static final int ICON_ALPHA_ANIM_DURATION = 200;
- private static final long SHOW_MENU_DELAY = 60;
- private static final long SWIPE_MENU_TIMING = 200;
-
// Notification must be swiped at least this fraction of a single menu item to show menu
private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
@@ -65,6 +60,9 @@
// menu item to snap back to menu (else it will cover the menu or it'll be dismissed)
private static final float SWIPED_BACK_ENOUGH_TO_COVER_FRACTION = 0.2f;
+ private static final int ICON_ALPHA_ANIM_DURATION = 200;
+ private static final long SHOW_MENU_DELAY = 60;
+
private ExpandableNotificationRow mParent;
private Context mContext;
@@ -89,22 +87,20 @@
private int[] mIconLocation = new int[2];
private int[] mParentLocation = new int[2];
- private float mHorizSpaceForIcon = -1;
+ private int mHorizSpaceForIcon = -1;
private int mVertSpaceForIcons = -1;
private int mIconPadding = -1;
private int mSidePadding;
private float mAlpha = 0f;
- private float mPrevX;
private CheckForDrag mCheckForDrag;
private Handler mHandler;
- private boolean mMenuSnappedTo;
+ private boolean mMenuSnapped;
private boolean mMenuSnappedOnLeft;
private boolean mShouldShowMenu;
- private NotificationSwipeActionHelper mSwipeHelper;
private boolean mIsUserTouching;
public NotificationMenuRow(Context context) {
@@ -134,9 +130,34 @@
return mSnoozeItem;
}
- @Override
- public void setSwipeActionHelper(NotificationSwipeActionHelper helper) {
- mSwipeHelper = helper;
+ @VisibleForTesting
+ protected ExpandableNotificationRow getParent() {
+ return mParent;
+ }
+
+ @VisibleForTesting
+ protected boolean isMenuOnLeft() {
+ return mOnLeft;
+ }
+
+ @VisibleForTesting
+ protected boolean isMenuSnappedOnLeft() {
+ return mMenuSnappedOnLeft;
+ }
+
+ @VisibleForTesting
+ protected boolean isMenuSnapped() {
+ return mMenuSnapped;
+ }
+
+ @VisibleForTesting
+ protected boolean isDismissing() {
+ return mDismissing;
+ }
+
+ @VisibleForTesting
+ protected boolean isSnapping() {
+ return mSnapping;
}
@Override
@@ -155,17 +176,37 @@
return mAlpha > 0;
}
+ @VisibleForTesting
+ protected boolean isUserTouching() {
+ return mIsUserTouching;
+ }
+
+ @Override
+ public boolean shouldShowMenu() {
+ return mShouldShowMenu;
+ }
+
@Override
public View getMenuView() {
return mMenuContainer;
}
+ @VisibleForTesting
+ protected float getTranslation() {
+ return mTranslation;
+ }
+
@Override
public void resetMenu() {
resetState(true);
}
@Override
+ public void onTouchEnd() {
+ mIsUserTouching = false;
+ }
+
+ @Override
public void onNotificationUpdated(StatusBarNotification sbn) {
if (mMenuContainer == null) {
// Menu hasn't been created yet, no need to do anything.
@@ -222,9 +263,7 @@
mIconsPlaced = false;
setMenuLocation();
if (!mIsUserTouching) {
- // If the # of items showing changed we need to update the snap position
- showMenu(mParent, mOnLeft ? getSpaceForMenu() : -getSpaceForMenu(),
- 0 /* velocity */);
+ onSnapOpen();
}
}
}
@@ -236,7 +275,7 @@
mAnimating = false;
mSnapping = false;
mDismissing = false;
- mMenuSnappedTo = false;
+ mMenuSnapped = false;
setMenuLocation();
if (mMenuListener != null && notify) {
mMenuListener.onMenuReset(mParent);
@@ -244,185 +283,102 @@
}
@Override
- public boolean onTouchEvent(View view, MotionEvent ev, float velocity) {
- final int action = ev.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mSnapping = false;
- if (mFadeAnimator != null) {
- mFadeAnimator.cancel();
- }
- mHandler.removeCallbacks(mCheckForDrag);
+ public void onTouchMove(float delta) {
+ mSnapping = false;
+
+ if (!isTowardsMenu(delta) && isMenuLocationChange()) {
+ // Don't consider it "snapped" if location has changed.
+ mMenuSnapped = false;
+
+ // Changed directions, make sure we check to fade in icon again.
+ if (!mHandler.hasCallbacks(mCheckForDrag)) {
+ // No check scheduled, set null to schedule a new one.
mCheckForDrag = null;
- mPrevX = ev.getRawX();
- mIsUserTouching = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- mSnapping = false;
- float diffX = ev.getRawX() - mPrevX;
- mPrevX = ev.getRawX();
- if (!isTowardsMenu(diffX) && isMenuLocationChange()) {
- // Don't consider it "snapped" if location has changed.
- mMenuSnappedTo = false;
-
- // Changed directions, make sure we check to fade in icon again.
- if (!mHandler.hasCallbacks(mCheckForDrag)) {
- // No check scheduled, set null to schedule a new one.
- mCheckForDrag = null;
- } else {
- // Check scheduled, reset alpha and update location; check will fade it in
- setMenuAlpha(0f);
- setMenuLocation();
- }
- }
- if (mShouldShowMenu
- && !NotificationStackScrollLayout.isPinnedHeadsUp(view)
- && !mParent.areGutsExposed()
- && !mParent.isDark()
- && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
- // Only show the menu if we're not a heads up view and guts aren't exposed.
- mCheckForDrag = new CheckForDrag();
- mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- mIsUserTouching = false;
- return handleUpEvent(ev, view, velocity);
- case MotionEvent.ACTION_CANCEL:
- mIsUserTouching = false;
- cancelDrag();
- return false;
- }
- return false;
- }
-
- private boolean handleUpEvent(MotionEvent ev, View animView, float velocity) {
- // If the menu should not be shown, then there is no need to check if the a swipe
- // should result in a snapping to the menu. As a result, just check if the swipe
- // was enough to dismiss the notification.
- if (!mShouldShowMenu) {
- if (mSwipeHelper.isDismissGesture(ev)) {
- dismiss(animView, velocity);
} else {
- snapBack(animView, velocity);
+ // Check scheduled, reset alpha and update location; check will fade it in
+ setMenuAlpha(0f);
+ setMenuLocation();
}
- return true;
}
-
- final boolean gestureTowardsMenu = isTowardsMenu(velocity);
- final boolean gestureFastEnough =
- mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
- final boolean gestureFarEnough =
- mSwipeHelper.swipedFarEnough(mTranslation, mParent.getWidth());
- final double timeForGesture = ev.getEventTime() - ev.getDownTime();
- final boolean showMenuForSlowOnGoing = !mParent.canViewBeDismissed()
- && timeForGesture >= SWIPE_MENU_TIMING;
- final float menuSnapTarget = mOnLeft ? getSpaceForMenu() : -getSpaceForMenu();
-
- if (DEBUG) {
- Log.d(TAG, "mTranslation= " + mTranslation
- + " mAlpha= " + mAlpha
- + " velocity= " + velocity
- + " mMenuSnappedTo= " + mMenuSnappedTo
- + " mMenuSnappedOnLeft= " + mMenuSnappedOnLeft
- + " mOnLeft= " + mOnLeft
- + " minDismissVel= " + mSwipeHelper.getMinDismissVelocity()
- + " isDismissGesture= " + mSwipeHelper.isDismissGesture(ev)
- + " gestureTowardsMenu= " + gestureTowardsMenu
- + " gestureFastEnough= " + gestureFastEnough
- + " gestureFarEnough= " + gestureFarEnough);
+ if (mShouldShowMenu
+ && !NotificationStackScrollLayout.isPinnedHeadsUp(getParent())
+ && !mParent.areGutsExposed()
+ && !mParent.isDark()
+ && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
+ // Only show the menu if we're not a heads up view and guts aren't exposed.
+ mCheckForDrag = new CheckForDrag();
+ mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
}
-
- if (mMenuSnappedTo && isMenuVisible() && mMenuSnappedOnLeft == mOnLeft) {
- // Menu was snapped to previously and we're on the same side, figure out if
- // we should stick to the menu, snap back into place, or dismiss
- final float maximumSwipeDistance = mHorizSpaceForIcon
- * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
- final float targetLeft = getSpaceForMenu() - maximumSwipeDistance;
- final float targetRight = mParent.getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
- boolean withinSnapMenuThreshold = mOnLeft
- ? mTranslation > targetLeft && mTranslation < targetRight
- : mTranslation < -targetLeft && mTranslation > -targetRight;
- boolean shouldSnapTo = mOnLeft ? mTranslation < targetLeft : mTranslation > -targetLeft;
- if (DEBUG) {
- Log.d(TAG, " withinSnapMenuThreshold= " + withinSnapMenuThreshold
- + " shouldSnapTo= " + shouldSnapTo
- + " targetLeft= " + targetLeft
- + " targetRight= " + targetRight);
- }
- if (withinSnapMenuThreshold && !mSwipeHelper.isDismissGesture(ev)) {
- // Haven't moved enough to unsnap from the menu
- showMenu(animView, menuSnapTarget, velocity);
- } else if (mSwipeHelper.isDismissGesture(ev) && !shouldSnapTo) {
- // Only dismiss if we're not moving towards the menu
- dismiss(animView, velocity);
- } else {
- snapBack(animView, velocity);
- }
- } else if (!mSwipeHelper.isFalseGesture(ev)
- && (swipedEnoughToShowMenu() && (!gestureFastEnough || showMenuForSlowOnGoing))
- || (gestureTowardsMenu && !mSwipeHelper.isDismissGesture(ev))) {
- // Menu has not been snapped to previously and this is menu revealing gesture
- showMenu(animView, menuSnapTarget, velocity);
- } else if (mSwipeHelper.isDismissGesture(ev) && !gestureTowardsMenu) {
- dismiss(animView, velocity);
- } else {
- snapBack(animView, velocity);
- }
- return true;
}
- private void showMenu(View animView, float targetLeft, float velocity) {
- mMenuSnappedTo = true;
- mMenuSnappedOnLeft = mOnLeft;
- mMenuListener.onMenuShown(animView);
- mSwipeHelper.snap(animView, targetLeft, velocity);
+ @VisibleForTesting
+ protected void beginDrag() {
+ mSnapping = false;
+ if (mFadeAnimator != null) {
+ mFadeAnimator.cancel();
+ }
+ mHandler.removeCallbacks(mCheckForDrag);
+ mCheckForDrag = null;
+ mIsUserTouching = true;
}
- private void snapBack(View animView, float velocity) {
+ @Override
+ public void onTouchStart() {
+ beginDrag();
+ }
+
+ @Override
+ public void onSnapOpen() {
+ mMenuSnapped = true;
+ mMenuSnappedOnLeft = isMenuOnLeft();
+ if (mMenuListener != null) {
+ mMenuListener.onMenuShown(getParent());
+ }
+ }
+
+ @Override
+ public void onSnapClosed() {
cancelDrag();
- mMenuSnappedTo = false;
+ mMenuSnapped = false;
mSnapping = true;
- mSwipeHelper.snap(animView, 0 /* leftTarget */, velocity);
}
- private void dismiss(View animView, float velocity) {
+ @Override
+ public void onDismiss() {
cancelDrag();
- mMenuSnappedTo = false;
+ mMenuSnapped = false;
mDismissing = true;
- mSwipeHelper.dismiss(animView, velocity);
}
- private void cancelDrag() {
+ @VisibleForTesting
+ protected void cancelDrag() {
if (mFadeAnimator != null) {
mFadeAnimator.cancel();
}
mHandler.removeCallbacks(mCheckForDrag);
}
- /**
- * @return whether the notification has been translated enough to show the menu and not enough
- * to be dismissed.
- */
- private boolean swipedEnoughToShowMenu() {
- final float multiplier = mParent.canViewBeDismissed()
+ @VisibleForTesting
+ protected float getMinimumSwipeDistance() {
+ final float multiplier = getParent().canViewBeDismissed()
? SWIPED_FAR_ENOUGH_MENU_FRACTION
: SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION;
- final float minimumSwipeDistance = mHorizSpaceForIcon * multiplier;
- return !mSwipeHelper.swipedFarEnough(0, 0) && isMenuVisible()
- && (mOnLeft ? mTranslation > minimumSwipeDistance
- : mTranslation < -minimumSwipeDistance);
+ return mHorizSpaceForIcon * multiplier;
+ }
+
+ @VisibleForTesting
+ protected float getMaximumSwipeDistance() {
+ return mHorizSpaceForIcon * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
}
/**
* Returns whether the gesture is towards the menu location or not.
*/
- private boolean isTowardsMenu(float movement) {
+ @Override
+ public boolean isTowardsMenu(float movement) {
return isMenuVisible()
- && ((mOnLeft && movement <= 0)
- || (!mOnLeft && movement >= 0));
+ && ((isMenuOnLeft() && movement <= 0)
+ || (!isMenuOnLeft() && movement >= 0));
}
@Override
@@ -445,7 +401,7 @@
}
@Override
- public void onHeightUpdate() {
+ public void onParentHeightUpdate() {
if (mParent == null || mMenuItems.size() == 0 || mMenuContainer == null) {
return;
}
@@ -460,7 +416,7 @@
}
@Override
- public void onTranslationUpdate(float translation) {
+ public void onParentTranslationUpdate(float translation) {
mTranslation = translation;
if (mAnimating || !mMenuFadedIn) {
// Don't adjust when animating, or if the menu hasn't been shown yet.
@@ -492,13 +448,15 @@
final int x = mIconLocation[0] - mParentLocation[0] + centerX;
final int y = mIconLocation[1] - mParentLocation[1] + centerY;
final int index = mMenuContainer.indexOfChild(v);
- mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+ if (mMenuListener != null) {
+ mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+ }
}
private boolean isMenuLocationChange() {
boolean onLeft = mTranslation > mIconPadding;
boolean onRight = mTranslation < -mIconPadding;
- if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
+ if ((isMenuOnLeft() && onRight) || (!isMenuOnLeft() && onLeft)) {
return true;
}
return false;
@@ -506,7 +464,7 @@
private void setMenuLocation() {
boolean showOnLeft = mTranslation > 0;
- if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping || mMenuContainer == null
+ if ((mIconsPlaced && showOnLeft == isMenuOnLeft()) || isSnapping() || mMenuContainer == null
|| !mMenuContainer.isAttachedToWindow()) {
// Do nothing
return;
@@ -522,7 +480,8 @@
mIconsPlaced = true;
}
- private void setMenuAlpha(float alpha) {
+ @VisibleForTesting
+ protected void setMenuAlpha(float alpha) {
mAlpha = alpha;
if (mMenuContainer == null) {
return;
@@ -542,7 +501,8 @@
/**
* Returns the horizontal space in pixels required to display the menu.
*/
- private float getSpaceForMenu() {
+ @VisibleForTesting
+ protected int getSpaceForMenu() {
return mHorizSpaceForIcon * mMenuContainer.getChildCount();
}
@@ -646,12 +606,71 @@
parent.addView(menuView);
menuView.setOnClickListener(this);
FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
- lp.width = (int) mHorizSpaceForIcon;
- lp.height = (int) mHorizSpaceForIcon;
+ lp.width = mHorizSpaceForIcon;
+ lp.height = mHorizSpaceForIcon;
menuView.setLayoutParams(lp);
}
}
+ @VisibleForTesting
+ /**
+ * Determine the minimum offset below which the menu should snap back closed.
+ */
+ protected float getSnapBackThreshold() {
+ return getSpaceForMenu() - getMaximumSwipeDistance();
+ }
+
+ /**
+ * Determine the maximum offset above which the parent notification should be dismissed.
+ * @return
+ */
+ @VisibleForTesting
+ protected float getDismissThreshold() {
+ return getParent().getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+ }
+
+ @Override
+ public boolean isWithinSnapMenuThreshold() {
+ float translation = getTranslation();
+ float snapBackThreshold = getSnapBackThreshold();
+ float targetRight = getDismissThreshold();
+ return isMenuOnLeft()
+ ? translation > snapBackThreshold && translation < targetRight
+ : translation < -snapBackThreshold && translation > -targetRight;
+ }
+
+ @Override
+ public boolean isSwipedEnoughToShowMenu() {
+ final float minimumSwipeDistance = getMinimumSwipeDistance();
+ final float translation = getTranslation();
+ return isMenuVisible() && (isMenuOnLeft() ?
+ translation > minimumSwipeDistance
+ : translation < -minimumSwipeDistance);
+ }
+
+ @Override
+ public int getMenuSnapTarget() {
+ return isMenuOnLeft() ? getSpaceForMenu() : -getSpaceForMenu();
+ }
+
+ @Override
+ public boolean shouldSnapBack() {
+ float translation = getTranslation();
+ float targetLeft = getSnapBackThreshold();
+ return isMenuOnLeft() ? translation < targetLeft : translation > -targetLeft;
+ }
+
+ @Override
+ public boolean isSnappedAndOnSameSide() {
+ return isMenuSnapped() && isMenuVisible()
+ && isMenuSnappedOnLeft() == isMenuOnLeft();
+ }
+
+ @Override
+ public boolean canBeDismissed() {
+ return getParent().canViewBeDismissed();
+ }
+
public static class NotificationMenuItem implements MenuItem {
View mMenuView;
GutsContent mGutsContent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 15eaaac..8969aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -20,14 +20,15 @@
import android.content.Context;
import android.view.View;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.ArrayList;
@@ -44,7 +45,7 @@
private int mSpeedBumpIndex = -1;
private boolean mDark;
private boolean mHideSensitive;
- private HeadsUpManager mHeadsUpManager;
+ private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
private float mStackTranslation;
private int mLayoutHeight;
private int mTopPadding;
@@ -207,10 +208,6 @@
mSpeedBumpIndex = shelfIndex;
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
public float getStackTranslation() {
return mStackTranslation;
}
@@ -334,10 +331,10 @@
}
public boolean isPulsing(NotificationData.Entry entry) {
- if (!mPulsing || mHeadsUpManager == null) {
+ if (!mPulsing || mAmbientPulseManager == null) {
return false;
}
- return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+ return mAmbientPulseManager.isAlerting(entry.key);
}
public boolean isPanelTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 3d44e3c..da089b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -213,7 +213,7 @@
// calculated correctly as they are used to calculate how many we can fit on the screen.
boolean isOverflow = i == overflowIndex;
child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null &&
- !mContainingNotification.isShowingAmbient()
+ !mContainingNotification.isOnAmbient()
? mOverflowNumber.getMeasuredWidth() : 0);
child.measure(widthMeasureSpec, newHeightSpec);
// layout the divider
@@ -406,7 +406,7 @@
if (childCount > maxAllowedVisibleChildren) {
int number = childCount - maxAllowedVisibleChildren;
mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
ExpandableNotificationRow overflowView = mChildren.get(0);
HybridNotificationView ambientSingleLineView = overflowView == null ? null
: overflowView.getAmbientSingleLineView();
@@ -522,7 +522,7 @@
if (mUserLocked) {
expandFactor = getGroupExpandFraction();
}
- boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
+ boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isOnAmbient();
for (int i = 0; i < childCount; i++) {
if (visibleChildren >= maxAllowedVisibleChildren) {
break;
@@ -641,7 +641,7 @@
getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
mGroupOverFlowState.alpha = 0.0f;
} else if (!mChildrenExpanded) {
HybridNotificationView alignView = overflowView.getSingleLineView();
@@ -710,7 +710,7 @@
@VisibleForTesting
int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
}
if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
@@ -900,7 +900,7 @@
return mCurrentHeader;
}
- public void notifyShowAmbientChanged() {
+ public void notifyDozingStateChanged() {
updateHeaderVisibility(false);
updateGroupOverflow();
}
@@ -970,7 +970,7 @@
private ViewGroup calculateDesiredHeader() {
ViewGroup desiredHeader;
- if (mContainingNotification.isShowingAmbient()) {
+ if (mContainingNotification.isOnAmbient()) {
desiredHeader = mNotificationHeaderAmbient;
} else if (showingAsLowPriority()) {
desiredHeader = mNotificationHeaderLowPriority;
@@ -1126,7 +1126,7 @@
}
public int getMinHeight() {
- return getMinHeight(mContainingNotification.isShowingAmbient()
+ return getMinHeight(mContainingNotification.isOnAmbient()
? NUMBER_OF_CHILDREN_WHEN_AMBIENT
: NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
}
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 52844fe..72c2c0b 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
@@ -26,7 +26,6 @@
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.FloatRange;
import android.annotation.Nullable;
import android.app.WallpaperManager;
import android.content.Context;
@@ -115,6 +114,8 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -134,8 +135,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.ShadeViewRefactor;
-import java.lang.annotation.ShadeViewRefactor.RefactorComponent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -620,7 +619,8 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class)
+ .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -4248,8 +4248,9 @@
mDimAnimator.addUpdateListener(mDimUpdateListener);
mDimAnimator.start();
}
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setHideSensitive(boolean hideSensitive, boolean animate) {
+ private void setHideSensitive(boolean hideSensitive, boolean animate) {
if (hideSensitive != mAmbientState.isHideSensitive()) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -4826,7 +4827,6 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
- mAmbientState.setHeadsUpManager(headsUpManager);
mHeadsUpManager.addListener(mRoundnessManager);
mHeadsUpManager.setAnimationStateHandler(this);
}
@@ -5008,8 +5008,12 @@
private void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
+ }
+
+ private void onStatePostChange() {
boolean onKeyguard = onKeyguard();
boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
+
if (mHeadsUpAppearanceController != null) {
mHeadsUpAppearanceController.setPublicMode(publicMode);
}
@@ -5026,6 +5030,8 @@
updateFooter();
updateChildren();
onUpdateRowStates();
+
+ mEntryManager.updateNotifications();
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5308,13 +5314,15 @@
void flingTopOverscroll(float velocity, boolean open);
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private class NotificationSwipeHelper extends SwipeHelper
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private class NotificationSwipeHelper extends SwipeHelper
implements NotificationSwipeActionHelper {
private static final long COVER_MENU_DELAY = 4000;
private Runnable mFalsingCheck;
private Handler mHandler;
+ private static final long SWIPE_MENU_TIMING = 200;
+
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
super(swipeDirection, callback, context);
mHandler = new Handler();
@@ -5330,7 +5338,7 @@
public void onDownUpdate(View currView, MotionEvent ev) {
mTranslatingParentView = currView;
if (mCurrMenuRow != null) {
- mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
+ mCurrMenuRow.onTouchStart();
}
mCurrMenuRow = null;
mHandler.removeCallbacks(mFalsingCheck);
@@ -5343,18 +5351,21 @@
if (row.getEntry().hasFinishedInitialization()) {
mCurrMenuRow = row.createMenu();
- mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
- mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
+ mCurrMenuRow.onTouchStart();
}
}
}
+ private boolean swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow) {
+ return !swipedFarEnough() && menuRow.isSwipedEnoughToShowMenu();
+ }
+
@Override
public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
mHandler.removeCallbacks(mFalsingCheck);
if (mCurrMenuRow != null) {
- mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
+ mCurrMenuRow.onTouchMove(delta);
}
}
@@ -5362,12 +5373,92 @@
public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
float translation) {
if (mCurrMenuRow != null) {
- return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
+ mCurrMenuRow.onTouchEnd();
+ handleMenuRowSwipe(ev, animView, velocity, mCurrMenuRow);
+ return true;
}
return false;
}
@Override
+ public boolean swipedFarEnough(float translation, float viewSize) {
+ return swipedFarEnough();
+ }
+
+ private void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity,
+ NotificationMenuRowPlugin menuRow) {
+ if (!menuRow.shouldShowMenu()) {
+ // If the menu should not be shown, then there is no need to check if the a swipe
+ // should result in a snapping to the menu. As a result, just check if the swipe
+ // was enough to dismiss the notification.
+ if (isDismissGesture(ev)) {
+ dismiss(animView, velocity);
+ } else {
+ snapBack(animView, velocity);
+ menuRow.onSnapClosed();
+ }
+ return;
+ }
+
+ if (menuRow.isSnappedAndOnSameSide()) {
+ // Menu was snapped to previously and we're on the same side
+ handleSwipeFromSnap(ev, animView, velocity, menuRow);
+ } else {
+ // Menu has not been snapped, or was snapped previously but is now on
+ // the opposite side.
+ handleSwipeFromNonSnap(ev, animView, velocity, menuRow);
+ }
+ }
+
+ private void handleSwipeFromNonSnap(MotionEvent ev, View animView, float velocity,
+ NotificationMenuRowPlugin menuRow) {
+ boolean isDismissGesture = isDismissGesture(ev);
+ final boolean gestureTowardsMenu = menuRow.isTowardsMenu(velocity);
+ final boolean gestureFastEnough =
+ mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
+
+ final double timeForGesture = ev.getEventTime() - ev.getDownTime();
+ final boolean showMenuForSlowOnGoing = !menuRow.canBeDismissed()
+ && timeForGesture >= SWIPE_MENU_TIMING;
+
+ if (!isFalseGesture(ev)
+ && (swipedEnoughToShowMenu(menuRow)
+ && (!gestureFastEnough || showMenuForSlowOnGoing))
+ || (gestureTowardsMenu && !isDismissGesture)) {
+ // Menu has not been snapped to previously and this is menu revealing gesture
+ snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
+ menuRow.onSnapOpen();
+ } else if (isDismissGesture(ev) && !gestureTowardsMenu) {
+ dismiss(animView, velocity);
+ menuRow.onDismiss();
+ } else {
+ snapBack(animView, velocity);
+ menuRow.onSnapClosed();
+ }
+ }
+
+ private void handleSwipeFromSnap(MotionEvent ev, View animView, float velocity,
+ NotificationMenuRowPlugin menuRow) {
+ boolean isDismissGesture = isDismissGesture(ev);
+
+ final boolean withinSnapMenuThreshold =
+ menuRow.isWithinSnapMenuThreshold();
+
+ if (withinSnapMenuThreshold && !isDismissGesture) {
+ // Haven't moved enough to unsnap from the menu
+ menuRow.onSnapOpen();
+ snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
+ } else if (isDismissGesture && !menuRow.shouldSnapBack()) {
+ // Only dismiss if we're not moving towards the menu
+ dismiss(animView, velocity);
+ menuRow.onDismiss();
+ } else {
+ snapBack(animView, velocity);
+ menuRow.onSnapClosed();
+ }
+ }
+
+ @Override
public void dismissChild(final View view, float velocity,
boolean useAccelerateInterpolator) {
super.dismissChild(view, velocity, useAccelerateInterpolator);
@@ -5396,10 +5487,6 @@
mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
}
- public boolean isFalseGesture(MotionEvent ev) {
- return super.isFalseGesture(ev);
- }
-
private void handleMenuCoveredOrDismissed() {
if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
mMenuExposedView = null;
@@ -5433,13 +5520,12 @@
}
@Override
- public void snap(View animView, float targetLeft, float velocity) {
+ public void snapOpen(View animView, int targetLeft, float velocity) {
snapChild(animView, targetLeft, velocity);
}
- @Override
- public boolean swipedFarEnough(float translation, float viewSize) {
- return swipedFarEnough();
+ private void snapBack(View animView, float velocity) {
+ snapChild(animView, 0, velocity);
}
@Override
@@ -5949,5 +6035,10 @@
public void onStateChanged(int newState) {
setStatusBarState(newState);
}
- };
+
+ @Override
+ public void onStatePostChange() {
+ NotificationStackScrollLayout.this.onStatePostChange();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 235ff2b..c32dcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -67,6 +67,10 @@
mView.setBackground(mBarBackground);
}
+ public void destroy() {
+ // To be overridden
+ }
+
public int getMode() {
return mMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 81b596c..424acfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -34,9 +34,11 @@
private val inCarMode: Boolean
private var uiMode: Int = 0
private var localeList: LocaleList? = null
+ private val context: Context
init {
val currentConfig = context.resources.configuration
+ this.context = context
fontScale = currentConfig.fontScale
density = currentConfig.densityDpi
inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
@@ -82,6 +84,10 @@
}
if (uiModeChanged) {
+ // We need to force the style re-evaluation to make sure that it's up to date
+ // and attrs were reloaded.
+ context.theme.applyStyle(context.themeResId, true)
+
this.uiMode = uiMode
listeners.filterForEach({ this.listeners.contains(it) }) {
it.onUiModeChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 0d3ba77..25db4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -63,9 +63,14 @@
if (!mDozing) {
return;
}
- mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
- mHandler.postDelayed(mPulseOutExtended,
- mDozeParameters.getPulseVisibleDurationExtended());
+ // All pulses except notifications should time out on their own. Pulses due to
+ // notifications should instead be managed externally based off the notification's
+ // lifetime.
+ if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION) {
+ mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+ mHandler.postDelayed(mPulseOutExtended,
+ mDozeParameters.getPulseVisibleDurationExtended());
+ }
mFullyPulsing = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4a05989..cfc3271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -177,7 +177,7 @@
mReleaseOnExpandFinish = false;
} else {
for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- if (contains(entry.key)) {
+ if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
}
@@ -345,7 +345,7 @@
public void onReorderingAllowed() {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (contains(entry.key)) {
+ if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
}
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 10b019d..66486ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -307,7 +307,10 @@
@Override
public void onDestroyView() {
super.onDestroyView();
- mNavigationBarView.getLightTransitionsController().destroy(getContext());
+ if (mNavigationBarView != null) {
+ mNavigationBarView.getBarTransitions().destroy();
+ mNavigationBarView.getLightTransitionsController().destroy(getContext());
+ }
mOverviewProxyService.removeCallback(mOverviewProxyListener);
getContext().unregisterReceiver(mBroadcastReceiver);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index e09d31c..d58b554 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -44,6 +44,17 @@
private boolean mAutoDim;
private View mNavButtons;
+ private final Handler mHandler = Handler.getMain();
+ private final IWallpaperVisibilityListener mWallpaperVisibilityListener =
+ new IWallpaperVisibilityListener.Stub() {
+ @Override
+ public void onWallpaperVisibilityChanged(boolean newVisibility,
+ int displayId) throws RemoteException {
+ mWallpaperVisible = newVisibility;
+ mHandler.post(() -> applyLightsOut(true, false));
+ }
+ };
+
public NavigationBarTransitions(NavigationBarView view) {
super(view, R.drawable.nav_background);
mView = view;
@@ -55,17 +66,9 @@
.getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
- Handler handler = Handler.getMain();
try {
mWallpaperVisible = windowManagerService.registerWallpaperVisibilityListener(
- new IWallpaperVisibilityListener.Stub() {
- @Override
- public void onWallpaperVisibilityChanged(boolean newVisibility,
- int displayId) throws RemoteException {
- mWallpaperVisible = newVisibility;
- handler.post(() -> applyLightsOut(true, false));
- }
- }, Display.DEFAULT_DISPLAY);
+ mWallpaperVisibilityListener, Display.DEFAULT_DISPLAY);
} catch (RemoteException e) {
}
mView.addOnLayoutChangeListener(
@@ -88,6 +91,16 @@
}
@Override
+ public void destroy() {
+ IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
+ try {
+ windowManagerService.unregisterWallpaperVisibilityListener(mWallpaperVisibilityListener,
+ Display.DEFAULT_DISPLAY);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void setAutoDim(boolean autoDim) {
if (mAutoDim == autoDim) return;
mAutoDim = autoDim;
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 2141016..9d13ea2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -615,6 +615,7 @@
((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
getImeSwitchButton().setImageDrawable(mImeIcon);
+ updateContextualContainerVisibility();
// Update menu button, visibility logic in method
setMenuVisibility(mShowMenu, true);
@@ -796,6 +797,7 @@
((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
+ updateContextualContainerVisibility();
}
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
@@ -810,6 +812,7 @@
getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
getAccessibilityButton().setLongClickable(longClickable);
+ updateContextualContainerVisibility();
}
public void updateRotateSuggestionButtonStyle(@StyleRes int style, boolean setIcon) {
@@ -839,6 +842,7 @@
getRotateSuggestionButton().setVisibility(vis);
mShowRotateButton = visible;
+ updateContextualContainerVisibility();
// Stop any active animations if hidden
if (!visible && mRotateSuggestionIcon.canAnimate()) {
@@ -855,6 +859,13 @@
public boolean isRotateButtonVisible() { return mShowRotateButton; }
+ private void updateContextualContainerVisibility() {
+ // Only show the menu container when one of its buttons are visible
+ getMenuContainer().setVisibility((mShowAccessibilityButton || mShowRotateButton || mShowMenu
+ || (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0)
+ ? VISIBLE : INVISIBLE);
+ }
+
void hideRecentsOnboarding() {
mRecentsOnboarding.hide(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
index 7b9ed88..09833d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
@@ -98,7 +98,6 @@
return mClickableChildren
.stream()
.filter(v -> v.isAttachedToWindow())
- .filter(v -> v.isFocusable())
.map(v -> new Pair<>(distance(v, event), v))
.min(Comparator.comparingInt(f -> f.first))
.get().second;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 5fe362f..c08366a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.NonNull;
import android.app.Notification;
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
@@ -23,6 +24,9 @@
import android.util.Log;
import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -43,15 +47,18 @@
/**
* A class to handle notifications and their corresponding groups.
*/
-public class NotificationGroupManager implements OnHeadsUpChangedListener {
+public class NotificationGroupManager implements OnHeadsUpChangedListener,
+ OnAmbientChangedListener {
private static final String TAG = "NotificationGroupManager";
- private static final long HEADS_UP_TRANSFER_TIMEOUT = 300;
+ private static final long ALERT_TRANSFER_TIMEOUT = 300;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private OnGroupChangeListener mListener;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
+ private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+ private boolean mIsDozing;
private boolean mIsUpdatingUnchangedGroup;
private HashMap<String, NotificationData.Entry> mPendingNotifications;
@@ -162,40 +169,58 @@
mListener.onGroupCreatedFromChildren(group);
}
}
- cleanUpHeadsUpStatesOnAdd(group, false /* addIsPending */);
+ cleanUpAlertStatesOnAdd(group, false /* addIsPending */);
}
public void onPendingEntryAdded(NotificationData.Entry shadeEntry) {
String groupKey = getGroupKey(shadeEntry.notification);
NotificationGroup group = mGroupMap.get(groupKey);
if (group != null) {
- cleanUpHeadsUpStatesOnAdd(group, true /* addIsPending */);
+ cleanUpAlertStatesOnAdd(group, true /* addIsPending */);
}
}
/**
- * Clean up the heads up states when a new child was added.
+ * Set whether or not the device is dozing. This allows the group manager to reset some
+ * specific alert state logic based off when the state changes.
+ * @param isDozing if the device is dozing.
+ */
+ public void setDozing(boolean isDozing) {
+ if (mIsDozing != isDozing) {
+ for (NotificationGroup group : mGroupMap.values()) {
+ group.lastAlertTransfer = 0;
+ group.alertSummaryOnNextAddition = false;
+ }
+ }
+ mIsDozing = isDozing;
+ }
+
+ /**
+ * Clean up the alert states when a new child was added.
* @param group The group where a view was added or will be added.
* @param addIsPending True if is the addition still pending or false has it already been added.
*/
- private void cleanUpHeadsUpStatesOnAdd(NotificationGroup group, boolean addIsPending) {
- if (!addIsPending && group.hunSummaryOnNextAddition) {
- if (!mHeadsUpManager.contains(group.summary.key)) {
- mHeadsUpManager.showNotification(group.summary);
+ private void cleanUpAlertStatesOnAdd(NotificationGroup group, boolean addIsPending) {
+
+ AlertingNotificationManager alertManager =
+ mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
+ if (!addIsPending && group.alertSummaryOnNextAddition) {
+ if (!alertManager.isAlerting(group.summary.key)) {
+ alertManager.showNotification(group.summary);
}
- group.hunSummaryOnNextAddition = false;
+ group.alertSummaryOnNextAddition = false;
}
// Because notification groups are not delivered as a whole unit, it may happen that a
// group child gets added quite a bit after the summary got posted. Our guidance is, that
// apps should always post the group summary as well and we'll hide it for them if the child
- // is the only child in a group. Because of this, we also have to transfer heads up to the
- // child, otherwise the invisible summary would be heads-upped.
+ // is the only child in a group. Because of this, we also have to transfer alert to the
+ // child, otherwise the invisible summary would be alerted.
// This transfer to the child is not always correct in case the app has just posted another
// child in addition to the existing one, but it hasn't arrived in systemUI yet. In such
- // a scenario we would transfer the heads up to the old child and the wrong notification
- // would be heads-upped. In oder to avoid this, we'll recover from this issue and hun the
+ // a scenario we would transfer the alert to the old child and the wrong notification
+ // would be alerted. In order to avoid this, we'll recover from this issue and alert the
// summary again instead of the old child if it's within a certain timeout.
- if (SystemClock.elapsedRealtime() - group.lastHeadsUpTransfer < HEADS_UP_TRANSFER_TIMEOUT) {
+ if (SystemClock.elapsedRealtime() - group.lastAlertTransfer < ALERT_TRANSFER_TIMEOUT) {
if (!onlySummaryAlerts(group.summary)) {
return;
}
@@ -215,26 +240,24 @@
int size = children.size();
for (int i = 0; i < size; i++) {
NotificationData.Entry entry = children.get(i);
- if (onlySummaryAlerts(entry) && entry.row.isHeadsUp()) {
+ if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.key, true /* releaseImmediately */);
+ alertManager.removeNotification(entry.key, true /* releaseImmediately */);
}
}
if (isolatedChild != null && onlySummaryAlerts(isolatedChild)
- && isolatedChild.row.isHeadsUp()) {
+ && alertManager.isAlerting(isolatedChild.key)) {
releasedChild = true;
- mHeadsUpManager.removeNotification(
- isolatedChild.key, true /* releaseImmediately */);
+ alertManager.removeNotification(isolatedChild.key, true /* releaseImmediately */);
}
- if (releasedChild && !mHeadsUpManager.contains(group.summary.key)) {
+ if (releasedChild && !alertManager.isAlerting(group.summary.key)) {
boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
if (notifyImmediately) {
- mHeadsUpManager.showNotification(group.summary);
+ alertManager.showNotification(group.summary);
} else {
- group.hunSummaryOnNextAddition = true;
+ group.alertSummaryOnNextAddition = true;
}
- group.lastHeadsUpTransfer = 0;
+ group.lastAlertTransfer = 0;
}
}
}
@@ -264,8 +287,8 @@
}
private void onEntryBecomingChild(NotificationData.Entry entry) {
- if (entry.row.isHeadsUp()) {
- onHeadsUpStateChanged(entry, true);
+ if (shouldIsolate(entry)) {
+ isolateNotification(entry);
}
}
@@ -281,7 +304,11 @@
&& hasIsolatedChildren(group)));
if (prevSuppressed != group.suppressed) {
if (group.suppressed) {
- handleSuppressedSummaryHeadsUpped(group.summary);
+ if (mHeadsUpManager.isAlerting(group.summary.key)) {
+ handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
+ } else if (mAmbientPulseManager.isAlerting(group.summary.key)) {
+ handleSuppressedSummaryAlerted(group.summary, mAmbientPulseManager);
+ }
}
if (!mIsUpdatingUnchangedGroup && mListener != null) {
mListener.onGroupsChanged();
@@ -415,10 +442,10 @@
return false;
}
NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
- if (group == null) {
+ if (group == null || group.summary == null) {
return false;
}
- return !group.children.isEmpty();
+ return !group.children.isEmpty() && Objects.equals(group.summary.notification, sbn);
}
/**
@@ -495,54 +522,56 @@
}
@Override
+ public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+ onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
+ }
+
+ @Override
public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ }
+
+ private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting,
+ AlertingNotificationManager alertManager) {
final StatusBarNotification sbn = entry.notification;
- if (entry.row.isHeadsUp()) {
- if (shouldIsolate(sbn)) {
- // We will be isolated now, so lets update the groups
- onEntryRemovedInternal(entry, entry.notification);
-
- mIsolatedEntries.put(sbn.getKey(), sbn);
-
- onEntryAdded(entry);
- // We also need to update the suppression of the old group, because this call comes
- // even before the groupManager knows about the notification at all.
- // When the notification gets added afterwards it is already isolated and therefore
- // it doesn't lead to an update.
- updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
- mListener.onGroupsChanged();
- } else {
- handleSuppressedSummaryHeadsUpped(entry);
+ if (isAlerting) {
+ if (shouldIsolate(entry)) {
+ isolateNotification(entry);
+ } else if (sbn.getNotification().isGroupSummary()
+ && isGroupSuppressed(sbn.getGroupKey())){
+ handleSuppressedSummaryAlerted(entry, alertManager);
}
} else {
- if (mIsolatedEntries.containsKey(sbn.getKey())) {
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.notification);
- mIsolatedEntries.remove(sbn.getKey());
- onEntryAdded(entry);
- mListener.onGroupsChanged();
- }
+ stopIsolatingNotification(entry);
}
}
- private void handleSuppressedSummaryHeadsUpped(NotificationData.Entry entry) {
- StatusBarNotification sbn = entry.notification;
+ /**
+ * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
+ * summary should for all intents and purposes be invisible to the user and as a result should
+ * not alert. When this is the case, it is our responsibility to pass the alert to the
+ * appropriate child which will be the representative notification alerting for the group.
+ * @param summary the summary that is suppressed and alerting
+ * @param alertManager the alert manager that manages the alerting summary
+ */
+ private void handleSuppressedSummaryAlerted(@NonNull NotificationData.Entry summary,
+ @NonNull AlertingNotificationManager alertManager) {
+ StatusBarNotification sbn = summary.notification;
if (!isGroupSuppressed(sbn.getGroupKey())
|| !sbn.getNotification().isGroupSummary()
- || !entry.row.isHeadsUp()) {
+ || !alertManager.isAlerting(sbn.getKey())) {
return;
}
- // The parent of a suppressed group got huned, lets hun the child!
+ // The parent of a suppressed group got alerted, lets alert the child!
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
- if (pendingInflationsWillAddChildren(notificationGroup)) {
- // New children will actually be added to this group, let's not transfer the heads
- // up
- return;
- }
-
if (notificationGroup != null) {
+ if (pendingInflationsWillAddChildren(notificationGroup)) {
+ // New children will actually be added to this group, let's not transfer the alert.
+ return;
+ }
+
Iterator<NotificationData.Entry> iterator
= notificationGroup.children.values().iterator();
NotificationData.Entry child = iterator.hasNext() ? iterator.next() : null;
@@ -551,20 +580,35 @@
}
if (child != null) {
if (child.row.keepInParent() || child.row.isRemoved() || child.row.isDismissed()) {
- // the notification is actually already removed, no need to do heads-up on it.
+ // the notification is actually already removed, no need to do alert on it.
return;
}
- if (mHeadsUpManager.contains(child.key)) {
- mHeadsUpManager.updateNotification(child.key, true /* alert */);
- } else {
- if (onlySummaryAlerts(entry)) {
- notificationGroup.lastHeadsUpTransfer = SystemClock.elapsedRealtime();
- }
- mHeadsUpManager.showNotification(child);
- }
+ transferAlertStateToChild(summary, child, alertManager);
}
}
- mHeadsUpManager.removeNotification(entry.key, true /* releaseImmediately */);
+ }
+
+ /**
+ * Transfers the alert state from a given summary notification to the specified child. The
+ * result is the child will now alert while the summary does not.
+ *
+ * @param summary the currently alerting summary notification
+ * @param child the child that should receive the alert
+ * @param alertManager the manager for the alert
+ */
+ private void transferAlertStateToChild(@NonNull NotificationData.Entry summary,
+ @NonNull NotificationData.Entry child,
+ @NonNull AlertingNotificationManager alertManager) {
+ NotificationGroup notificationGroup = mGroupMap.get(summary.notification.getGroupKey());
+ if (alertManager.isAlerting(child.key)) {
+ alertManager.updateNotification(child.key, true /* alert */);
+ } else {
+ if (onlySummaryAlerts(summary)) {
+ notificationGroup.lastAlertTransfer = SystemClock.elapsedRealtime();
+ }
+ alertManager.showNotification(child);
+ }
+ alertManager.removeNotification(summary.key, true /* releaseImmediately */);
}
private boolean onlySummaryAlerts(NotificationData.Entry entry) {
@@ -596,13 +640,69 @@
return false;
}
- private boolean shouldIsolate(StatusBarNotification sbn) {
+ /**
+ * Whether a notification that is normally part of a group should be temporarily isolated from
+ * the group and put in their own group visually. This generally happens when the notification
+ * is alerting.
+ *
+ * @param entry the notification to check
+ * @return true if the entry should be isolated
+ */
+
+ private boolean shouldIsolate(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
- return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
- && (sbn.getNotification().fullScreenIntent != null
- || notificationGroup == null
- || !notificationGroup.expanded
- || isGroupNotFullyVisible(notificationGroup));
+ if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
+ return false;
+ }
+ if (!mIsDozing && !mHeadsUpManager.isAlerting(entry.key)) {
+ return false;
+ }
+ if (mIsDozing && !mAmbientPulseManager.isAlerting(entry.key)) {
+ return false;
+ }
+ return (sbn.getNotification().fullScreenIntent != null
+ || notificationGroup == null
+ || !notificationGroup.expanded
+ || isGroupNotFullyVisible(notificationGroup));
+ }
+
+ /**
+ * Isolate a notification from its group so that it visually shows as its own group.
+ *
+ * @param entry the notification to isolate
+ */
+ private void isolateNotification(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+
+ // We will be isolated now, so lets update the groups
+ onEntryRemovedInternal(entry, entry.notification);
+
+ mIsolatedEntries.put(sbn.getKey(), sbn);
+
+ onEntryAdded(entry);
+ // We also need to update the suppression of the old group, because this call comes
+ // even before the groupManager knows about the notification at all.
+ // When the notification gets added afterwards it is already isolated and therefore
+ // it doesn't lead to an update.
+ updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
+ mListener.onGroupsChanged();
+ }
+
+ /**
+ * Stop isolating a notification and re-group it with its original logical group.
+ *
+ * @param entry the notification to un-isolate
+ */
+ private void stopIsolatingNotification(NotificationData.Entry entry) {
+ StatusBarNotification sbn = entry.notification;
+ if (mIsolatedEntries.containsKey(sbn.getKey())) {
+ // not isolated anymore, we need to update the groups
+ onEntryRemovedInternal(entry, entry.notification);
+ mIsolatedEntries.remove(sbn.getKey());
+ onEntryAdded(entry);
+ mListener.onGroupsChanged();
+ }
}
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
@@ -641,11 +741,11 @@
*/
public boolean suppressed;
/**
- * The time when the last heads transfer from group to child happened, while the summary
- * has the flags to heads up on its own.
+ * The time when the last alert transfer from group to child happened, while the summary
+ * has the flags to alert up on its own.
*/
- public long lastHeadsUpTransfer;
- public boolean hunSummaryOnNextAddition;
+ public long lastAlertTransfer;
+ public boolean alertSummaryOnNextAddition;
@Override
public String toString() {
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 bd4d790..30e6afa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -77,6 +77,8 @@
/** Experiment to swipe home button left to execute a back key press */
private static final String PULL_HOME_GO_BACK_PROP = "persist.quickstepcontroller.homegoesback";
private static final String HIDE_BACK_BUTTON_PROP = "persist.quickstepcontroller.hideback";
+ private static final String BACK_AFTER_END_PROP
+ = "persist.quickstepcontroller.homegoesbackwhenend";
private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
@@ -84,9 +86,6 @@
/** When the home-swipe-back gesture is disallowed, make it harder to pull */
private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
- /** When dragging the home button too far during back gesture, make it harder to pull */
- private static final float EXCEED_DRAG_HOME_DAMPING_FACTOR = 0.33f;
-
private NavigationBarView mNavigationBarView;
private boolean mQuickScrubActive;
@@ -361,7 +360,7 @@
// Once the user drags the home button past a certain limit, the distance
// will lessen as the home button dampens showing that it was pulled too far
float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
- * EXCEED_DRAG_HOME_DAMPING_FACTOR;
+ * DISALLOW_GESTURE_DAMPING_FACTOR;
diff = (int)(distanceAfterDragLimit + mHomeBackGestureDragLimit);
if (mDragPositive) {
diff *= -1;
@@ -580,15 +579,21 @@
if (!mBackGestureActive) {
mBackGestureActive = true;
mNavigationBarView.getHomeButton().abortCurrentGesture();
+ final boolean runBackMidGesture
+ = !SystemProperties.getBoolean(BACK_AFTER_END_PROP, false);
if (mCanPerformBack) {
if (!shouldhideBackButton()) {
mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
BACK_BUTTON_FADE_OUT_ALPHA);
}
- performBack();
+ if (runBackMidGesture) {
+ performBack();
+ }
}
mHandler.removeCallbacks(mExecuteBackRunnable);
- mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
+ if (runBackMidGesture) {
+ mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
+ }
}
}
@@ -609,6 +614,9 @@
mNavigationBarView.getBackButton().setAlpha(
mOverviewEventSender.getBackButtonAlpha(), true /* animate */);
}
+ if (SystemProperties.getBoolean(BACK_AFTER_END_PROP, false)) {
+ performBack();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7ec4db2..e1d8638 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -187,6 +187,7 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.AppOpsListener;
import com.android.systemui.statusbar.BackDropView;
@@ -254,7 +255,7 @@
ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter,
- StatusBarStateController.StateListener {
+ StatusBarStateController.StateListener, AmbientPulseManager.OnAmbientChangedListener {
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -632,7 +633,7 @@
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
- mStatusBarStateController.addListener(this);
+ mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
@@ -732,10 +733,12 @@
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
- try {
- vrManager.registerListener(mVrStateCallbacks);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ if (vrManager != null) {
+ try {
+ vrManager.registerListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ }
}
IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
@@ -863,6 +866,8 @@
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
+ mAmbientPulseManager.addListener(this);
+ mAmbientPulseManager.addListener(mGroupManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
@@ -1142,11 +1147,6 @@
@Override
public void onUiModeChanged() {
- // UiMode will change the style was already evaluated.
- // We need to force the re-evaluation to make sure that all parents
- // are up to date and new attrs will be rettrieved.
- mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
-
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onUiModeChanged();
}
@@ -1236,7 +1236,7 @@
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
if (mNotificationPanel.hasPulsingNotifications() &&
- !mHeadsUpManager.hasNotifications()) {
+ !mAmbientPulseManager.hasNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -1700,8 +1700,12 @@
}
@Override
- public boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
- if (mIsOccluded && !isDozing()) {
+ public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+ if (isDozing()) {
+ return false;
+ }
+
+ if (mIsOccluded) {
boolean devicePublic = mLockscreenUserManager.
isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
@@ -1714,17 +1718,14 @@
if (!panelsEnabled()) {
if (DEBUG) {
- Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey());
+ Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
}
return false;
}
if (sbn.getNotification().fullScreenIntent != null) {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
- return false;
- } else if (isDozing()) {
- // We never want heads up when we are dozing.
+ if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
return false;
} else {
// we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
@@ -1800,9 +1801,16 @@
@Override
public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
mEntryManager.updateNotificationRanking(null /* rankingMap */);
+ }
- if (isHeadsUp) {
- mDozeServiceHost.fireNotificationHeadsUp();
+ @Override
+ public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+ mEntryManager.updateNotificationRanking(null);
+ if (isAmbient) {
+ mDozeServiceHost.fireNotificationPulse();
+ } else if (!mAmbientPulseManager.hasNotifications()) {
+ // There are no longer any notifications to show. We should end the pulse now.
+ mDozeScrimController.pulseOutNow();
}
}
@@ -3456,7 +3464,14 @@
mIsKeyguard = false;
Trace.beginSection("StatusBar#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- mStatusBarStateController.setState(StatusBarState.SHADE);
+ if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) {
+ //TODO: StatusBarStateController should probably know about hiding the keyguard and
+ // notify listeners.
+
+ // If the state didn't change, we may still need to update public mode
+ updatePublicMode();
+ mEntryManager.updateNotifications();
+ }
View viewToClick = null;
if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
if (!mKeyguardRequested) {
@@ -3599,6 +3614,7 @@
mKeyguardIndicationController.setDozing(mDozing);
mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
mNotificationLogger.setDozing(mDozing);
+ mGroupManager.setDozing(mDozing);
updateQsExpansionEnabled();
Trace.endSection();
}
@@ -4145,6 +4161,7 @@
@Override
public void onStartedWakingUp() {
mDeviceInteractive = true;
+ mAmbientPulseManager.releaseAllImmediately();
mVisualStabilityManager.setScreenOn(true);
mNotificationPanel.setTouchAndAnimationDisabled(false);
mDozeServiceHost.stopDozing();
@@ -4177,11 +4194,7 @@
public void onScreenTurnedOff() {
mFalsingManager.onScreenOff();
mScrimController.onScreenTurnedOff();
- // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
- // in that case destroys the HeadsUpManager state, so don't do it in that case.
- if (!isPulsing()) {
- updateIsKeyguard();
- }
+ updateIsKeyguard();
}
};
@@ -4420,9 +4433,9 @@
}
}
- public void fireNotificationHeadsUp() {
+ public void fireNotificationPulse() {
for (Callback callback : mCallbacks) {
- callback.onNotificationHeadsUp();
+ callback.onNotificationAlerted();
}
}
@@ -4458,7 +4471,7 @@
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- if (mHeadsUpManager.hasNotifications()) {
+ if (mAmbientPulseManager.hasNotifications()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
setPulsing(true);
@@ -4539,7 +4552,11 @@
@Override
public void extendPulse() {
- mDozeScrimController.extendPulse();
+ if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) {
+ mAmbientPulseManager.extendPulse();
+ } else {
+ mDozeScrimController.extendPulse();
+ }
}
@Override
@@ -4625,6 +4642,8 @@
// for heads up notifications
protected HeadsUpManagerPhone mHeadsUpManager;
+ protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
@@ -4730,7 +4749,7 @@
final boolean wasOccluded = mIsOccluded;
dismissKeyguardThenExecute(() -> {
// TODO: Some of this code may be able to move to NotificationEntryManager.
- if (mHeadsUpManager != null && mHeadsUpManager.contains(notificationKey)) {
+ if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
// Release the HUN notification to the shade.
if (isPresenterFullyCollapsed()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e8389af..3db1456 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -460,6 +460,11 @@
boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowController.setKeyguardFadingAway(true);
+ // hide() will happen asynchronously and might arrive after the scrims
+ // were already hidden, this means that the transition callback won't
+ // be triggered anymore and StatusBarWindowController will be forever in
+ // the fadingAway state.
+ mStatusBar.updateScrimController();
wakeAndUnlockDejank();
} else {
mStatusBar.finishKeyguardFadingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 167bba6..57c7e28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -93,7 +93,8 @@
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).addListener(
+ mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -135,6 +136,7 @@
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
+ onThemeChanged();
}
public void setDozeScreenBrightness(int value) {
@@ -483,6 +485,10 @@
@Override
public void onThemeChanged() {
+ if (mStatusBarView == null) {
+ return;
+ }
+
StatusBarStateController state = Dependency.get(StatusBarStateController.class);
int which;
if (state.getState() == StatusBarState.KEYGUARD
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 3c16329..e9efaa1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -205,6 +205,8 @@
public void onTetheringFailed() {
if (DEBUG) Log.d(TAG, "onTetheringFailed");
mWaitingForCallback = false;
+ // TODO(b/110697252): stopTethering must be called to reset soft ap state after failure
+ mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
fireHotspotChangedCallback(isHotspotEnabled());
// TODO: Show error.
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 52b813f..7dd0d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -586,7 +586,8 @@
final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
if (mShowImeOnInputConnection && inputConnection != null) {
- final InputMethodManager imm = InputMethodManager.getInstance();
+ final InputMethodManager imm =
+ getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
// onCreateInputConnection is called by InputMethodManager in the middle of
// setting up the connection to the IME; wait with requesting the IME until that
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 0adb439..29a6b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -403,7 +403,7 @@
boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
return new Pair<Integer, Boolean>(userId[0], hasCACerts);
} catch (RemoteException | InterruptedException | AssertionError e) {
- Log.i(TAG, e.getMessage());
+ Log.i(TAG, "failed to get CA certs", e);
new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
() -> new CACertLoader().execute(userId[0]),
CA_CERT_LOADING_RETRY_TIME_IN_MS);
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 8b1324a..e73c70b 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -17,6 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:sharedUserId="android.uid.system"
package="com.android.systemui.tests">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index c2da7f5..5c8336c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -88,14 +88,14 @@
mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
- mHost.callback.onNotificationHeadsUp();
+ mHost.callback.onNotificationAlerted();
mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- mHost.callback.onNotificationHeadsUp();
+ mHost.callback.onNotificationAlerted();
mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f21ce27..8b41516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -56,19 +56,19 @@
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
- private static final int TEST_MINIMUM_DISPLAY_TIME = 200;
- private static final int TEST_AUTO_DISMISS_TIME = 500;
+ protected static final int TEST_MINIMUM_DISPLAY_TIME = 200;
+ protected static final int TEST_AUTO_DISMISS_TIME = 500;
// Number of notifications to use in tests requiring multiple notifications
private static final int TEST_NUM_NOTIFICATIONS = 4;
- private static final int TEST_TIMEOUT_TIME = 10000;
- private final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
+ protected static final int TEST_TIMEOUT_TIME = 10000;
+ protected final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
private AlertingNotificationManager mAlertingNotificationManager;
protected NotificationData.Entry mEntry;
protected Handler mTestHandler;
private StatusBarNotification mSbn;
- private boolean mTimedOut = false;
+ protected boolean mTimedOut = false;
@Mock protected ExpandableNotificationRow mRow;
@@ -122,7 +122,7 @@
public void testShowNotification_addsEntry() {
mAlertingNotificationManager.showNotification(mEntry);
- assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
assertTrue(mAlertingNotificationManager.hasNotifications());
assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.key));
}
@@ -136,7 +136,7 @@
TestableLooper.get(this).processMessages(1);
assertFalse("Test timed out", mTimedOut);
- assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -146,7 +146,7 @@
// Try to remove but defer, since the notification has not been shown long enough.
mAlertingNotificationManager.removeNotification(mEntry.key, false /* releaseImmediately */);
- assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -156,7 +156,7 @@
// Remove forcibly with releaseImmediately = true.
mAlertingNotificationManager.removeNotification(mEntry.key, true /* releaseImmediately */);
- assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
}
@Test
@@ -174,10 +174,18 @@
}
@Test
- public void testShouldExtendLifetime_notShownLongEnough() {
+ public void testCanRemoveImmediately_notShownLongEnough() {
mAlertingNotificationManager.showNotification(mEntry);
- // The entry has just been added so the lifetime should be extended
+ // The entry has just been added so we should not remove immediately.
+ assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.key));
+ }
+
+ @Test
+ public void testShouldExtendLifetime() {
+ mAlertingNotificationManager.showNotification(mEntry);
+
+ // While the entry is alerting, it should not be removable.
assertTrue(mAlertingNotificationManager.shouldExtendLifetime(mEntry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
new file mode 100644
index 0000000..f0344e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AmbientPulseManagerTest extends AlertingNotificationManagerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ private static final int TEST_EXTENSION_TIME = 500;
+ private AmbientPulseManager mAmbientPulseManager;
+ private boolean mLivesPastNormalTime;
+
+ protected AlertingNotificationManager createAlertingNotificationManager() {
+ return mAmbientPulseManager;
+ }
+
+ @Before
+ public void setUp() {
+ mAmbientPulseManager = new AmbientPulseManager(mContext);
+ mAmbientPulseManager.mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
+ mAmbientPulseManager.mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+ mAmbientPulseManager.mExtensionTime = TEST_EXTENSION_TIME;
+ super.setUp();
+ mAmbientPulseManager.mHandler = mTestHandler;
+ }
+
+ @Test
+ public void testExtendPulse() {
+ mAmbientPulseManager.showNotification(mEntry);
+ Runnable pastNormalTimeRunnable =
+ () -> mLivesPastNormalTime = mAmbientPulseManager.isAlerting(mEntry.key);
+ mTestHandler.postDelayed(pastNormalTimeRunnable,
+ mAmbientPulseManager.mAutoDismissNotificationDecay +
+ mAmbientPulseManager.mExtensionTime / 2);
+ mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME);
+
+ mAmbientPulseManager.extendPulse();
+
+ // Wait for normal time runnable and extended remove runnable and process them on arrival.
+ TestableLooper.get(this).processMessages(2);
+
+ assertFalse("Test timed out", mTimedOut);
+ assertTrue("Pulse was not extended", mLivesPastNormalTime);
+ assertFalse(mAmbientPulseManager.isAlerting(mEntry.key));
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index b2170fa..edf29ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -31,6 +31,7 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -63,6 +64,7 @@
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 06265e5..18dd1fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -15,10 +15,15 @@
package com.android.systemui.statusbar.notification.row;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doNothing;
import android.app.Notification;
import android.service.notification.StatusBarNotification;
@@ -27,7 +32,6 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
-import android.testing.ViewUtils;
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -36,6 +40,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -72,6 +77,7 @@
row.resetMenu();
}
+
@Test
public void testNoAppOpsInSlowSwipe() {
NotificationMenuRow row = new NotificationMenuRow(mContext);
@@ -86,4 +92,255 @@
// one for snooze and one for noti blocking
assertEquals(2, container.getChildCount());
}
+
+ @Test
+ public void testIsSnappedAndOnSameSide() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuSnapped()).thenReturn(true);
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.isMenuSnappedOnLeft()).thenReturn(true);
+
+ assertTrue("Showing on left and on left", row.isSnappedAndOnSameSide());
+
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.isMenuSnappedOnLeft()).thenReturn(false);
+ assertTrue("Snapped to right and on right", row.isSnappedAndOnSameSide());
+
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.isMenuSnapped()).thenReturn(false);
+ assertFalse("Snapped to right and on left", row.isSnappedAndOnSameSide());
+
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.isMenuSnappedOnLeft()).thenReturn(true);
+ when(row.isMenuVisible()).thenReturn(false);
+ assertFalse("Snapped to left and on left, but menu not visible",
+ row.isSnappedAndOnSameSide());
+
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuSnapped()).thenReturn(false);
+ assertFalse("Snapped to left and on left, but not actually snapped to",
+ row.isSnappedAndOnSameSide());
+ }
+
+ @Test
+ public void testGetMenuSnapTarget() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuOnLeft()).thenReturn(true);
+ doReturn(30).when(row).getSpaceForMenu();
+
+ assertEquals("When on left, snap target is space for menu",
+ 30, row.getMenuSnapTarget());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ assertEquals("When on right, snap target is negative space for menu",
+ -30, row.getMenuSnapTarget());
+ }
+
+ @Test
+ public void testIsSwipedEnoughToShowMenu() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuOnLeft()).thenReturn(true);
+ doReturn(40f).when(row).getMinimumSwipeDistance();
+
+ when(row.getTranslation()).thenReturn(30f);
+ assertFalse("on left, translation is less than min", row.isSwipedEnoughToShowMenu());
+
+ when(row.getTranslation()).thenReturn(50f);
+ assertTrue("on left, translation is greater than min", row.isSwipedEnoughToShowMenu());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(-30f);
+ assertFalse("on right, translation is greater than -min", row.isSwipedEnoughToShowMenu());
+
+ when(row.getTranslation()).thenReturn(-50f);
+ assertTrue("on right, translation is less than -min", row.isSwipedEnoughToShowMenu());
+
+ when(row.isMenuVisible()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(30f);
+ assertFalse("on left, translation greater than min, but not visible",
+ row.isSwipedEnoughToShowMenu());
+ }
+
+ @Test
+ public void testIsWithinSnapMenuThreshold() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doReturn(30f).when(row).getSnapBackThreshold();
+ doReturn(50f).when(row).getDismissThreshold();
+
+ when(row.isMenuOnLeft()).thenReturn(true);
+ when(row.getTranslation()).thenReturn(40f);
+ assertTrue("When on left, translation is between min and max",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(20f);
+ assertFalse("When on left, translation is less than min",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(60f);
+ assertFalse("When on left, translation is greater than max",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(-40f);
+ assertTrue("When on right, translation is between -min and -max",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(-20f);
+ assertFalse("When on right, translation is greater than -min",
+ row.isWithinSnapMenuThreshold());
+
+ when(row.getTranslation()).thenReturn(-60f);
+ assertFalse("When on right, translation is less than -max",
+ row.isWithinSnapMenuThreshold());
+ }
+
+ @Test
+ public void testShouldSnapBack() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doReturn(40f).when(row).getSnapBackThreshold();
+ when(row.isMenuVisible()).thenReturn(false);
+ when(row.isMenuOnLeft()).thenReturn(true);
+
+ when(row.getTranslation()).thenReturn(50f);
+ assertFalse("On left, translation greater than minimum target", row.shouldSnapBack());
+
+ when(row.getTranslation()).thenReturn(30f);
+ assertTrue("On left, translation less than minimum target", row.shouldSnapBack());
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ when(row.getTranslation()).thenReturn(-50f);
+ assertFalse("On right, translation less than minimum target", row.shouldSnapBack());
+
+ when(row.getTranslation()).thenReturn(-30f);
+ assertTrue("On right, translation greater than minimum target", row.shouldSnapBack());
+ }
+
+ @Test
+ public void testCanBeDismissed() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
+
+ when(row.getParent()).thenReturn(parent);
+ when(parent.canViewBeDismissed()).thenReturn(true);
+
+ assertTrue("Row can be dismissed if parent can be dismissed", row.canBeDismissed());
+
+ when(parent.canViewBeDismissed()).thenReturn(false);
+ assertFalse("Row cannot be dismissed if parent cannot be dismissed",
+ row.canBeDismissed());
+ }
+
+ @Test
+ public void testIsTowardsMenu() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuVisible()).thenReturn(true);
+ when(row.isMenuOnLeft()).thenReturn(true);
+
+ assertTrue("menu on left, movement is negative", row.isTowardsMenu(-30f));
+ assertFalse("menu on left, movement is positive", row.isTowardsMenu(30f));
+ assertTrue("menu on left, movement is 0", row.isTowardsMenu(0f));
+
+ when(row.isMenuOnLeft()).thenReturn(false);
+ assertTrue("menu on right, movement is positive", row.isTowardsMenu(30f));
+ assertFalse("menu on right, movement is negative", row.isTowardsMenu(-30f));
+ assertTrue("menu on right, movement is 0", row.isTowardsMenu(0f));
+
+ when(row.isMenuVisible()).thenReturn(false);
+ assertFalse("menu on left, movement is negative, but menu not visible",
+ row.isTowardsMenu(-30f));
+ }
+
+ @Test
+ public void onSnapBack() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ NotificationMenuRowPlugin.OnMenuEventListener listener = mock(NotificationMenuRowPlugin
+ .OnMenuEventListener.class);
+ row.setMenuClickListener(listener);
+ ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
+ when(row.getParent()).thenReturn(parent);
+ doNothing().when(row).cancelDrag();
+
+ row.onSnapOpen();
+
+ assertTrue("before onSnapClosed, row is snapped to", row.isMenuSnapped());
+ assertFalse("before onSnapClosed, row is not snapping", row.isSnapping());
+
+ row.onSnapClosed();
+
+ assertFalse("after onSnapClosed, row is not snapped to", row.isMenuSnapped());
+ assertTrue("after onSnapClosed, row is snapping", row.isSnapping());
+ }
+
+ @Test
+ public void testOnSnap() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ when(row.isMenuOnLeft()).thenReturn(true);
+ NotificationMenuRowPlugin.OnMenuEventListener listener = mock(NotificationMenuRowPlugin
+ .OnMenuEventListener.class);
+ row.setMenuClickListener(listener);
+ ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
+ when(row.getParent()).thenReturn(parent);
+
+ assertFalse("before onSnapOpen, row is not snapped to", row.isMenuSnapped());
+ assertFalse("before onSnapOpen, row is not snapped on left", row.isMenuSnappedOnLeft());
+
+ row.onSnapOpen();
+
+ assertTrue("after onSnapOpen, row is snapped to", row.isMenuSnapped());
+ assertTrue("after onSnapOpen, row is snapped on left", row.isMenuSnapped());
+ verify(listener, times(1)).onMenuShown(parent);
+ }
+
+ @Test
+ public void testOnDismiss() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doNothing().when(row).cancelDrag();
+ row.onSnapOpen();
+
+ assertFalse("before onDismiss, row is not dismissing", row.isDismissing());
+ assertTrue("before onDismiss, row is showing", row.isMenuSnapped());
+
+ row.onDismiss();
+
+ verify(row, times(1)).cancelDrag();
+ assertTrue("after onDismiss, row is dismissing", row.isDismissing());
+ assertFalse("after onDismiss, row is not showing", row.isMenuSnapped());
+ }
+
+ @Test
+ public void testOnDown() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ doNothing().when(row).beginDrag();
+
+ row.onTouchStart();
+
+ verify(row, times(1)).beginDrag();
+ }
+
+ @Test
+ public void testOnUp() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ row.onTouchStart();
+
+ assertTrue("before onTouchEnd, isUserTouching is true", row.isUserTouching());
+
+ row.onTouchEnd();
+
+ assertFalse("after onTouchEnd, isUserTouching is false", row.isUserTouching());
+ }
+
+ @Test
+ public void testIsMenuVisible() {
+ NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
+ row.setMenuAlpha(0);
+
+ assertFalse("when alpha is 0, menu is not visible", row.isMenuVisible());
+
+ row.setMenuAlpha(0.5f);
+ assertTrue("when alpha is .5, menu is visible", row.isMenuVisible());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 087aa59..2728453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.stack;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
@@ -51,7 +50,7 @@
@Test
public void testGetMaxAllowedVisibleChildren_ambient() {
- mGroup.setShowAmbient(true);
+ mGroup.setOnAmbient(true);
Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index ce0bd58..1e3d42b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -19,6 +19,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
@@ -316,7 +317,7 @@
private void setBarStateForTest(int state) {
ArgumentCaptor<StatusBarStateController.StateListener> captor =
ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
- verify(mBarState).addListener(captor.capture());
+ verify(mBarState, atLeastOnce()).addListener(captor.capture());
captor.getValue().onStateChanged(state);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index a81d17f..1070795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -82,28 +82,26 @@
// Remove should succeed because the notification is swiped out
mHeadsUpManager.removeNotification(mEntry.key, false /* releaseImmediately */);
- assertFalse(mHeadsUpManager.contains(mEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(mEntry.key));
}
@Test
- public void testShouldExtendLifetime_swipedOut() {
+ public void testCanRemoveImmediately_swipedOut() {
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.addSwipedOutNotification(mEntry.key);
- // Notification is swiped so its lifetime should not be extended even if it hasn't been
- // shown long enough
- assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ // Notification is swiped so it can be immediately removed.
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
}
@Test
- public void testShouldExtendLifetime_notTopEntry() {
+ public void testCanRemoveImmediately_notTopEntry() {
NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
laterEntry.row = mRow;
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
- // Notification is "behind" a higher priority notification so we have no reason to keep
- // its lifetime extended
- assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ // Notification is "behind" a higher priority notification so we can remove it immediately.
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
index 2423e14..667a508 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
@@ -171,23 +171,6 @@
ev.recycle();
}
- @Test
- public void testFurtherSelectedWhenCloserNotFocusable() {
- View closer = mockViewAt(0, 0, 10, 10);
- View further = mockViewAt(20, 0, 10, 10);
- closer.setFocusable(false);
-
- mNearestTouchFrame.addView(closer);
- mNearestTouchFrame.addView(further);
- mNearestTouchFrame.onMeasure(0, 0);
-
- MotionEvent ev = MotionEvent.obtain(0, 0, 0,
- 12 /* x */, 5 /* y */, 0);
- mNearestTouchFrame.onTouchEvent(ev);
- verify(further).onTouchEvent(eq(ev));
- ev.recycle();
- }
-
private View mockViewAt(int x, int y, int width, int height) {
View v = spy(new View(mContext));
doAnswer(invocation -> {
@@ -204,7 +187,6 @@
v.setRight(width);
v.setTop(0);
v.setBottom(height);
- v.setFocusable(true);
return v;
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
new file mode 100644
index 0000000..464f74b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationGroupManagerTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ private static final String TEST_CHANNEL_ID = "test_channel";
+ private static final String TEST_GROUP_ID = "test_group";
+ private static final String TEST_PACKAGE_NAME = "test_pkg";
+ private NotificationGroupManager mGroupManager;
+ private int mId = 0;
+
+ @Mock HeadsUpManager mHeadsUpManager;
+ @Mock AmbientPulseManager mAmbientPulseManager;
+
+ @Before
+ public void setup() {
+ mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+
+ initializeGroupManager();
+ }
+
+ private void initializeGroupManager() {
+ mGroupManager = new NotificationGroupManager();
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
+ }
+
+ @Test
+ public void testIsOnlyChildInGroup() {
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ assertTrue(mGroupManager.isOnlyChildInGroup(childEntry.notification));
+ }
+
+ @Test
+ public void testIsChildInGroupWithSummary() {
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+
+ assertTrue(mGroupManager.isChildInGroupWithSummary(childEntry.notification));
+ }
+
+ @Test
+ public void testIsSummaryOfGroupWithChildren() {
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+
+ assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
+ assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+ }
+
+ @Test
+ public void testRemoveChildFromGroupWithSummary() {
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+
+ mGroupManager.onEntryRemoved(childEntry);
+
+ assertFalse(mGroupManager.isChildInGroupWithSummary(childEntry.notification));
+ }
+
+ @Test
+ public void testRemoveSummaryFromGroupWithSummary() {
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+
+ mGroupManager.onEntryRemoved(summaryEntry);
+
+ assertNull(mGroupManager.getGroupSummary(childEntry.notification));
+ assertFalse(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
+ }
+
+ @Test
+ public void testHeadsUpEntryIsIsolated() {
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+ when(mHeadsUpManager.isAlerting(childEntry.key)).thenReturn(true);
+
+ mGroupManager.onHeadsUpStateChanged(childEntry, true);
+
+ // Child entries that are heads upped should be considered separate groups visually even if
+ // they are the same group logically
+ assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry.row,
+ mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ }
+
+ @Test
+ public void testAmbientPulseEntryIsIsolated() {
+ mGroupManager.setDozing(true);
+ NotificationData.Entry childEntry = createChildNotification();
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ mGroupManager.onEntryAdded(createChildNotification());
+ when(mAmbientPulseManager.isAlerting(childEntry.key)).thenReturn(true);
+
+ mGroupManager.onAmbientStateChanged(childEntry, true);
+
+ // Child entries that are heads upped should be considered separate groups visually even if
+ // they are the same group logically
+ assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+ assertEquals(summaryEntry.row,
+ mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpTransfersToChild() {
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ when(mHeadsUpManager.isAlerting(summaryEntry.key)).thenReturn(true);
+ NotificationData.Entry childEntry = createChildNotification();
+
+ // Summary will be suppressed because there is only one child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // A suppressed summary should transfer its heads up state to the child.
+ verify(mHeadsUpManager, never()).showNotification(summaryEntry);
+ verify(mHeadsUpManager).showNotification(childEntry);
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
+ mHeadsUpManager = new HeadsUpManager(mContext) {};
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ NotificationData.Entry summaryEntry =
+ createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry2 =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mHeadsUpManager.showNotification(summaryEntry);
+ // Trigger a transfer of heads up state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Add second child notification so that summary is no longer suppressed.
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // The heads up state should transfer back to the summary as there is now more than one
+ // child and the summary should no longer be suppressed.
+ assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ }
+
+ @Test
+ public void testSuppressedSummaryAmbientPulseTransfersToChild() {
+ mGroupManager.setDozing(true);
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ when(mAmbientPulseManager.isAlerting(summaryEntry.key)).thenReturn(true);
+ NotificationData.Entry childEntry = createChildNotification();
+
+ // Summary will be suppressed because there is only one child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // A suppressed summary should transfer its ambient state to the child.
+ verify(mAmbientPulseManager, never()).showNotification(summaryEntry);
+ verify(mAmbientPulseManager).showNotification(childEntry);
+ }
+
+ @Test
+ public void testSuppressedSummaryAmbientPulseTransfersToChildButBackAgain() {
+ mGroupManager.setDozing(true);
+ mAmbientPulseManager = new AmbientPulseManager(mContext);
+ mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+ initializeGroupManager();
+ NotificationData.Entry summaryEntry =
+ createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry2 =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mAmbientPulseManager.showNotification(summaryEntry);
+ // Trigger a transfer of ambient state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Add second child notification so that summary is no longer suppressed.
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // The ambient state should transfer back to the summary as there is now more than one
+ // child and the summary should no longer be suppressed.
+ assertTrue(mAmbientPulseManager.isAlerting(summaryEntry.key));
+ assertFalse(mAmbientPulseManager.isAlerting(childEntry.key));
+ }
+
+ private NotificationData.Entry createSummaryNotification() {
+ return createSummaryNotification(Notification.GROUP_ALERT_ALL);
+ }
+
+ private NotificationData.Entry createSummaryNotification(int groupAlertBehavior) {
+ return createEntry(true, groupAlertBehavior);
+ }
+
+ private NotificationData.Entry createChildNotification() {
+ return createChildNotification(Notification.GROUP_ALERT_ALL);
+ }
+
+ private NotificationData.Entry createChildNotification(int groupAlertBehavior) {
+ return createEntry(false, groupAlertBehavior);
+ }
+
+ private NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) {
+ Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentTitle("Title")
+ .setSmallIcon(R.drawable.ic_person)
+ .setGroupAlertBehavior(groupAlertBehavior)
+ .setGroupSummary(isSummary)
+ .setGroup(TEST_GROUP_ID)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ TEST_PACKAGE_NAME /* pkg */,
+ TEST_PACKAGE_NAME,
+ mId++,
+ null /* tag */,
+ 0, /* uid */
+ 0 /* initialPid */,
+ notif,
+ new UserHandle(ActivityManager.getCurrentUser()),
+ null /* overrideGroupKey */,
+ 0 /* postTime */);
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ entry.row = row;
+ when(row.getEntry()).thenReturn(entry);
+ when(row.getStatusBarNotification()).thenReturn(sbn);
+ return entry;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cbba251..5006b0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -358,7 +358,7 @@
}
@Test
- public void testShouldPeek_nonSuppressedGroupSummary() {
+ public void testShouldHeadsUp_nonSuppressedGroupSummary() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
@@ -375,11 +375,11 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mEntryManager.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_suppressedGroupSummary() {
+ public void testShouldHeadsUp_suppressedGroupSummary() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
@@ -396,11 +396,11 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mEntryManager.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_suppressedPeek() {
+ public void testShouldHeadsUp_suppressedHeadsUp() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
@@ -414,11 +414,11 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertFalse(mEntryManager.shouldPeek(entry, sbn));
+ assertFalse(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testShouldPeek_noSuppressedPeek() {
+ public void testShouldHeadsUp_noSuppressedHeadsUp() {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
@@ -432,31 +432,31 @@
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
- assertTrue(mEntryManager.shouldPeek(entry, sbn));
+ assertTrue(mEntryManager.shouldHeadsUp(entry));
}
@Test
- public void testPeek_disabledStatusBar() {
+ public void testHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
- assertFalse("The panel shouldn't allow peek while disabled",
- mStatusBar.shouldPeek(entry, sbn));
+ assertFalse("The panel shouldn't allow heads up while disabled",
+ mStatusBar.canHeadsUp(entry, sbn));
}
@Test
- public void testPeek_disabledNotificationShade() {
+ public void testHeadsUp_disabledNotificationShade() {
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
NotificationData.Entry entry = new NotificationData.Entry(sbn);
mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
- assertFalse("The panel shouldn't allow peek while notitifcation shade disabled",
- mStatusBar.shouldPeek(entry, sbn));
+ assertFalse("The panel shouldn't allow heads up while notification shade disabled",
+ mStatusBar.canHeadsUp(entry, sbn));
}
@Test
@@ -472,7 +472,7 @@
}
@Test
- public void testPanelOpenForPeek() {
+ public void testPanelOpenForHeadsUp() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
when(mNotificationList.size()).thenReturn(5);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
index f8223f6..f7a7e04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -83,4 +84,16 @@
& WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
assertThat(flag).isEqualTo(0);
}
+
+ @Test
+ public void testOnThemeChanged_doesntCrash() {
+ mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
+ mActivityManager, mDozeParameters);
+ mStatusBarWindowController.onThemeChanged();
+ }
+
+ @Test
+ public void testAdd_updatesVisibilityFlags() {
+ verify(mStatusBarView).setSystemUiVisibility(anyInt());
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 371f932..cf9f233 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -170,6 +170,20 @@
* action.
*/
public TouchExplorer(Context context, AccessibilityManagerService service) {
+ this(context, service, null);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context A context handle for accessing resources.
+ * @param service The service to notify touch interaction and gesture completed and to perform
+ * action.
+ * @param detector The gesture detector to handle accessibility touch event. If null the default
+ * one created in place, or for testing purpose.
+ */
+ public TouchExplorer(Context context, AccessibilityManagerService service,
+ AccessibilityGestureDetector detector) {
mContext = context;
mAms = service;
mReceivedPointerTracker = new ReceivedPointerTracker();
@@ -186,7 +200,11 @@
mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
mDetermineUserIntentTimeout);
- mGestureDetector = new AccessibilityGestureDetector(context, this);
+ if (detector == null) {
+ mGestureDetector = new AccessibilityGestureDetector(context, this);
+ } else {
+ mGestureDetector = detector;
+ }
final float density = context.getResources().getDisplayMetrics().density;
mScaledMinPointerDistanceToUseMiddleLocation =
(int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b71d7a7..da52d40 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1834,7 +1834,7 @@
if (provider.widgets.isEmpty()) {
// cancel the future updates
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
// send the broacast saying that the provider is not in use any more
sendDisabledIntentLocked(provider);
@@ -1843,18 +1843,16 @@
}
}
- private void cancelBroadcasts(Provider provider) {
+ private void cancelBroadcastsLocked(Provider provider) {
if (DEBUG) {
- Slog.i(TAG, "cancelBroadcasts() for " + provider);
+ Slog.i(TAG, "cancelBroadcastsLocked() for " + provider);
}
if (provider.broadcast != null) {
- mAlarmManager.cancel(provider.broadcast);
- long token = Binder.clearCallingIdentity();
- try {
- provider.broadcast.cancel();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ final PendingIntent broadcast = provider.broadcast;
+ mSaveStateHandler.post(() -> {
+ mAlarmManager.cancel(broadcast);
+ broadcast.cancel();
+ });
provider.broadcast = null;
}
}
@@ -2315,7 +2313,7 @@
mProviders.remove(provider);
// no need to send the DISABLE broadcast, since the receiver is gone anyway
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
}
private void sendEnableIntentLocked(Provider p) {
@@ -2369,17 +2367,14 @@
Binder.restoreCallingIdentity(token);
}
if (!alreadyRegistered) {
- long period = provider.info.updatePeriodMillis;
- if (period < MIN_UPDATE_PERIOD) {
- period = MIN_UPDATE_PERIOD;
- }
- final long oldId = Binder.clearCallingIdentity();
- try {
+ // Set the alarm outside of our locks; we've latched the first-time
+ // invariant and established the PendingIntent safely.
+ final long period = Math.max(provider.info.updatePeriodMillis, MIN_UPDATE_PERIOD);
+ final PendingIntent broadcast = provider.broadcast;
+ mSaveStateHandler.post(() ->
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, provider.broadcast);
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ SystemClock.elapsedRealtime() + period, period, broadcast)
+ );
}
}
}
@@ -3382,7 +3377,7 @@
// Reschedule for the new updatePeriodMillis (don't worry about handling
// it specially if updatePeriodMillis didn't change because we just sent
// an update, and the next one will be updatePeriodMillis from now).
- cancelBroadcasts(provider);
+ cancelBroadcastsLocked(provider);
registerForBroadcastsLocked(provider, appWidgetIds);
// If it's currently showing, call back with the new
// AppWidgetProviderInfo.
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index ad80cc26..2671327 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -587,6 +587,7 @@
}
final RemoteFillService remoteService = getService();
if (remoteService != null) {
+ if (sVerbose) Slog.v(LOG_TAG, "calling onFillRequest() for id=" + mRequest.getId());
try {
remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
} catch (RemoteException e) {
@@ -659,6 +660,7 @@
public void run() {
final RemoteFillService remoteService = getService();
if (remoteService != null) {
+ if (sVerbose) Slog.v(LOG_TAG, "calling onSaveRequest()");
try {
remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
} catch (RemoteException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 9af952d..c933833 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -105,7 +105,7 @@
try {
IBackupTransport transport =
transportClient.connectOrThrow(
- "AppBackupUtils.appIsEligibleForBackupAtRuntime");
+ "AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport");
return transport.isAppEligibleForBackup(
packageInfo, AppBackupUtils.appGetsFullBackup(packageInfo));
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 15673a7..872261a 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -118,10 +118,22 @@
this.mBinderCallsStats = binderCallsStats;
}
+ /** @see BinderCallsStats#reset */
+ public void reset() {
+ mBinderCallsStats.reset();
+ }
+
+ /**
+ * @see BinderCallsStats#getExportedCallStats.
+ *
+ * Note that binder calls stats will be reset by statsd every time
+ * the data is exported.
+ */
public ArrayList<BinderCallsStats.ExportedCallStat> getExportedCallStats() {
return mBinderCallsStats.getExportedCallStats();
}
+ /** @see BinderCallsStats#getExportedExceptionStats */
public ArrayMap<String, Integer> getExportedExceptionStats() {
return mBinderCallsStats.getExportedExceptionStats();
}
@@ -190,6 +202,15 @@
reset();
pw.println("binder_calls_stats reset.");
return;
+ } else if ("--enable".equals(arg)) {
+ Binder.setObserver(mBinderCallsStats);
+ return;
+ } else if ("--disable".equals(arg)) {
+ Binder.setObserver(null);
+ return;
+ } else if ("--no-sampling".equals(arg)) {
+ mBinderCallsStats.setSamplingInterval(1);
+ return;
} else if ("--enable-detailed-tracking".equals(arg)) {
SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
mBinderCallsStats.setDetailedTracking(true);
@@ -203,6 +224,9 @@
} else if ("-h".equals(arg)) {
pw.println("binder_calls_stats commands:");
pw.println(" --reset: Reset stats");
+ pw.println(" --enable: Enable tracking binder calls");
+ pw.println(" --disable: Disables tracking binder calls");
+ pw.println(" --no-sampling: Tracks all calls");
pw.println(" --enable-detailed-tracking: Enables detailed tracking");
pw.println(" --disable-detailed-tracking: Disables detailed tracking");
return;
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 380f6a7..a69d416 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1490,23 +1490,19 @@
}
}
- private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+ private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
private void enforceTunnelPermissions(String callingPackage) {
checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
- if (false) { // STOPSHIP if this line is present
- switch (getAppOpsManager().noteOp(
- TUNNEL_OP,
- Binder.getCallingUid(), callingPackage)) {
- case AppOpsManager.MODE_DEFAULT:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
- break;
- case AppOpsManager.MODE_ALLOWED:
- return;
- default:
- throw new SecurityException("Request to ignore AppOps for non-legacy API");
- }
+ switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+ case AppOpsManager.MODE_DEFAULT:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
+ break;
+ case AppOpsManager.MODE_ALLOWED:
+ return;
+ default:
+ throw new SecurityException("Request to ignore AppOps for non-legacy API");
}
}
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index ab7bf28..4f0e170 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -93,10 +93,12 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
List<LooperStats.ExportedEntry> entries = mStats.getEntries();
entries.sort(Comparator
- .comparing((LooperStats.ExportedEntry entry) -> entry.threadName)
+ .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
+ .thenComparing(entry -> entry.threadName)
.thenComparing(entry -> entry.handlerClassName)
.thenComparing(entry -> entry.messageName));
String header = String.join(",", Arrays.asList(
+ "work_source_uid",
"thread_name",
"handler_class",
"message_name",
@@ -110,11 +112,11 @@
"exception_count"));
pw.println(header);
for (LooperStats.ExportedEntry entry : entries) {
- pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName,
- entry.handlerClassName, entry.messageName, entry.isInteractive,
- entry.messageCount, entry.recordedMessageCount, entry.totalLatencyMicros,
- entry.maxLatencyMicros, entry.cpuUsageMicros, entry.maxCpuUsageMicros,
- entry.exceptionCount);
+ pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.workSourceUid,
+ entry.threadName, entry.handlerClassName, entry.messageName,
+ entry.isInteractive, entry.messageCount, entry.recordedMessageCount,
+ entry.totalLatencyMicros, entry.maxLatencyMicros, entry.cpuUsageMicros,
+ entry.maxCpuUsageMicros, entry.exceptionCount);
}
}
@@ -189,6 +191,10 @@
} else if ("reset".equals(cmd)) {
mStats.reset();
return 0;
+ } else if ("sampling_interval".equals(cmd)) {
+ int sampling = Integer.parseUnsignedInt(getNextArgRequired());
+ setSamplingInterval(sampling);
+ return 0;
} else {
return handleDefaultCommands(cmd);
}
@@ -198,9 +204,10 @@
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
pw.println(LOOPER_STATS_SERVICE_NAME + " commands:");
- pw.println(" enable: Enable collecting stats");
- pw.println(" disable: Disable collecting stats");
- pw.println(" reset: Reset stats");
+ pw.println(" enable: Enable collecting stats.");
+ pw.println(" disable: Disable collecting stats.");
+ pw.println(" sampling_interval: Change the sampling interval.");
+ pw.println(" reset: Reset stats.");
}
}
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index cb03255..f436286 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -42,31 +42,25 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.Sandman;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
import android.util.Slog;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-
import com.android.internal.R;
import com.android.internal.app.DisableCarModeActivity;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
-import com.android.server.power.ShutdownThread;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
final class UiModeManagerService extends SystemService {
private static final String TAG = UiModeManager.class.getSimpleName();
private static final boolean LOG = false;
@@ -466,7 +460,7 @@
uiMode |= mNightMode << 4;
}
- if (mPowerSave && !mNightModeLocked) {
+ if (mPowerSave) {
uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
uiMode |= Configuration.UI_MODE_NIGHT_YES;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e5aa3f4..510d333 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2490,7 +2490,7 @@
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
- mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
+ mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
r.postNotification();
created = true;
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 1866420..01421c7 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -126,11 +126,6 @@
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
- @VisibleForTesting
- ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
- this(supervisor, supervisor.mDisplayManager.getDisplay(displayId));
- }
-
ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
mSupervisor = supervisor;
mDisplayId = display.getDisplayId();
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 2c8f2fc..3a0289c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -68,6 +68,7 @@
static final String KEY_BOUND_SERVICE_CRASH_MAX_RETRY = "service_crash_max_retry";
static final String KEY_PROCESS_START_ASYNC = "process_start_async";
static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
+ static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -97,6 +98,7 @@
private static final int DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY = 16;
private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
+ private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -212,6 +214,10 @@
// throttle requests from apps.
public long MEMORY_INFO_THROTTLE_TIME = DEFAULT_MEMORY_INFO_THROTTLE_TIME;
+ // Allow app just moving from TOP to FOREGROUND_SERVICE to stay in a higher adj value for
+ // this long.
+ public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION;
+
// Indicates whether the activity starts logging is enabled.
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
boolean mFlagActivityStartsLoggingEnabled;
@@ -355,6 +361,8 @@
DEFAULT_PROCESS_START_ASYNC);
MEMORY_INFO_THROTTLE_TIME = mParser.getLong(KEY_MEMORY_INFO_THROTTLE_TIME,
DEFAULT_MEMORY_INFO_THROTTLE_TIME);
+ TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
+ DEFAULT_TOP_TO_FGS_GRACE_DURATION);
updateMaxCachedProcesses();
}
@@ -438,6 +446,8 @@
pw.println(FLAG_PROCESS_START_ASYNC);
pw.print(" "); pw.print(KEY_MEMORY_INFO_THROTTLE_TIME); pw.print("=");
pw.println(MEMORY_INFO_THROTTLE_TIME);
+ pw.print(" "); pw.print(KEY_TOP_TO_FGS_GRACE_DURATION); pw.print("=");
+ pw.println(TOP_TO_FGS_GRACE_DURATION);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b64e8b8..f7fe9e2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -665,11 +665,6 @@
int mNextIsolatedProcessUid = 0;
/**
- * The currently running heavy-weight process, if any.
- */
- ProcessRecord mHeavyWeightProcess = null;
-
- /**
* Non-persistent appId whitelist for background restrictions
*/
int[] mBackgroundAppIdWhitelist = new int[] {
@@ -826,12 +821,6 @@
final SparseArray<UidRecord> mValidateUids = new SparseArray<>();
/**
- * Packages that the user has asked to have run in screen size
- * compatibility mode instead of filling the screen.
- */
- final CompatModePackages mCompatModePackages;
-
- /**
* Set of IntentSenderRecord objects that are currently active.
*/
final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
@@ -1097,11 +1086,10 @@
volatile boolean mSystemReady = false;
volatile boolean mOnBattery = false;
volatile int mFactoryTest;
+ volatile boolean mBooting = false;
- @GuardedBy("this") boolean mBooting = false;
@GuardedBy("this") boolean mCallFinishBooting = false;
@GuardedBy("this") boolean mBootAnimationComplete = false;
- @GuardedBy("this") boolean mLaunchWarningShown = false;
private @GuardedBy("this") boolean mCheckedForSetup = false;
final Context mContext;
@@ -1388,10 +1376,8 @@
long mLastWriteTime = 0;
- /**
- * Set to true after the system has finished booting.
- */
- boolean mBooted = false;
+ /** Set to true after the system has finished booting. */
+ volatile boolean mBooted = false;
/**
* Current boot phase.
@@ -1441,8 +1427,6 @@
static final int PROC_START_TIMEOUT_MSG = 20;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
- static final int POST_HEAVY_NOTIFICATION_MSG = 24;
- static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26;
static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27;
static final int CLEAR_DNS_CACHE_MSG = 28;
@@ -1451,7 +1435,6 @@
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
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;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
@@ -1758,65 +1741,6 @@
case FINALIZE_PENDING_INTENT_MSG: {
((PendingIntentRecord)msg.obj).completeFinalize();
} break;
- case POST_HEAVY_NOTIFICATION_MSG: {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
- return;
- }
-
- ActivityRecord root = (ActivityRecord)msg.obj;
- final WindowProcessController process = root.app;
- if (process == null) {
- return;
- }
-
- try {
- Context context = mContext.createPackageContext(process.mInfo.packageName, 0);
- String text = mContext.getString(R.string.heavy_weight_notification,
- context.getApplicationInfo().loadLabel(context.getPackageManager()));
- Notification notification =
- new Notification.Builder(context,
- SystemNotificationChannels.HEAVY_WEIGHT_APP)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setWhen(0)
- .setOngoing(true)
- .setTicker(text)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(text)
- .setContentText(
- mContext.getText(R.string.heavy_weight_notification_detail))
- .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
- root.intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
- new UserHandle(root.userId)))
- .build();
- try {
- inm.enqueueNotificationWithTag("android", "android", null,
- SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION,
- notification, root.userId);
- } catch (RuntimeException e) {
- Slog.w(ActivityManagerService.TAG,
- "Error showing notification for heavy-weight app", e);
- } catch (RemoteException e) {
- }
- } catch (NameNotFoundException e) {
- Slog.w(TAG, "Unable to create context for heavy notification", e);
- }
- } break;
- case CANCEL_HEAVY_NOTIFICATION_MSG: {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
- return;
- }
- try {
- inm.cancelNotificationWithTag("android", null,
- SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
- } catch (RuntimeException e) {
- Slog.w(ActivityManagerService.TAG,
- "Error canceling notification for service", e);
- } catch (RemoteException e) {
- }
- } break;
case CHECK_EXCESSIVE_POWER_USE_MSG: {
synchronized (ActivityManagerService.this) {
checkExcessivePowerUsageLocked();
@@ -1853,17 +1777,6 @@
}
break;
}
- case FINISH_BOOTING_MSG: {
- if (msg.arg1 != 0) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
- finishBooting();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
- if (msg.arg2 != 0) {
- mAtmInternal.enableScreenAfterBoot(mBooted);
- }
- break;
- }
case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: {
try {
Locale l = (Locale) msg.obj;
@@ -2430,7 +2343,6 @@
mAppErrors = null;
mAppOpsService = mInjector.getAppOpsService(null, null);
mBatteryStatsService = null;
- mCompatModePackages = null;
mConstants = null;
mHandler = null;
mHandlerThread = null;
@@ -2519,7 +2431,6 @@
}
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
- mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mActivityTaskManager = atm;
@@ -3791,31 +3702,31 @@
return true;
}
- void updateUsageStats(ActivityRecord component, boolean resumed) {
+ void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
- "updateUsageStats: comp=" + component + "res=" + resumed);
+ "updateUsageStats: comp=" + activity + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
- component.app.mUid, component.realActivity.getPackageName(),
- component.realActivity.getShortClassName(), resumed ?
+ uid, activity.getPackageName(),
+ activity.getShortClassName(), resumed ?
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
if (resumed) {
if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(component.realActivity, component.userId,
+ mUsageStatsService.reportEvent(activity, userId,
UsageEvents.Event.MOVE_TO_FOREGROUND);
}
synchronized (stats) {
- stats.noteActivityResumedLocked(component.app.mUid);
+ stats.noteActivityResumedLocked(uid);
}
} else {
if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(component.realActivity, component.userId,
+ mUsageStatsService.reportEvent(activity, userId,
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
- stats.noteActivityPausedLocked(component.app.mUid);
+ stats.noteActivityPausedLocked(uid);
}
}
}
@@ -3895,8 +3806,8 @@
mCheckedForSetup = checked;
}
- CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
- return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
+ CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
+ return mAtmInternal.compatibilityInfoForPackage(ai);
}
private void enforceNotIsolatedCaller(String caller) {
@@ -3906,37 +3817,8 @@
}
@Override
- public int getPackageScreenCompatMode(String packageName) {
- enforceNotIsolatedCaller("getPackageScreenCompatMode");
- synchronized (this) {
- return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
- }
- }
-
- @Override
public void setPackageScreenCompatMode(String packageName, int mode) {
- enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
- "setPackageScreenCompatMode");
- synchronized (this) {
- mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
- }
- }
-
- @Override
- public boolean getPackageAskScreenCompat(String packageName) {
- enforceNotIsolatedCaller("getPackageAskScreenCompat");
- synchronized (this) {
- return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
- }
- }
-
- @Override
- public void setPackageAskScreenCompat(String packageName, boolean ask) {
- enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
- "setPackageAskScreenCompat");
- synchronized (this) {
- mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
- }
+ mActivityTaskManager.setPackageScreenCompatMode(packageName, mode);
}
private boolean hasUsageStatsPermission(String callingPackage) {
@@ -4307,19 +4189,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
- synchronized(this) {
- final ProcessRecord proc = mHeavyWeightProcess;
- if (proc == null) {
- return;
- }
-
- proc.getWindowProcessController().finishActivities();
-
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- proc.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.finishHeavyWeightApp();
}
@Override
@@ -5789,11 +5659,8 @@
return false;
}
removeProcessNameLocked(name, uid);
- if (mHeavyWeightProcess == app) {
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- mHeavyWeightProcess.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
+
boolean needRestart = false;
if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
int pid = app.pid;
@@ -5851,11 +5718,7 @@
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
removeProcessNameLocked(app.processName, app.uid);
- if (mHeavyWeightProcess == app) {
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- mHeavyWeightProcess.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
// Take care of any launching providers waiting for this process.
cleanupAppInLaunchingProvidersLocked(app, true);
@@ -6034,7 +5897,7 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
+ processName + " with config " + getGlobalConfiguration());
ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
- app.compat = compatibilityInfoForPackageLocked(appInfo);
+ app.compat = compatibilityInfoForPackage(appInfo);
ProfilerInfo profilerInfo = null;
String preBindAgent = null;
@@ -6231,7 +6094,7 @@
PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
- compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
+ compatibilityInfoForPackage(mBackupTarget.appInfo),
mBackupTarget.backupMode);
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
@@ -6264,11 +6127,6 @@
}
}
- void postFinishBooting(boolean finishBooting, boolean enableScreen) {
- mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
- finishBooting ? 1 : 0, enableScreen ? 1 : 0));
- }
-
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
if (Binder.getCallingUid() != myUid()) {
@@ -6278,6 +6136,8 @@
}
final void finishBooting() {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
+
synchronized (this) {
if (!mBootAnimationComplete) {
mCallFinishBooting = true;
@@ -6383,6 +6243,8 @@
});
mUserController.scheduleStartProfiles();
}
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@Override
@@ -6393,9 +6255,7 @@
mBootAnimationComplete = true;
}
if (callFinishBooting) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -6410,9 +6270,7 @@
}
if (booting) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
if (enableScreen) {
@@ -8406,10 +8264,17 @@
}
}
+ /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
+ @Deprecated
+ @Override
public void removeContentProviderExternal(String name, IBinder token) {
+ removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call removeContentProviderExternal()");
- int userId = UserHandle.getCallingUserId();
long ident = Binder.clearCallingIdentity();
try {
removeContentProviderExternalUnchecked(name, token, userId);
@@ -10845,7 +10710,7 @@
int clientTargetSdk) {
outInfo.pid = app.pid;
outInfo.uid = app.info.uid;
- if (mHeavyWeightProcess == app) {
+ if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
}
if (app.isPersistent()) {
@@ -11888,13 +11753,13 @@
TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb);
pw.println(sb);
}
- if (mHeavyWeightProcess != null && (dumpPackage == null
- || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
+ if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
- pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ pw.println(" mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess);
}
if (dumpAll && mPendingStarts.size() > 0) {
if (needSep) pw.println();
@@ -11913,10 +11778,10 @@
pw.println(" mConfigWillChange: "
+ mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
}
- if (mCompatModePackages.getPackages().size() > 0) {
+ if (mActivityTaskManager.mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
for (Map.Entry<String, Integer> entry
- : mCompatModePackages.getPackages().entrySet()) {
+ : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage != null && !dumpPackage.equals(pkg)) {
@@ -12298,12 +12163,13 @@
proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime);
}
- if (mHeavyWeightProcess != null && (dumpPackage == null
- || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
- mHeavyWeightProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
+ if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+ ((ProcessRecord) mActivityTaskManager.mHeavyWeightProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
}
- for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) {
+ for (Map.Entry<String, Integer> entry
+ : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage == null || dumpPackage.equals(pkg)) {
@@ -12556,8 +12422,8 @@
pw.println();
pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess);
pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
- if (mHeavyWeightProcess != null) {
- pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ if (mActivityTaskManager.mHeavyWeightProcess != null) {
+ pw.println(" mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess);
}
return true;
@@ -15189,11 +15055,7 @@
if (!replacingPid) {
removeProcessNameLocked(app.processName, app.uid, app);
}
- if (mHeavyWeightProcess == app) {
- mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- mHeavyWeightProcess.userId, 0));
- mHeavyWeightProcess = null;
- }
+ mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
@@ -15577,7 +15439,7 @@
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc);
try {
proc.thread.scheduleCreateBackupAgent(app,
- compatibilityInfoForPackageLocked(app), backupMode);
+ compatibilityInfoForPackage(app), backupMode);
} catch (RemoteException e) {
// Will time out on the backup manager side
}
@@ -15676,7 +15538,7 @@
if (proc.thread != null) {
try {
proc.thread.scheduleDestroyBackupAgent(appInfo,
- compatibilityInfoForPackageLocked(appInfo));
+ compatibilityInfoForPackage(appInfo));
} catch (Exception e) {
Slog.e(TAG, "Exception when unbinding backup agent:");
e.printStackTrace();
@@ -16385,7 +16247,6 @@
mServices.forceStopPackageLocked(ssp, userId);
mAtmInternal.onPackageUninstalled(ssp);
- mCompatModePackages.handlePackageUninstalledLocked(ssp);
mBatteryStatsService.notePackageUninstalled(ssp);
}
} else {
@@ -16447,7 +16308,7 @@
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
final boolean replacing =
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- mCompatModePackages.handlePackageAddedLocked(ssp, replacing);
+ mAtmInternal.onPackageAdded(ssp, replacing);
try {
ApplicationInfo ai = AppGlobals.getPackageManager().
@@ -16464,7 +16325,6 @@
Uri data = intent.getData();
String ssp;
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
- mCompatModePackages.handlePackageDataClearedLocked(ssp);
mAtmInternal.onPackageDataCleared(ssp);
}
break;
@@ -17778,6 +17638,19 @@
}
}
+ // If the app was recently in the foreground and moved to a foreground service status,
+ // allow it to get a higher rank in memory for some time, compared to other foreground
+ // services so that it can finish performing any persistence/processing of in-memory state.
+ if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
+ && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
+ || app.setProcState <= ActivityManager.PROCESS_STATE_TOP)) {
+ adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
+ app.adjType = "fg-service-act";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
+ }
+ }
+
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
|| procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
if (app.forcingToImportant != null) {
@@ -17796,7 +17669,7 @@
}
}
- if (app == mHeavyWeightProcess) {
+ if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
@@ -18045,6 +17918,10 @@
cr.trackProcState(procState, mAdjSeq, now);
trackedProcState = true;
}
+ } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0
+ && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+ && adj > ProcessList.PERCEPTIBLE_APP_ADJ + 1) {
+ newAdj = ProcessList.PERCEPTIBLE_APP_ADJ + 1;
} else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
&& adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -19044,6 +18921,8 @@
// Must be called before updating setProcState
maybeUpdateUsageStatsLocked(app, nowElapsed);
+ maybeUpdateLastTopTime(app, now);
+
app.setProcState = app.curProcState;
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
app.notCachedSinceIdle = false;
@@ -19268,6 +19147,13 @@
}
}
+ private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
+ if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
+ && app.curProcState > ActivityManager.PROCESS_STATE_TOP) {
+ app.lastTopTime = nowUptime;
+ }
+ }
+
private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
if (proc.thread != null && proc.baseProcessTracker != null) {
proc.baseProcessTracker.setState(
@@ -21064,18 +20950,6 @@
ActivityManagerService.this.trimApplications();
}
- public int getPackageScreenCompatMode(ApplicationInfo ai) {
- synchronized (ActivityManagerService.this) {
- return mCompatModePackages.computeCompatModeLocked(ai);
- }
- }
-
- public void setPackageScreenCompatMode(ApplicationInfo ai, int mode) {
- synchronized (ActivityManagerService.this) {
- mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
- }
- }
-
public void closeSystemDialogs(String reason) {
ActivityManagerService.this.closeSystemDialogs(reason);
}
@@ -21124,6 +20998,38 @@
}
@Override
+ public void updateCpuStats() {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.updateCpuStats();
+ }
+ }
+
+ @Override
+ public void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.updateUsageStats(activity, uid, userId, resumed);
+ }
+ }
+
+ @Override
+ public void updateForegroundTimeIfOnBattery(
+ String packageName, int uid, long cpuTimeDiff) {
+ synchronized (ActivityManagerService.this) {
+ if (!mBatteryStatsService.isOnBattery()) {
+ return;
+ }
+ final BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
+ synchronized (bsi) {
+ final BatteryStatsImpl.Uid.Proc ps =
+ bsi.getProcessStatsLocked(uid, packageName);
+ if (ps != null) {
+ ps.addForegroundTimeLocked(cpuTimeDiff);
+ }
+ }
+ }
+ }
+
+ @Override
public void sendForegroundProfileChanged(int userId) {
mUserController.sendForegroundProfileChanged(userId);
}
@@ -21183,6 +21089,31 @@
return ActivityManagerService.this.getTaskForActivity(token, onlyRoot);
}
}
+
+ @Override
+ public void setBooting(boolean booting) {
+ mBooting = booting;
+ }
+
+ @Override
+ public boolean isBooting() {
+ return mBooting;
+ }
+
+ @Override
+ public void setBooted(boolean booted) {
+ mBooted = booted;
+ }
+
+ @Override
+ public boolean isBooted() {
+ return mBooted;
+ }
+
+ @Override
+ public void finishBooting() {
+ ActivityManagerService.this.finishBooting();
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 628207c..77cfb12 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,18 +17,18 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -81,6 +81,8 @@
import static android.os.Build.VERSION_CODES.O;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
@@ -93,6 +95,13 @@
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.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -110,14 +119,6 @@
import static com.android.server.am.TaskPersister.DEBUG;
import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -132,6 +133,7 @@
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
@@ -143,7 +145,6 @@
import android.app.servertransaction.PipModeChangeItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.WindowVisibilityItem;
-import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -1279,20 +1280,14 @@
/**
* Check whether this activity can be launched on the specified display.
+ *
* @param displayId Target display id.
- * @return {@code true} if either it is the default display or this activity is resizeable and
- * can be put a secondary screen.
+ * @return {@code true} if either it is the default display or this activity can be put on a
+ * secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
- final TaskRecord task = getTask();
-
- // The resizeability of an Activity's parent task takes precendence over the ActivityInfo.
- // This allows for a non resizable activity to be launched into a resizeable task.
- final boolean resizeable =
- task != null ? task.isResizeable() : supportsResizeableMultiWindow();
-
- return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- resizeable, launchedFromPid, launchedFromUid, info);
+ return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+ launchedFromUid, info);
}
/**
@@ -2331,7 +2326,7 @@
}
final CompatibilityInfo compatInfo =
- service.mAm.compatibilityInfoForPackageLocked(info.applicationInfo);
+ service.compatibilityInfoForPackageLocked(info.applicationInfo);
final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
@@ -2495,6 +2490,14 @@
}
}
+ /**
+ * @return {@code true} if this activity was reparented to another display but
+ * {@link #ensureActivityConfiguration} is not called.
+ */
+ boolean shouldUpdateConfigForDisplayChanged() {
+ return mLastReportedDisplayId != getDisplayId();
+ }
+
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
return ensureActivityConfiguration(globalChanges, preserveWindow,
false /* ignoreStopState */);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index aa4e68d..35a1eb8 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -107,6 +107,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -151,6 +152,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.wm.ConfigurationContainer;
@@ -1514,14 +1516,14 @@
mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode());
- mService.mAm.updateCpuStats();
+ mService.updateCpuStats();
if (prev.attachedToProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
- mService.mAm.updateUsageStats(prev, false);
+ mService.updateUsageStats(prev, false);
mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
@@ -1683,20 +1685,13 @@
if (prev != null) {
prev.resumeKeyDispatchingLocked();
- if (prev.hasProcess() && prev.cpuTimeAtResume > 0
- && mService.mAm.mBatteryStatsService.isOnBattery()) {
- long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
- if (diff > 0) {
- BatteryStatsImpl bsi = mService.mAm.mBatteryStatsService.getActiveStatistics();
- synchronized (bsi) {
- BatteryStatsImpl.Uid.Proc ps =
- bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
- prev.info.packageName);
- if (ps != null) {
- ps.addForegroundTimeLocked(diff);
- }
- }
- }
+ final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
+ if (prev.hasProcess() && prev.cpuTimeAtResume > 0 && diff > 0) {
+ final Runnable r = PooledLambda.obtainRunnable(
+ ActivityManagerInternal::updateForegroundTimeIfOnBattery,
+ mService.mAmInternal, prev.info.packageName, prev.info.applicationInfo.uid,
+ diff);
+ mService.mH.post(r);
}
prev.cpuTimeAtResume = 0; // reset it
}
@@ -1713,7 +1708,7 @@
mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
}
- void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
+ private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
mStackSupervisor.mStoppingActivities.add(r);
@@ -2407,7 +2402,7 @@
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
- if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
+ if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
return false;
}
@@ -2691,7 +2686,7 @@
lastStack == null ? null :lastStack.mResumedActivity;
final ActivityState lastState = next.getState();
- mService.mAm.updateCpuStats();
+ mService.updateCpuStats();
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+ " (in existing)");
@@ -4166,7 +4161,7 @@
mWindowManager.notifyAppRelaunchesCleared(r.appToken);
}
- void removeTimeoutsForActivityLocked(ActivityRecord r) {
+ private void removeTimeoutsForActivityLocked(ActivityRecord r) {
mStackSupervisor.removeTimeoutsForActivityLocked(r);
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
@@ -4368,12 +4363,8 @@
if (hadApp) {
if (removeFromApp) {
r.app.removeActivity(r);
- if (mService.mAm.mHeavyWeightProcess != null
- && mService.mAm.mHeavyWeightProcess.getWindowProcessController() == r.app
- && !r.app.hasActivities()) {
- mService.mAm.mHeavyWeightProcess = null;
- mService.mAm.mHandler.sendEmptyMessage(
- ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
+ if (!r.app.hasActivities()) {
+ mService.clearHeavyWeightProcessIfEquals(r.app);
}
if (!r.app.hasActivities()) {
// Update any services we are bound to that might care about whether
@@ -4564,7 +4555,7 @@
r.getTask().taskId, r.shortComponentName,
"proc died without state saved");
if (r.getState() == RESUMED) {
- mService.mAm.updateUsageStats(r, false);
+ mService.updateUsageStats(r, false);
}
}
} else {
@@ -5375,12 +5366,14 @@
display.positionChildAtTop(this, false /* includingParents */);
}
+ /** NOTE: Should only be called from {@link TaskRecord#reparent}. */
void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
boolean setPause, String reason) {
if (!moveToFront) {
return;
}
+ final ActivityState origState = r.getState();
// If the activity owns the last resumed activity, transfer that together,
// so that we don't resume the same activity again in the new stack.
// Apps may depend on onResume()/onPause() being called in pairs.
@@ -5393,9 +5386,14 @@
mPausingActivity = r;
schedulePauseTimeout(r);
}
- // Move the stack in which we are placing the activity to the front. The call will also
- // make sure the activity focus is set.
+ // Move the stack in which we are placing the activity to the front.
moveToFront(reason);
+ // If the original state is resumed, there is no state change to update focused app.
+ // So here makes sure the activity focus is set if it is the top.
+ if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+ // TODO(b/111361570): Support multiple focused apps in WM
+ mService.setResumedActivityUncheckLocked(r, reason);
+ }
}
public int getStackId() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 310898e..c887370 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -488,8 +488,8 @@
}
/** Check if placing task or activity on specified display is allowed. */
- boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
- int callingUid, ActivityInfo activityInfo) {
+ boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+ ActivityInfo activityInfo) {
if (displayId == DEFAULT_DISPLAY) {
// No restrictions for the default display.
return true;
@@ -498,10 +498,6 @@
// Can't launch on secondary displays if feature is not supported.
return false;
}
- if (!resizeable && !displayConfigMatchesGlobal(displayId)) {
- // Can't apply wrong configuration to non-resizeable activities.
- return false;
- }
if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
// Can't place activities to a display that has restricted launch rules.
// In this case the request should be made by explicitly adding target display id and
@@ -690,7 +686,7 @@
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
final Display[] displays = mDisplayManager.getDisplays();
- for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
+ for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
@@ -804,7 +800,7 @@
}
final ActivityRecord r = topRunningActivityLocked();
- if (mService.mAm.mBooting || !mService.mAm.mBooted) {
+ if (mService.isBooting() || !mService.isBooted()) {
if (r != null && r.idle) {
checkFinishBootingLocked();
}
@@ -836,7 +832,7 @@
}
boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
- if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
+ if (!mService.isBooting() && !mService.isBooted()) {
// Not ready yet!
return false;
}
@@ -1534,7 +1530,7 @@
r.sleeping = false;
r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
- r.compat = mService.mAm.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+ r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
ProfilerInfo profilerInfo = null;
if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
@@ -1595,22 +1591,17 @@
if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
&& mService.mAm.mHasHeavyWeightFeature) {
- // This may be a heavy-weight process! Note that the package
- // manager will ensure that only activity can run in the main
- // process of the .apk, which is the only thing that will be
- // considered heavy-weight.
+ // This may be a heavy-weight process! Note that the package manager will ensure
+ // that only activity can run in the main process of the .apk, which is the only
+ // thing that will be considered heavy-weight.
if (app.processName.equals(app.info.packageName)) {
- if (mService.mAm.mHeavyWeightProcess != null
- && mService.mAm.mHeavyWeightProcess != app) {
- Slog.w(TAG, "Starting new heavy weight process " + app
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != proc) {
+ Slog.w(TAG, "Starting new heavy weight process " + proc
+ " when already running "
- + mService.mAm.mHeavyWeightProcess);
+ + mService.mHeavyWeightProcess);
}
- mService.mAm.mHeavyWeightProcess = app;
- Message msg = mService.mAm.mHandler.obtainMessage(
- ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
- msg.obj = r;
- mService.mAm.mHandler.sendMessage(msg);
+ mService.setHeavyWeightProcess(r);
}
}
@@ -2057,15 +2048,15 @@
*/
@GuardedBy("mService")
private boolean checkFinishBootingLocked() {
- final boolean booting = mService.mAm.mBooting;
+ final boolean booting = mService.isBooting();
boolean enableScreen = false;
- mService.mAm.mBooting = false;
- if (!mService.mAm.mBooted) {
- mService.mAm.mBooted = true;
+ mService.setBooting(false);
+ if (!mService.isBooted()) {
+ mService.setBooted(true);
enableScreen = true;
}
if (booting || enableScreen) {
- mService.mAm.postFinishBooting(booting, enableScreen);
+ mService.postFinishBooting(booting, enableScreen);
}
return booting;
}
@@ -3686,7 +3677,7 @@
final ActivityStack stack = r.getStack();
if (isTopDisplayFocusedStack(stack)) {
- mService.mAm.updateUsageStats(r, true);
+ mService.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -4530,11 +4521,14 @@
mService.setTaskWindowingMode(task.taskId,
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
if (preferredDisplayId != actualDisplayId) {
- // Display a warning toast that we tried to put a non-resizeable task on a secondary
- // display with config different from global config.
+ Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
+ // Display a warning toast that we failed to put a task on a secondary display.
mService.getTaskChangeNotificationController()
.notifyActivityLaunchOnSecondaryDisplayFailed();
return;
+ } else if (!forceNonResizable && handleForcedResizableTask(task,
+ FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) {
+ return;
}
}
@@ -4554,16 +4548,23 @@
return;
}
+ handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
+ }
+
+ /**
+ * @return {@code true} if the top activity of the task is forced to be resizable and the user
+ * was notified about activity being forced resized.
+ */
+ private boolean handleForcedResizableTask(TaskRecord task, int reason) {
final ActivityRecord topActivity = task.getTopActivity();
if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
- && !topActivity.noDisplay) {
+ && !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
- final int reason = isSecondaryDisplayPreferred
- ? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
- : FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
task.taskId, reason, packageName);
+ return true;
}
+ return false;
}
void activityRelaunchedLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index eb41fe7..7da0519 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -52,6 +52,7 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -944,7 +945,8 @@
auxiliaryResponse == null ? null : auxiliaryResponse.filters);
}
- void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
+ void postStartActivityProcessing(ActivityRecord r, int result,
+ ActivityStack startedActivityStack) {
if (ActivityManager.isStartResultFatalError(result)) {
return;
}
@@ -956,14 +958,6 @@
// about this, so it waits for the new activity to become visible instead.
mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
- ActivityStack startedActivityStack = null;
- final ActivityStack currentStack = r.getStack();
- if (currentStack != null) {
- startedActivityStack = currentStack;
- } else if (mTargetStack != null) {
- startedActivityStack = targetStack;
- }
-
if (startedActivityStack == null) {
return;
}
@@ -1085,9 +1079,9 @@
// This may be a heavy-weight process! Check to see if we already
// have another, different heavy-weight process running.
if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
- final ProcessRecord heavy = mService.mAm.mHeavyWeightProcess;
- if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
- || !heavy.processName.equals(aInfo.processName))) {
+ final WindowProcessController heavy = mService.mHeavyWeightProcess;
+ if (heavy != null && (heavy.mInfo.uid != aInfo.applicationInfo.uid
+ || !heavy.mName.equals(aInfo.processName))) {
int appCallingUid = callingUid;
if (caller != null) {
ProcessRecord callerApp = mService.mAm.getRecordForAppLocked(caller);
@@ -1115,8 +1109,7 @@
}
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
new IntentSender(target));
- heavy.getWindowProcessController().updateIntentForHeavyWeightActivity(
- newIntent);
+ heavy.updateIntentForHeavyWeightActivity(newIntent);
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
aInfo.packageName);
newIntent.setFlags(intent.getFlags());
@@ -1239,23 +1232,41 @@
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
int result = START_CANCELED;
+ final ActivityStack startedActivityStack;
try {
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);
} finally {
- // If we are not able to proceed, disassociate the activity from the task. Leaving an
- // activity in an incomplete state can lead to issues, such as performing operations
- // without a window container.
- final ActivityStack stack = mStartActivity.getStack();
- if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
- stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
- null /* intentResultData */, "startActivity", true /* oomAdj */);
+ final ActivityStack currentStack = r.getStack();
+ startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+ if (ActivityManager.isStartResultSuccessful(result)) {
+ if (startedActivityStack != null) {
+ // If there is no state change (e.g. a resumed activity is reparented to
+ // top of another display) to trigger a visibility/configuration checking,
+ // we have to update the configuration for changing to different display.
+ final ActivityRecord currentTop =
+ startedActivityStack.topRunningActivityLocked();
+ if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+ mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+ }
+ } else {
+ // If we are not able to proceed, disassociate the activity from the task.
+ // Leaving an activity in an incomplete state can lead to issues, such as
+ // performing operations without a window container.
+ final ActivityStack stack = mStartActivity.getStack();
+ if (stack != null) {
+ stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+ null /* intentResultData */, "startActivity", true /* oomAdj */);
+ }
}
mService.mWindowManager.continueSurfaceLayout();
}
- postStartActivityProcessing(r, result, mTargetStack);
+ postStartActivityProcessing(r, result, startedActivityStack);
return result;
}
@@ -1901,14 +1912,20 @@
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
// the same behavior as if a new instance was being started, which means not bringing it
// to the front if the caller is not itself in the front.
- final ActivityStack focusStack = mSupervisor.getTopDisplayFocusedStack();
- ActivityRecord curTop = (focusStack == null)
- ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+ final boolean differentTopTask;
+ if (mPreferredDisplayId == mTargetStack.mDisplayId) {
+ final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
+ final ActivityRecord curTop = (focusStack == null)
+ ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+ final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
+ differentTopTask = topTask != null
+ && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
+ } else {
+ // The existing task should always be different from those in other displays.
+ differentTopTask = true;
+ }
- final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
- if (topTask != null
- && (topTask != intentActivity.getTask() || topTask != focusStack.topTask())
- && !mAvoidMoveToFront) {
+ if (differentTopTask && !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 212844a..9acb04b 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -125,7 +125,11 @@
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
+import android.app.INotificationManager;
import android.app.ITaskStackListener;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ProfilerInfo;
import android.app.RemoteAction;
@@ -150,6 +154,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -208,10 +213,13 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.LocalServices;
@@ -266,6 +274,7 @@
ActivityManagerInternal mAmInternal;
UriGrantsManagerInternal mUgmInternal;
private PackageManagerInternal mPmInternal;
+ private ActivityTaskManagerInternal mInternal;
/* Global service lock used by the package the owns this service. */
Object mGlobalLock;
ActivityStackSupervisor mStackSupervisor;
@@ -278,6 +287,8 @@
final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
/** This is the process holding what we currently consider to be the "home" activity. */
WindowProcessController mHomeProcess;
+ /** The currently running heavy-weight process, if any. */
+ WindowProcessController mHeavyWeightProcess = null;
/**
* This is the process holding the activity the user last visited that is in a different process
* from the one they are currently in.
@@ -475,6 +486,12 @@
private AppWarnings mAppWarnings;
+ /**
+ * Packages that the user has asked to have run in screen size
+ * compatibility mode instead of filling the screen.
+ */
+ CompatModePackages mCompatModePackages;
+
private FontScaleSettingObserver mFontScaleSettingObserver;
private final class FontScaleSettingObserver extends ContentObserver {
@@ -607,8 +624,9 @@
mGlobalLock = mAm;
mH = new H(mAm.mHandlerThread.getLooper());
mUiHandler = new UiHandler();
- mAppWarnings = new AppWarnings(
- this, mUiContext, mH, mUiHandler, SystemServiceManager.ensureSystemDir());
+ final File systemDir = SystemServiceManager.ensureSystemDir();
+ mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
+ mCompatModePackages = new CompatModePackages(this, systemDir, mH);
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
@@ -687,7 +705,8 @@
}
private void start() {
- LocalServices.addService(ActivityTaskManagerInternal.class, new LocalService());
+ mInternal = new LocalService();
+ LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
public static final class Lifecycle extends SystemService {
@@ -1461,16 +1480,13 @@
@Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
- ApplicationInfo ai;
synchronized (mGlobalLock) {
final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
- ai = r.info.applicationInfo;
+ return mCompatModePackages.computeCompatModeLocked(r.info.applicationInfo);
}
-
- return mAmInternal.getPackageScreenCompatMode(ai);
}
@Override
@@ -1485,9 +1501,8 @@
return;
}
ai = r.info.applicationInfo;
+ mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
}
-
- mAmInternal.setPackageScreenCompatMode(ai, mode);
}
@Override
@@ -4112,7 +4127,7 @@
return mVrController.shouldDisableNonVrUiLocked();
}
- void applyUpdateVrModeLocked(ActivityRecord r) {
+ private void applyUpdateVrModeLocked(ActivityRecord r) {
// VR apps are expected to run in a main display. If an app is turning on VR for
// itself, but lives in a dynamic stack, then make sure that it is moved to the main
// fullscreen stack before enabling VR Mode.
@@ -4143,6 +4158,40 @@
});
}
+ @Override
+ public int getPackageScreenCompatMode(String packageName) {
+ enforceNotIsolatedCaller("getPackageScreenCompatMode");
+ synchronized (mGlobalLock) {
+ return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
+ }
+ }
+
+ @Override
+ public void setPackageScreenCompatMode(String packageName, int mode) {
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+ "setPackageScreenCompatMode");
+ synchronized (mGlobalLock) {
+ mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
+ }
+ }
+
+ @Override
+ public boolean getPackageAskScreenCompat(String packageName) {
+ enforceNotIsolatedCaller("getPackageAskScreenCompat");
+ synchronized (mGlobalLock) {
+ return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
+ }
+ }
+
+ @Override
+ public void setPackageAskScreenCompat(String packageName, boolean ask) {
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+ "setPackageAskScreenCompat");
+ synchronized (mGlobalLock) {
+ mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
+ }
+ }
+
ActivityStack getTopDisplayFocusedStack() {
return mStackSupervisor.getTopDisplayFocusedStack();
}
@@ -4688,11 +4737,7 @@
return mSleeping;
}
- /**
- * Update AMS states when an activity is resumed. This should only be called by
- * {@link ActivityStack#onActivityStateChanged(
- * ActivityRecord, ActivityStack.ActivityState, String)} when an activity is resumed.
- */
+ /** Update AMS states when an activity is resumed. */
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final TaskRecord task = r.getTask();
if (task.isActivityTypeStandard()) {
@@ -4812,6 +4857,122 @@
mH.post(mAmInternal::updateOomAdj);
}
+ void updateCpuStats() {
+ mH.post(mAmInternal::updateCpuStats);
+ }
+
+ void updateUsageStats(ActivityRecord component, boolean resumed) {
+ final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
+ mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed);
+ mH.sendMessage(m);
+ }
+
+ void setBooting(boolean booting) {
+ mAmInternal.setBooting(booting);
+ }
+
+ boolean isBooting() {
+ return mAmInternal.isBooting();
+ }
+
+ void setBooted(boolean booted) {
+ mAmInternal.setBooted(booted);
+ }
+
+ boolean isBooted() {
+ return mAmInternal.isBooted();
+ }
+
+ void postFinishBooting(boolean finishBooting, boolean enableScreen) {
+ mH.post(() -> {
+ if (finishBooting) {
+ mAmInternal.finishBooting();
+ }
+ if (enableScreen) {
+ mInternal.enableScreenAfterBoot(isBooted());
+ }
+ });
+ }
+
+ void setHeavyWeightProcess(ActivityRecord root) {
+ mHeavyWeightProcess = root.app;
+ final Message m = PooledLambda.obtainMessage(
+ ActivityTaskManagerService::postHeavyWeightProcessNotification, this,
+ root.app, root.intent, root.userId);
+ mH.sendMessage(m);
+ }
+
+ void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
+ if (mHeavyWeightProcess == null || mHeavyWeightProcess != proc) {
+ return;
+ }
+
+ mHeavyWeightProcess = null;
+ final Message m = PooledLambda.obtainMessage(
+ ActivityTaskManagerService::cancelHeavyWeightProcessNotification, this,
+ proc.mUserId);
+ mH.sendMessage(m);
+ }
+
+ private void cancelHeavyWeightProcessNotification(int userId) {
+ final INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag("android", null,
+ SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, userId);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+
+ }
+
+ private void postHeavyWeightProcessNotification(
+ WindowProcessController proc, Intent intent, int userId) {
+ if (proc == null) {
+ return;
+ }
+
+ final INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(proc.mInfo.packageName, 0);
+ String text = mContext.getString(R.string.heavy_weight_notification,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ Notification notification =
+ new Notification.Builder(context,
+ SystemNotificationChannels.HEAVY_WEIGHT_APP)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(text)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(text)
+ .setContentText(
+ mContext.getText(R.string.heavy_weight_notification_detail))
+ .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
+ intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(userId)))
+ .build();
+ try {
+ inm.enqueueNotificationWithTag("android", "android", null,
+ SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, notification, userId);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error showing notification for heavy-weight app", e);
+ } catch (RemoteException e) {
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for heavy notification", e);
+ }
+
+ }
+
// TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
private void startTimeTrackingFocusedActivityLocked() {
final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
@@ -4888,6 +5049,10 @@
mH.post(() -> mAmInternal.scheduleAppGcs());
}
+ CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
+ return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
+ }
+
/**
* Returns the PackageManager. Used by classes hosted by {@link ActivityTaskManagerService}. The
* PackageManager could be unavailable at construction time and therefore needs to be accessed
@@ -5274,6 +5439,28 @@
}
@Override
+ public boolean isHeavyWeightProcess(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ return proc == mHeavyWeightProcess;
+ }
+ }
+
+ @Override
+ public void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(proc);
+ }
+ }
+
+ @Override
+ public void finishHeavyWeightApp() {
+ synchronized (mGlobalLock) {
+ ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
+ mHeavyWeightProcess);
+ }
+ }
+
+ @Override
public boolean isSleeping() {
synchronized (mGlobalLock) {
return isSleepingLocked();
@@ -5382,6 +5569,7 @@
@Override
public void onPackageDataCleared(String name) {
synchronized (mGlobalLock) {
+ mCompatModePackages.handlePackageDataClearedLocked(name);
mAppWarnings.onPackageDataCleared(name);
}
}
@@ -5390,7 +5578,23 @@
public void onPackageUninstalled(String name) {
synchronized (mGlobalLock) {
mAppWarnings.onPackageUninstalled(name);
+ mCompatModePackages.handlePackageUninstalledLocked(name);
}
}
+
+ @Override
+ public void onPackageAdded(String name, boolean replacing) {
+ synchronized (mGlobalLock) {
+ mCompatModePackages.handlePackageAddedLocked(name, replacing);
+ }
+ }
+
+ @Override
+ public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
+ synchronized (mGlobalLock) {
+ return compatibilityInfoForPackageLocked(ai);
+ }
+ }
+
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0a7e127..1387f45 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -302,7 +302,7 @@
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
- mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
+ mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.getReportedProcState());
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 77efbfc..536f3a9 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -51,13 +51,13 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_AM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final AtomicFile mFile;
// Compatibility state: no longer ask user to select the mode.
- public static final int COMPAT_FLAG_DONT_ASK = 1<<0;
+ private static final int COMPAT_FLAG_DONT_ASK = 1<<0;
// Compatibility state: compatibility mode is enabled.
- public static final int COMPAT_FLAG_ENABLED = 1<<1;
+ private static final int COMPAT_FLAG_ENABLED = 1<<1;
private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
@@ -80,7 +80,7 @@
}
};
- public CompatModePackages(ActivityManagerService service, File systemDir, Handler handler) {
+ public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) {
mService = service;
mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode");
mHandler = new CompatHandler(handler.getLooper());
@@ -317,12 +317,12 @@
scheduleWrite();
- final ActivityStack stack = mService.mActivityTaskManager.getTopDisplayFocusedStack();
+ final ActivityStack stack = mService.getTopDisplayFocusedStack();
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
- for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord app = mService.mLruProcesses.get(i);
+ for (int i = mService.mAm.mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mService.mAm.mLruProcesses.get(i);
if (!app.pkgList.containsKey(packageName)) {
continue;
}
@@ -346,10 +346,10 @@
}
}
- void saveCompatModes() {
+ private void saveCompatModes() {
HashMap<String, Integer> pkgs;
- synchronized (mService) {
- pkgs = new HashMap<String, Integer>(mPackages);
+ synchronized (mService.mGlobalLock) {
+ pkgs = new HashMap<>(mPackages);
}
FileOutputStream fos = null;
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index f9dccea0..aad890b 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.os.FileUtils;
+import android.os.SystemProperties;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,6 +39,10 @@
final class MemoryStatUtil {
private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
+ /** True if device has per-app memcg */
+ private static final Boolean DEVICE_HAS_PER_APP_MEMCG =
+ SystemProperties.getBoolean("ro.config.per_app_memcg", false);
+
/** Path to check if device has memcg */
private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
/** Path to memory stat file for logging app start memory state */
@@ -55,15 +60,12 @@
private static final int PGMAJFAULT_INDEX = 11;
private static final int RSS_IN_BYTES_INDEX = 23;
- /** True if device has memcg */
- private static volatile Boolean sDeviceHasMemCg;
-
private MemoryStatUtil() {}
/**
* Reads memory stat for a process.
*
- * Reads from memcg if available on device, else fallback to procfs.
+ * Reads from per-app memcg if available on device, else fallback to procfs.
* Returns null if no stats can be read.
*/
@Nullable
@@ -156,15 +158,10 @@
}
/**
- * Checks if memcg is available on device.
- *
- * Touches the filesystem to do the check.
+ * Returns whether per-app memcg is available on device.
*/
static boolean hasMemcg() {
- if (sDeviceHasMemCg == null) {
- sDeviceHasMemCg = (new File(MEMCG_TEST_PATH)).exists();
- }
- return sDeviceHasMemCg;
+ return DEVICE_HAS_PER_APP_MEMCG;
}
static final class MemoryStat {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 784d62e..3ac7885 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -104,6 +104,11 @@
static final int VISIBLE_APP_ADJ = 100;
static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
+ // This is a process that was recently TOP and moved to FGS. Continue to treat it almost
+ // like a foreground app for a while.
+ // @see TOP_TO_FGS_GRACE_PERIOD
+ static final int PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
+
// This is the process running the current foreground app. We'd really
// rather not kill it!
static final int FOREGROUND_APP_ADJ = 0;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 144f18b..d3dc0f3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -198,6 +198,7 @@
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
long lastProviderTime; // The last time someone else was using a provider in this process.
+ long lastTopTime; // The last time the process was in the TOP state or greater.
boolean reportLowMemory; // Set to true when waiting to report low mem
boolean empty; // Is this an empty background process?
boolean cached; // Is this a cached process?
@@ -415,6 +416,11 @@
TimeUtils.formatDuration(lastProviderTime, nowUptime, pw);
pw.println();
}
+ if (lastTopTime > 0) {
+ pw.print(prefix); pw.print("lastTopTime=");
+ TimeUtils.formatDuration(lastTopTime, nowUptime, pw);
+ pw.println();
+ }
if (hasStartedServices) {
pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
}
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index b817669..3f603cc 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -37,20 +37,6 @@
]
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.am."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "WmTests",
"options": [
{
@@ -73,14 +59,6 @@
"name": "CtsActivityManagerDeviceSdk25TestCases"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.am."
- }
- ]
- },
- {
"name": "WmTests",
"options": [
{
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7256e23..9b42d65 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1521,14 +1521,14 @@
/**
* Check whether this task can be launched on the specified display.
+ *
* @param displayId Target display id.
- * @return {@code true} if either it is the default display or this activity is resizeable and
- * can be put a secondary screen.
+ * @return {@code true} if either it is the default display or this activity can be put on a
+ * secondary display.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- isResizeable(false /* checkSupportsPip */), -1 /* don't check PID */,
- -1 /* don't check UID */, null /* activityInfo */);
+ -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
}
/**
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 36e7cba..65e55d6 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -26,10 +26,13 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.security.KeyStore;
import android.util.Slog;
import com.android.internal.statusbar.IStatusBarService;
+import java.util.ArrayList;
+
/**
* A class to keep track of the authentication state for a given client.
*/
@@ -39,34 +42,79 @@
public abstract int handleFailedAttempt();
public abstract void resetFailedAttempts();
+ public abstract String getErrorString(int error, int vendorCode);
+ public abstract String getAcquiredString(int acquireInfo, int vendorCode);
+ /**
+ * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
+ */
+ public abstract int getBiometricType();
public static final int LOCKOUT_NONE = 0;
public static final int LOCKOUT_TIMED = 1;
public static final int LOCKOUT_PERMANENT = 2;
- private final BiometricAuthenticator mAuthenticator;
+ private final boolean mRequireConfirmation;
// Callback mechanism received from the client
// (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
private IBiometricPromptReceiver mDialogReceiverFromClient;
private Bundle mBundle;
private IStatusBarService mStatusBarService;
private boolean mInLockout;
+ private TokenEscrow mEscrow;
protected boolean mDialogDismissed;
+ /**
+ * Container that holds the identifier and authToken. For biometrics that require user
+ * confirmation, these should not be sent to their final destinations until the user confirms.
+ */
+ class TokenEscrow {
+ final BiometricAuthenticator.Identifier mIdentifier;
+ final ArrayList<Byte> mToken;
+
+ TokenEscrow(BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
+ mIdentifier = identifier;
+ mToken = token;
+ }
+
+ BiometricAuthenticator.Identifier getIdentifier() {
+ return mIdentifier;
+ }
+
+ ArrayList<Byte> getToken() {
+ return mToken;
+ }
+ }
+
// Receives events from SystemUI and handles them before forwarding them to BiometricDialog
protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
@Override // binder call
public void onDialogDismissed(int reason) {
if (mBundle != null && mDialogReceiverFromClient != null) {
try {
- mDialogReceiverFromClient.onDialogDismissed(reason);
+ if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Positive button is used by passive modalities as a "confirm" button,
+ // do not send to client
+ mDialogReceiverFromClient.onDialogDismissed(reason);
+ }
if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
0 /* vendorCode */);
+ } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Have the service send the token to KeyStore, and send onAuthenticated
+ // to the application.
+ if (mEscrow != null) {
+ if (DEBUG) Slog.d(getLogTag(), "Confirmed");
+ addTokenToKeyStore(mEscrow.getToken());
+ notifyClientAuthenticationSucceeded(mEscrow.getIdentifier());
+ mEscrow = null;
+ onAuthenticationConfirmed();
+ } else {
+ Slog.e(getLogTag(), "Escrow is null!!!");
+ }
}
mDialogDismissed = true;
} catch (RemoteException e) {
- Slog.e(getLogTag(), "Unable to notify dialog dismissed", e);
+ Slog.e(getLogTag(), "Remote exception", e);
}
stop(true /* initiatedByClient */);
}
@@ -84,20 +132,26 @@
*/
public abstract void onStop();
+ /**
+ * This method is called when biometric authentication was confirmed by the user. The client
+ * should be removed.
+ */
+ public abstract void onAuthenticationConfirmed();
+
public AuthenticationClient(Context context, Metrics metrics,
BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, Bundle bundle,
IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- BiometricAuthenticator authenticator) {
+ boolean requireConfirmation) {
super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
restricted, owner);
mOpId = opId;
mBundle = bundle;
mDialogReceiverFromClient = dialogReceiver;
mStatusBarService = statusBarService;
- mAuthenticator = authenticator;
mHandler = new Handler(Looper.getMainLooper());
+ mRequireConfirmation = requireConfirmation;
}
@Override
@@ -115,8 +169,7 @@
if (mBundle != null) {
try {
if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- mStatusBarService.onBiometricHelp(
- mAuthenticator.getAcquiredString(acquiredInfo, vendorCode));
+ mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode));
}
return false; // acquisition continues
} catch (RemoteException e) {
@@ -144,8 +197,7 @@
}
if (mBundle != null) {
try {
- mStatusBarService.onBiometricError(
- mAuthenticator.getErrorString(error, vendorCode));
+ mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception when sending error", e);
}
@@ -153,9 +205,41 @@
return super.onError(deviceId, error, vendorCode);
}
+ private void notifyClientAuthenticationSucceeded(BiometricAuthenticator.Identifier identifier)
+ throws RemoteException {
+ final BiometricService.ServiceListener listener = getListener();
+ // 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());
+ }
+ }
+
+ private void addTokenToKeyStore(ArrayList<Byte> token) {
+ // Send the token to KeyStore
+ final byte[] byteToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ byteToken[i] = token.get(i);
+ }
+ KeyStore.getInstance().addAuthToken(byteToken);
+ }
+
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
+ if (authenticated) {
+ if (mRequireConfirmation) {
+ // Store the token so it can be sent to keystore after the user presses confirm
+ mEscrow = new TokenEscrow(identifier, token);
+ } else {
+ addTokenToKeyStore(token);
+ }
+ }
+
boolean result = false;
// If the biometric dialog is showing, notify authentication succeeded
@@ -183,15 +267,8 @@
Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
+ ", id=" + identifier.getBiometricId());
}
-
- // Explicitly have if/else here to make it super obvious in case the code is
- // touched in the future.
- if (!getIsRestricted()) {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), identifier, getTargetUserId());
- } else {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), null, getTargetUserId());
+ if (!mRequireConfirmation) {
+ notifyClientAuthenticationSucceeded(identifier);
}
}
} catch (RemoteException e) {
@@ -220,7 +297,7 @@
// Send the lockout message to the system dialog
if (mBundle != null) {
mStatusBarService.onBiometricError(
- mAuthenticator.getErrorString(errorCode, 0 /* vendorCode */));
+ getErrorString(errorCode, 0 /* vendorCode */));
mHandler.postDelayed(() -> {
try {
listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
@@ -240,7 +317,8 @@
if (listener != null) {
vibrateSuccess();
}
- result |= true; // we have a valid biometric, done
+ // we have a valid biometric that doesn't require confirmation, done
+ result |= !mRequireConfirmation;
resetFailedAttempts();
onStop();
}
@@ -268,7 +346,7 @@
if (mBundle != null) {
try {
mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
- mAuthenticator.getType());
+ getBiometricType(), mRequireConfirmation);
} catch (RemoteException e) {
Slog.e(getLogTag(), "Unable to show biometric dialog", e);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricPromptService.java b/services/core/java/com/android/server/biometrics/BiometricPromptService.java
index 29eda8b..35c9d3e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricPromptService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricPromptService.java
@@ -41,7 +41,6 @@
import android.util.Slog;
import com.android.internal.R;
-import com.android.internal.os.SomeArgs;
import com.android.server.SystemService;
import java.util.ArrayList;
@@ -133,6 +132,12 @@
// AppOps and foreground check.
checkPermission();
+ if (token == null || receiver == null || opPackageName == null || bundle == null
+ || dialogReceiver == null) {
+ Slog.e(TAG, "Unable to authenticate, one or more null arguments");
+ return;
+ }
+
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -149,9 +154,9 @@
} else if (mCurrentModality == BIOMETRIC_IRIS) {
Slog.w(TAG, "Unsupported modality");
} else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.authenticateFromService(token, sessionId, userId,
- receiver, flags, opPackageName, bundle, dialogReceiver,
- callingUid, callingPid, callingUserId);
+ mFaceService.authenticateFromService(true /* requireConfirmation */, token,
+ sessionId, userId, receiver, flags, opPackageName, bundle,
+ dialogReceiver, callingUid, callingPid, callingUserId);
} else {
Slog.w(TAG, "Unsupported modality");
}
@@ -166,6 +171,11 @@
throws RemoteException {
checkPermission();
+ if (token == null || opPackageName == null) {
+ Slog.e(TAG, "Unable to cancel, one or more null arguments");
+ return;
+ }
+
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -295,7 +305,9 @@
try {
receiver.onError(0 /* deviceId */,
BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
- hardwareUnavailable);
+ FaceManager.getErrorString(getContext(),
+ BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
+ 0 /* vendorCode */));
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send error", e);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a181b61..c9b740d 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -51,7 +51,6 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.security.KeyStore;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -108,6 +107,8 @@
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
protected int mCurrentUserId = UserHandle.USER_NULL;
+ // Tracks if the current authentication makes use of CryptoObjects.
+ protected boolean mIsCrypto;
// Normal authentications are tracked by mPerformanceMap.
protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
// Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
@@ -217,16 +218,16 @@
*/
protected void notifyClientActiveCallbacks(boolean isActive) {}
- protected class AuthenticationClientImpl extends AuthenticationClient {
+ protected abstract class AuthenticationClientImpl extends AuthenticationClient {
public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, Bundle bundle,
IBiometricPromptReceiver dialogReceiver,
- IStatusBarService statusBarService, BiometricAuthenticator authenticator) {
+ IStatusBarService statusBarService, boolean requireConfirmation) {
super(context, getMetrics(), daemon, halDeviceId, token, listener,
targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
- statusBarService, authenticator);
+ statusBarService, requireConfirmation);
}
@Override
@@ -277,6 +278,11 @@
}
return AuthenticationClient.LOCKOUT_NONE;
}
+
+ @Override
+ public void onAuthenticationConfirmed() {
+ removeClient(mCurrentClient);
+ }
}
protected class EnrollClientImpl extends EnrollClient {
@@ -585,14 +591,7 @@
ClientMonitor client = mCurrentClient;
final boolean authenticated = identifier.getBiometricId() != 0;
- if (authenticated) {
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
- }
- KeyStore.getInstance().addAuthToken(byteToken);
- }
- if (client != null && client.onAuthenticated(identifier, authenticated)) {
+ if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
removeClient(client);
}
if (authenticated) {
@@ -715,6 +714,7 @@
pmap.put(mCurrentUserId, stats);
}
mPerformanceStats = stats;
+ mIsCrypto = (opId != 0);
startAuthentication(client, opPackageName);
});
@@ -847,7 +847,7 @@
return mKeyguardPackage.equals(clientPackage);
}
- private int getLockoutMode() {
+ protected int getLockoutMode() {
final int currentUser = ActivityManager.getCurrentUser();
final int failedAttempts = mFailedAttempts.get(currentUser, 0);
if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
@@ -1109,4 +1109,4 @@
LockoutResetMonitor monitor) {
mLockoutMonitors.remove(monitor);
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index d30bed2..408e26a 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -28,6 +28,7 @@
import com.android.internal.logging.MetricsLogger;
+import java.util.ArrayList;
import java.util.NoSuchElementException;
/**
@@ -128,7 +129,7 @@
public abstract boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
int remaining);
public abstract boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated);
+ boolean authenticated, ArrayList<Byte> token);
public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier,
int remaining);
public abstract boolean onEnumerationResult(
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index c305eca6..7d186a9 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -126,7 +127,7 @@
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enroll!");
return true; // Invalid for EnrollClient
}
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index b763769..159d95e 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -23,6 +23,8 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
+
/**
* A class to keep track of the enumeration state for a given client.
*/
@@ -94,7 +96,7 @@
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enumerate!");
return true; // Invalid for Enumerate.
}
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 3bf7f04..1995b9c 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -23,6 +23,8 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.ArrayList;
+
/**
* A class to keep track of the remove state for a given client.
*/
@@ -111,7 +113,7 @@
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated) {
+ boolean authenticated, ArrayList<Byte> token) {
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 8afac97..376eabc 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -49,6 +49,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.BiometricService;
@@ -82,6 +83,34 @@
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
+ private final class FaceAuthClient extends AuthenticationClientImpl {
+ public FaceAuthClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int targetUserId, int groupId, long opId,
+ boolean restricted, String owner, Bundle bundle,
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean requireConfirmation) {
+ super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+ restricted, owner, bundle, dialogReceiver, statusBarService,
+ requireConfirmation);
+ }
+
+ @Override
+ public String getErrorString(int error, int vendorCode) {
+ return FaceManager.getErrorString(getContext(), error, vendorCode);
+ }
+
+ @Override
+ public String getAcquiredString(int acquireInfo, int vendorCode) {
+ return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
+ }
+
+ @Override
+ public int getBiometricType() {
+ return BiometricAuthenticator.TYPE_FACE;
+ }
+ }
+
/**
* Receives the incoming binder calls from FaceManager.
*/
@@ -128,26 +157,26 @@
final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = isRestricted();
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- null /* bundle */, null /* dialogReceiver */, mStatusBarService, mFaceManager);
-
+ null /* bundle */, null /* dialogReceiver */, mStatusBarService,
+ false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(IBinder token, long opId, int groupId,
- IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ public void authenticateFromService(boolean requireConfirmation, IBinder token, long opId,
+ int groupId, IBiometricPromptServiceReceiver receiver, int flags,
+ String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver,
int callingUid, int callingPid, int callingUserId) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = true; // BiometricPrompt is always restricted
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- bundle, dialogReceiver, mStatusBarService, mFaceManager);
+ bundle, dialogReceiver, mStatusBarService, true /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -338,8 +367,8 @@
*/
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onAcquired(deviceId,
- mFaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
- mFaceManager.getAcquiredString(acquiredInfo, vendorCode));
+ FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
+ FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
}
}
@@ -362,7 +391,7 @@
public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onError(deviceId, error,
- mFaceManager.getErrorString(error, vendorCode));
+ FaceManager.getErrorString(getContext(), error, vendorCode));
}
}
}
@@ -447,8 +476,6 @@
@GuardedBy("this")
private IBiometricsFace mDaemon;
private long mHalDeviceId;
- // Use FaceManager to get strings, so BiometricPrompt interface is cleaner
- private FaceManager mFaceManager;
/**
* Receives callbacks from the HAL.
@@ -589,7 +616,6 @@
super.onStart();
publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
- mFaceManager = (FaceManager) getContext().getSystemService(Context.FACE_SERVICE);
}
@Override
@@ -862,4 +888,4 @@
mPerformanceMap.clear();
mCryptoPerformanceMap.clear();
}
-}
\ No newline at end of file
+}
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 95fb9e3..1852aa8 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -50,12 +50,15 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
+import android.util.StatsLog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricService;
import com.android.server.biometrics.BiometricUtils;
import com.android.server.biometrics.ClientMonitor;
@@ -102,6 +105,34 @@
}
}
+ private final class FingerprintAuthClient extends AuthenticationClientImpl {
+ public FingerprintAuthClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int targetUserId, int groupId, long opId,
+ boolean restricted, String owner, Bundle bundle,
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean requireConfirmation) {
+ super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+ restricted, owner, bundle, dialogReceiver, statusBarService,
+ requireConfirmation);
+ }
+
+ @Override
+ public String getErrorString(int error, int vendorCode) {
+ return FingerprintManager.getErrorString(getContext(), error, vendorCode);
+ }
+
+ @Override
+ public String getAcquiredString(int acquireInfo, int vendorCode) {
+ return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
+ }
+
+ @Override
+ public int getBiometricType() {
+ return BiometricAuthenticator.TYPE_FINGERPRINT;
+ }
+ }
+
/**
* Receives the incoming binder calls from FingerprintManager.
*/
@@ -149,11 +180,10 @@
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
final boolean restricted = isRestricted();
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
- null /* dialogReceiver */, mStatusBarService, mFingerprintManager);
-
+ null /* dialogReceiver */, mStatusBarService, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@@ -164,11 +194,11 @@
int callingUid, int callingPid, int callingUserId) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = true; // BiometricPrompt is always restricted
- final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService, mFingerprintManager);
+ dialogReceiver, mStatusBarService, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -371,7 +401,8 @@
throws RemoteException {
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onAcquired(deviceId, acquiredInfo,
- mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
+ FingerprintManager.getAcquiredString(
+ getContext(), acquiredInfo, vendorCode));
}
}
@@ -394,7 +425,7 @@
public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
if (mBiometricPromptServiceReceiver != null) {
mBiometricPromptServiceReceiver.onError(deviceId, error,
- mFingerprintManager.getErrorString(error, vendorCode));
+ FingerprintManager.getErrorString(getContext(), error, vendorCode));
}
}
}
@@ -567,8 +598,6 @@
private long mHalDeviceId;
private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
- // Use FingerprintManager to get strings, so BiometricPrompt interface is cleaner.
- private FingerprintManager mFingerprintManager;
/**
* Receives callbacks from the HAL.
@@ -590,6 +619,11 @@
public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
mHandler.post(() -> {
FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+ if (getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+ && getCurrentClient() instanceof AuthenticationClient) {
+ // Ignore enrollment acquisitions or acquisitions when we are locked out.
+ StatsLog.write(StatsLog.FINGERPRINT_ACQUIRED, mCurrentUserId, mIsCrypto);
+ }
});
}
@@ -599,6 +633,22 @@
mHandler.post(() -> {
Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
FingerprintService.super.handleAuthenticated(fp, token);
+ // Send authentication to statsd.
+ final boolean authenticated = fingerId != 0;
+ StatsLog.write(StatsLog.FINGERPRINT_AUTHENTICATED, mCurrentUserId, mIsCrypto,
+ authenticated);
+ if (!authenticated) {
+ // If we failed to authenticate because of a lockout, inform statsd.
+ final int lockoutMode = getLockoutMode();
+ if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+ StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId,
+ mIsCrypto, StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__LOCKOUT);
+ } else if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+ StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId,
+ mIsCrypto,
+ StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__PERMANENT_LOCKOUT);
+ }
+ }
});
}
@@ -717,8 +767,6 @@
super.onStart();
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
- mFingerprintManager = (FingerprintManager)
- getContext().getSystemService(Context.FINGERPRINT_SERVICE);
}
@Override
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index c028a43..873a8e3 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -639,12 +639,20 @@
}
}
- // Otherwise only focused applications can access the clipboard.
- boolean uidFocused = mWm.isUidFocused(callingUid);
- if (!uidFocused) {
- Slog.e(TAG, "Denying clipboard access to " + callingPackage
- + ", application is not in focus.");
+ switch (op) {
+ case AppOpsManager.OP_READ_CLIPBOARD:
+ // Clipboard can only be read by applications with focus.
+ boolean uidFocused = mWm.isUidFocused(callingUid);
+ if (!uidFocused) {
+ Slog.e(TAG, "Denying clipboard access to " + callingPackage
+ + ", application is not in focus.");
+ }
+ return uidFocused;
+ case AppOpsManager.OP_WRITE_CLIPBOARD:
+ // Writing is allowed without focus.
+ return true;
+ default:
+ throw new IllegalArgumentException("Unknown clipboard appop " + op);
}
- return uidFocused;
}
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
index d4932f9..2fdcb51 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
@@ -25,6 +25,9 @@
// State that waits for <Active Source> once send <Request Active Source>.
private static final int STATE_WAITING_FOR_ACTIVE_SOURCE = 1;
+ // State that waits for TV supporting Audio System Mode or not
+ // once received <Active Source>
+ private static final int STATE_WAITING_FOR_TV_SUPPORT = 2;
@VisibleForTesting
static final int MAX_RETRY_COUNT = 5;
@@ -60,6 +63,7 @@
if (physicalAddress != getSourcePath()) {
audioSystem().setActiveSource(cmd.getSource(), physicalAddress);
}
+ mState = STATE_WAITING_FOR_TV_SUPPORT;
queryTvSystemAudioModeSupport();
return true;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index b5a9f74..3264790 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,45 +18,11 @@
import android.content.ComponentName;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-
/**
* Input method manager local system service interface.
*/
public abstract class InputMethodManagerInternal {
/**
- * Called by the window manager service when a client process is being attached to the window
- * manager service.
- *
- * <p>The caller must not have WindowManagerService lock. This method internally acquires
- * InputMethodManagerService lock.</p>
- *
- * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
- * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
- * process
- * @param inputContext communication channel for the dummy
- * {@link android.view.inputmethod.InputConnection}
- * @param uid UID of the client process
- * @param pid PID of the client process
- */
- public abstract void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
- int pid);
-
- /**
- * Called by the window manager service when a client process is being attached to the window
- * manager service.
- *
- * <p>The caller must not have WindowManagerService lock. This method internally acquires
- * InputMethodManagerService lock.</p>
- *
- * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
- * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
- * process
- */
- public abstract void removeClient(IInputMethodClient client);
-
- /**
* Called by the power manager to tell the input method manager whether it
* should start watching for wake events.
*/
@@ -78,15 +44,6 @@
public static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
@Override
- public void addClient(IInputMethodClient client, IInputContext inputContext,
- int uid, int pid) {
- }
-
- @Override
- public void removeClient(IInputMethodClient client) {
- }
-
- @Override
public void setInteractive(boolean interactive) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 44fb9ea..6f5f90a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -130,10 +130,6 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
-import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
-import com.android.internal.inputmethod.InputMethodUtils;
-import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.HandlerCaller;
@@ -153,6 +149,8 @@
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.wm.WindowManagerInternal;
@@ -403,12 +401,28 @@
}
}
+ private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
+ private final InputMethodManagerService mImms;
+ private final IInputMethodClient mClient;
+
+ ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) {
+ mImms = imms;
+ mClient = client;
+ }
+
+ @Override
+ public void binderDied() {
+ mImms.removeClient(mClient);
+ }
+ }
+
static final class ClientState {
final IInputMethodClient client;
final IInputContext inputContext;
final int uid;
final int pid;
final InputBinding binding;
+ final ClientDeathRecipient clientDeathRecipient;
boolean sessionRequested;
SessionState curSession;
@@ -421,12 +435,13 @@
}
ClientState(IInputMethodClient _client, IInputContext _inputContext,
- int _uid, int _pid) {
+ int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) {
client = _client;
inputContext = _inputContext;
uid = _uid;
pid = _pid;
binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
+ clientDeathRecipient = _clientDeathRecipient;
}
}
@@ -1718,9 +1733,39 @@
}
}
- void addClient(ClientState clientState) {
+ /**
+ * Called by each application process as a preparation to start interacting with
+ * {@link InputMethodManagerService}.
+ *
+ * <p>As a general principle, IPCs from the application process that take
+ * {@link InputMethodClient} will be rejected without this step.</p>
+ *
+ * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
+ * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
+ * process
+ * @param inputContext communication channel for the dummy
+ * {@link android.view.inputmethod.InputConnection}
+ */
+ @Override
+ public void addClient(IInputMethodClient client, IInputContext inputContext) {
+ final int callerUid = Binder.getCallingUid();
+ final int callerPid = Binder.getCallingPid();
synchronized (mMethodMap) {
- mClients.put(clientState.client.asBinder(), clientState);
+ // TODO: Optimize this linear search.
+ for (ClientState state : mClients.values()) {
+ if (state.uid == callerUid && state.pid == callerPid) {
+ throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
+ + " is already registered");
+ }
+ }
+ final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
+ try {
+ client.asBinder().linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ mClients.put(client.asBinder(),
+ new ClientState(client, inputContext, callerUid, callerPid, deathRecipient));
}
}
@@ -1728,6 +1773,7 @@
synchronized (mMethodMap) {
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
+ client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
clearClientSessionLocked(cs);
if (mCurClient == cs) {
if (mBoundToMethod) {
@@ -2538,7 +2584,11 @@
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
- if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+ final ClientState cs = mClients.get(client.asBinder());
+ if (cs == null) {
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
+ }
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
return false;
}
@@ -2618,7 +2668,11 @@
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
- if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+ final ClientState cs = mClients.get(client.asBinder());
+ if (cs == null) {
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
+ }
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
if (DEBUG) {
Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
}
@@ -2736,7 +2790,7 @@
+ client.asBinder());
}
- if (!mWindowManagerInternal.inputMethodClientHasFocus(cs.client)) {
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
// Check with the window manager to make sure this client actually
// has a window with focus. If not, reject. This is thread safe
// because if the focus changes some time before or after, the
@@ -4401,20 +4455,6 @@
}
@Override
- public void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
- int pid) {
- // Work around Bug 113877122: We need to handle this synchronously. Otherwise, some
- // IMM binder calls from the client process before we register this client.
- mService.addClient(new ClientState(client, inputContext, uid, pid));
- }
-
- @Override
- public void removeClient(IInputMethodClient client) {
- // Handle this synchronously to be consistent with addClient().
- mService.removeClient(client);
- }
-
- @Override
public void setInteractive(boolean interactive) {
// Do everything in handler so as not to block the caller.
mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
similarity index 98%
rename from core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
rename to services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 14ac2f6..77b2fee 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
+package com.android.server.inputmethod;
import android.annotation.Nullable;
import android.content.Context;
@@ -27,7 +27,7 @@
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
+import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
import java.util.ArrayList;
import java.util.Collections;
@@ -41,13 +41,11 @@
/**
* InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes.
- * <p>
- * This class is designed to be used from and only from
- * {@link com.android.server.InputMethodManagerService} by using
- * {@link com.android.server.InputMethodManagerService#mMethodMap} as a global lock.
- * </p>
+ *
+ * <p>This class is designed to be used from and only from {@link InputMethodManagerService} by
+ * using {@link InputMethodManagerService#mMethodMap} as a global lock.</p>
*/
-public class InputMethodSubtypeSwitchingController {
+final class InputMethodSubtypeSwitchingController {
private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
similarity index 88%
rename from core/java/com/android/internal/inputmethod/InputMethodUtils.java
rename to services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 1e5b5c8..e951b27 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
+package com.android.server.inputmethod;
import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR;
import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS;
@@ -53,10 +53,14 @@
import java.util.Locale;
/**
- * InputMethodManagerUtils contains some static methods that provides IME informations.
- * This methods are supposed to be used in both the framework and the Settings application.
+ * This class provides random static utility methods for {@link InputMethodManagerService} and its
+ * utility classes.
+ *
+ * <p>This class is intentionally package-private. Utility methods here are tightly coupled with
+ * implementation details in {@link InputMethodManagerService}. Hence this class is not suitable
+ * for other components to directly use.</p>
*/
-public class InputMethodUtils {
+final class InputMethodUtils {
public static final boolean DEBUG = false;
public static final int NOT_A_SUBTYPE_ID = -1;
public static final String SUBTYPE_MODE_ANY = null;
@@ -334,32 +338,6 @@
return getDefaultEnabledImes(context, imis, false /* onlyMinimum */);
}
- public static Locale constructLocaleFromString(String localeStr) {
- if (TextUtils.isEmpty(localeStr)) {
- return null;
- }
- // TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}.
- String[] localeParams = localeStr.split("_", 3);
- if (localeParams.length >= 1 && "tl".equals(localeParams[0])) {
- // Convert a locale whose language is "tl" to one whose language is "fil".
- // For example, "tl_PH" will get converted to "fil_PH".
- // Versions of Android earlier than Lollipop did not support three letter language
- // codes, and used "tl" (Tagalog) as the language string for "fil" (Filipino).
- // On Lollipop and above, the current three letter version must be used.
- localeParams[0] = "fil";
- }
- // The length of localeStr is guaranteed to always return a 1 <= value <= 3
- // because localeStr is not empty.
- if (localeParams.length == 1) {
- return new Locale(localeParams[0]);
- } else if (localeParams.length == 2) {
- return new Locale(localeParams[0], localeParams[1]);
- } else if (localeParams.length == 3) {
- return new Locale(localeParams[0], localeParams[1], localeParams[2]);
- }
- return null;
- }
-
public static boolean containsSubtypeOf(final InputMethodInfo imi,
@Nullable final Locale locale, final boolean checkCountry, final String mode) {
if (locale == null) {
@@ -1320,133 +1298,6 @@
}
}
- // For spell checker service manager.
- // TODO: Should we have TextServicesUtils.java?
- private static final Locale LOCALE_EN_US = new Locale("en", "US");
- private static final Locale LOCALE_EN_GB = new Locale("en", "GB");
-
- /**
- * Returns a list of {@link Locale} in the order of appropriateness for the default spell
- * checker service.
- *
- * <p>If the system language is English, and the region is also explicitly specified in the
- * system locale, the following fallback order will be applied.</p>
- * <ul>
- * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li>
- * <li>(system-locale-language, system-locale-region)</li>
- * <li>("en", "US")</li>
- * <li>("en", "GB")</li>
- * <li>("en")</li>
- * </ul>
- *
- * <p>If the system language is English, but no region is specified in the system locale,
- * the following fallback order will be applied.</p>
- * <ul>
- * <li>("en")</li>
- * <li>("en", "US")</li>
- * <li>("en", "GB")</li>
- * </ul>
- *
- * <p>If the system language is not English, the following fallback order will be applied.</p>
- * <ul>
- * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li>
- * <li>(system-locale-language, system-locale-region) (if exists)</li>
- * <li>(system-locale-language) (if exists)</li>
- * <li>("en", "US")</li>
- * <li>("en", "GB")</li>
- * <li>("en")</li>
- * </ul>
- *
- * @param systemLocale the current system locale to be taken into consideration.
- * @return a list of {@link Locale}. The first one is considered to be most appropriate.
- */
- @VisibleForTesting
- public static ArrayList<Locale> getSuitableLocalesForSpellChecker(
- @Nullable final Locale systemLocale) {
- final Locale systemLocaleLanguageCountryVariant;
- final Locale systemLocaleLanguageCountry;
- final Locale systemLocaleLanguage;
- if (systemLocale != null) {
- final String language = systemLocale.getLanguage();
- final boolean hasLanguage = !TextUtils.isEmpty(language);
- final String country = systemLocale.getCountry();
- final boolean hasCountry = !TextUtils.isEmpty(country);
- final String variant = systemLocale.getVariant();
- final boolean hasVariant = !TextUtils.isEmpty(variant);
- if (hasLanguage && hasCountry && hasVariant) {
- systemLocaleLanguageCountryVariant = new Locale(language, country, variant);
- } else {
- systemLocaleLanguageCountryVariant = null;
- }
- if (hasLanguage && hasCountry) {
- systemLocaleLanguageCountry = new Locale(language, country);
- } else {
- systemLocaleLanguageCountry = null;
- }
- if (hasLanguage) {
- systemLocaleLanguage = new Locale(language);
- } else {
- systemLocaleLanguage = null;
- }
- } else {
- systemLocaleLanguageCountryVariant = null;
- systemLocaleLanguageCountry = null;
- systemLocaleLanguage = null;
- }
-
- final ArrayList<Locale> locales = new ArrayList<>();
- if (systemLocaleLanguageCountryVariant != null) {
- locales.add(systemLocaleLanguageCountryVariant);
- }
-
- if (Locale.ENGLISH.equals(systemLocaleLanguage)) {
- if (systemLocaleLanguageCountry != null) {
- // If the system language is English, and the region is also explicitly specified,
- // following fallback order will be applied.
- // - systemLocaleLanguageCountry [if systemLocaleLanguageCountry is non-null]
- // - en_US [if systemLocaleLanguageCountry is non-null and not en_US]
- // - en_GB [if systemLocaleLanguageCountry is non-null and not en_GB]
- // - en
- if (systemLocaleLanguageCountry != null) {
- locales.add(systemLocaleLanguageCountry);
- }
- if (!LOCALE_EN_US.equals(systemLocaleLanguageCountry)) {
- locales.add(LOCALE_EN_US);
- }
- if (!LOCALE_EN_GB.equals(systemLocaleLanguageCountry)) {
- locales.add(LOCALE_EN_GB);
- }
- locales.add(Locale.ENGLISH);
- } else {
- // If the system language is English, but no region is specified, following
- // fallback order will be applied.
- // - en
- // - en_US
- // - en_GB
- locales.add(Locale.ENGLISH);
- locales.add(LOCALE_EN_US);
- locales.add(LOCALE_EN_GB);
- }
- } else {
- // If the system language is not English, the fallback order will be
- // - systemLocaleLanguageCountry [if non-null]
- // - systemLocaleLanguage [if non-null]
- // - en_US
- // - en_GB
- // - en
- if (systemLocaleLanguageCountry != null) {
- locales.add(systemLocaleLanguageCountry);
- }
- if (systemLocaleLanguage != null) {
- locales.add(systemLocaleLanguage);
- }
- locales.add(LOCALE_EN_US);
- locales.add(LOCALE_EN_GB);
- locales.add(Locale.ENGLISH);
- }
- return locales;
- }
-
public static boolean isSoftInputModeStateVisibleAllowed(
int targetSdkVersion, int controlFlags) {
if (targetSdkVersion < Build.VERSION_CODES.P) {
diff --git a/core/java/com/android/internal/inputmethod/LocaleUtils.java b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
similarity index 97%
rename from core/java/com/android/internal/inputmethod/LocaleUtils.java
rename to services/core/java/com/android/server/inputmethod/LocaleUtils.java
index eeb3854..4958ece 100644
--- a/core/java/com/android/internal/inputmethod/LocaleUtils.java
+++ b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
-
-import com.android.internal.annotations.VisibleForTesting;
+package com.android.server.inputmethod;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -31,9 +29,7 @@
import java.util.List;
import java.util.Locale;
-public final class LocaleUtils {
-
- @VisibleForTesting
+final class LocaleUtils {
public interface LocaleExtractor<T> {
@Nullable
Locale get(@Nullable T source);
@@ -149,7 +145,6 @@
* @param dest Destination into which the filtered items will be added.
* @param <T> Type of the data items.
*/
- @VisibleForTesting
public static <T> void filterByLanguage(
@NonNull List<T> sources,
@NonNull LocaleExtractor<T> extractor,
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 7751f5f..d326c22 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -20,6 +20,7 @@
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.os.UserHandle.USER_ALL;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -53,12 +54,15 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -72,6 +76,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@@ -138,10 +143,6 @@
// not mean that we are currently bound to said package/component.
private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
- // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
- // user change).
- private int[] mLastSeenProfileIds;
-
// True if approved services are stored in xml, not settings.
private boolean mUseXml;
@@ -178,6 +179,10 @@
}
}
+ protected int getBindFlags() {
+ return BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT;
+ }
+
protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
private ManagedServiceInfo newServiceInfo(IInterface service,
@@ -296,7 +301,7 @@
Settings.Secure.putStringForUser(
mContext.getContentResolver(), element, value, userId);
loadAllowedComponentsFromSettings();
- rebindServices(false);
+ rebindServices(false, userId);
}
}
}
@@ -381,7 +386,7 @@
}
}
}
- rebindServices(false);
+ rebindServices(false, USER_ALL);
}
protected void upgradeXml(final int xmlVersion, final int userId) {}
@@ -456,7 +461,7 @@
}
}
- rebindServices(false);
+ rebindServices(false, userId);
}
private String getApprovedValue(String pkgOrComponent) {
@@ -574,7 +579,7 @@
if (anyServicesInvolved) {
// make sure we're still bound to any of our services who may have just upgraded
- rebindServices(false);
+ rebindServices(false, USER_ALL);
}
}
}
@@ -582,21 +587,17 @@
public void onUserRemoved(int user) {
Slog.i(TAG, "Removing approved services for removed user " + user);
mApproved.remove(user);
- rebindServices(true);
+ rebindServices(true, user);
}
public void onUserSwitched(int user) {
if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
- if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
- if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
- return;
- }
- rebindServices(true);
+ rebindServices(true, user);
}
public void onUserUnlocked(int user) {
if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
- rebindServices(false);
+ rebindServices(false, user);
}
private ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
@@ -690,9 +691,10 @@
component.flattenToShortString());
synchronized (mMutex) {
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
+ final IntArray userIds = mUserProfiles.getCurrentProfileIds();
- for (int userId : userIds) {
+ for (int i = 0; i < userIds.size(); i++) {
+ final int userId = userIds.get(i);
if (enabled) {
if (isPackageOrComponentAllowed(component.toString(), userId)
|| isPackageOrComponentAllowed(component.getPackageName(), userId)) {
@@ -834,20 +836,14 @@
return false;
}
- /**
- * Called whenever packages change, the user switches, or the secure setting
- * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
- */
- protected void rebindServices(boolean forceRebind) {
- if (DEBUG) Slog.d(TAG, "rebindServices");
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
- final int nUserIds = userIds.length;
-
+ @VisibleForTesting
+ protected SparseArray<ArraySet<ComponentName>> getAllowedComponents(IntArray userIds) {
+ final int nUserIds = userIds.size();
final SparseArray<ArraySet<ComponentName>> componentsByUser = new SparseArray<>();
for (int i = 0; i < nUserIds; ++i) {
- final int userId = userIds[i];
- final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userIds[i]);
+ final int userId = userIds.get(i);
+ final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId);
if (approvedLists != null) {
final int N = approvedLists.size();
for (int j = 0; j < N; j++) {
@@ -861,67 +857,125 @@
}
}
}
+ return componentsByUser;
+ }
- final ArrayList<ManagedServiceInfo> removableBoundServices = new ArrayList<>();
- final SparseArray<Set<ComponentName>> toAdd = new SparseArray<>();
-
- synchronized (mMutex) {
- // Rebind to non-system services if user switched
- for (ManagedServiceInfo service : mServices) {
- if (!service.isSystem && !service.isGuest(this)) {
- removableBoundServices.add(service);
- }
- }
-
- mEnabledServicesForCurrentProfiles.clear();
- mEnabledServicesPackageNames.clear();
-
- for (int i = 0; i < nUserIds; ++i) {
- // decode the list of components
- final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
- if (null == userComponents) {
- toAdd.put(userIds[i], new ArraySet<>());
- continue;
- }
-
- final Set<ComponentName> add = new HashSet<>(userComponents);
- add.removeAll(mSnoozingForCurrentProfiles);
-
- toAdd.put(userIds[i], add);
-
- mEnabledServicesForCurrentProfiles.addAll(userComponents);
-
- for (int j = 0; j < userComponents.size(); j++) {
- final ComponentName component = userComponents.valueAt(j);
- mEnabledServicesPackageNames.add(component.getPackageName());
- }
- }
- }
-
- for (ManagedServiceInfo info : removableBoundServices) {
- final ComponentName component = info.component;
- final int oldUser = info.userid;
- final Set<ComponentName> allowedComponents = toAdd.get(info.userid);
- if (allowedComponents != null) {
- if (allowedComponents.contains(component) && !forceRebind) {
- // Already bound, don't need to bind again.
- allowedComponents.remove(component);
- } else {
- // No longer allowed to be bound, or must rebind.
- Slog.v(TAG, "disabling " + getCaption() + " for user "
- + oldUser + ": " + component);
- unregisterService(component, oldUser);
- }
- }
- }
+ @GuardedBy("mMutex")
+ protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
+ final IntArray activeUsers,
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
+ mEnabledServicesForCurrentProfiles.clear();
+ mEnabledServicesPackageNames.clear();
+ final int nUserIds = activeUsers.size();
for (int i = 0; i < nUserIds; ++i) {
- final Set<ComponentName> add = toAdd.get(userIds[i]);
+ // decode the list of components
+ final int userId = activeUsers.get(i);
+ final ArraySet<ComponentName> userComponents = approvedComponentsByUser.get(userId);
+ if (null == userComponents) {
+ componentsToBind.put(userId, new ArraySet<>());
+ continue;
+ }
+
+ final Set<ComponentName> add = new HashSet<>(userComponents);
+ add.removeAll(mSnoozingForCurrentProfiles);
+
+ componentsToBind.put(userId, add);
+
+ mEnabledServicesForCurrentProfiles.addAll(userComponents);
+
+ for (int j = 0; j < userComponents.size(); j++) {
+ final ComponentName component = userComponents.valueAt(j);
+ mEnabledServicesPackageNames.add(component.getPackageName());
+ }
+ }
+ }
+
+ @GuardedBy("mMutex")
+ protected Set<ManagedServiceInfo> getRemovableConnectedServices() {
+ final Set<ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ for (ManagedServiceInfo service : mServices) {
+ if (!service.isSystem && !service.isGuest(this)) {
+ removableBoundServices.add(service);
+ }
+ }
+ return removableBoundServices;
+ }
+
+ protected void populateComponentsToUnbind(
+ boolean forceRebind,
+ Set<ManagedServiceInfo> removableBoundServices,
+ SparseArray<Set<ComponentName>> allowedComponentsToBind,
+ SparseArray<Set<ComponentName>> componentsToUnbind) {
+ for (ManagedServiceInfo info : removableBoundServices) {
+ final Set<ComponentName> allowedComponents = allowedComponentsToBind.get(info.userid);
+ if (allowedComponents != null) {
+ if (forceRebind || !allowedComponents.contains(info.component)) {
+ Set<ComponentName> toUnbind =
+ componentsToUnbind.get(info.userid, new ArraySet<>());
+ toUnbind.add(info.component);
+ componentsToUnbind.put(info.userid, toUnbind);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called whenever packages change, the user switches, or the secure setting
+ * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+ */
+ protected void rebindServices(boolean forceRebind, int userToRebind) {
+ if (DEBUG) Slog.d(TAG, "rebindServices " + forceRebind + " " + userToRebind);
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
+ if (userToRebind != USER_ALL) {
+ userIds = new IntArray(1);
+ userIds.add(userToRebind);
+ }
+
+ final SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+ final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ synchronized (mMutex) {
+ final SparseArray<ArraySet<ComponentName>> approvedComponentsByUser =
+ getAllowedComponents(userIds);
+ final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+
+ // Filter approvedComponentsByUser to collect all of the components that are allowed
+ // for the currently active user(s).
+ populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
+
+ // For every current non-system connection, disconnect services that are no longer
+ // approved, or ALL services if we are force rebinding
+ populateComponentsToUnbind(
+ forceRebind, removableBoundServices, componentsToBind, componentsToUnbind);
+ }
+
+ unbindFromServices(componentsToUnbind);
+ bindToServices(componentsToBind);
+ }
+
+ protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
+ for (int i = 0; i < componentsToUnbind.size(); i++) {
+ final int userId = componentsToUnbind.keyAt(i);
+ final Set<ComponentName> removableComponents = componentsToUnbind.get(userId);
+ for (ComponentName cn : removableComponents) {
+ // No longer allowed to be bound, or must rebind.
+ Slog.v(TAG, "disabling " + getCaption() + " for user " + userId + ": " + cn);
+ unregisterService(cn, userId);
+ }
+ }
+ }
+
+ // Attempt to bind to services, skipping those that cannot be found or lack the permission.
+ private void bindToServices(SparseArray<Set<ComponentName>> componentsToBind) {
+ for (int i = 0; i < componentsToBind.size(); i++) {
+ final int userId = componentsToBind.keyAt(i);
+ final Set<ComponentName> add = componentsToBind.get(userId);
for (ComponentName component : add) {
try {
ServiceInfo info = mPm.getServiceInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
if (info == null) {
Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ ": service not found");
@@ -933,15 +987,13 @@
continue;
}
Slog.v(TAG,
- "enabling " + getCaption() + " for " + userIds[i] + ": " + component);
- registerService(component, userIds[i]);
+ "enabling " + getCaption() + " for " + userId + ": " + component);
+ registerService(component, userId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
-
- mLastSeenProfileIds = userIds;
}
/**
@@ -1018,7 +1070,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
- Slog.v(TAG, getCaption() + " service connected: " + name);
+ Slog.v(TAG, userid + " " + getCaption() + " service connected: " + name);
boolean added = false;
ManagedServiceInfo info = null;
synchronized (mMutex) {
@@ -1040,12 +1092,12 @@
@Override
public void onServiceDisconnected(ComponentName name) {
- Slog.v(TAG, getCaption() + " connection lost: " + name);
+ Slog.v(TAG, userid + " " + getCaption() + " connection lost: " + name);
}
@Override
public void onBindingDied(ComponentName name) {
- Slog.w(TAG, getCaption() + " binding died: " + name);
+ Slog.w(TAG, userid + " " + getCaption() + " binding died: " + name);
synchronized (mMutex) {
unbindService(this, name, userid);
if (!mServicesRebinding.contains(servicesBindingTag)) {
@@ -1057,18 +1109,19 @@
}
}, ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
- Slog.v(TAG, getCaption() + " not rebinding as "
- + "a previous rebind attempt was made: " + name);
+ Slog.v(TAG, getCaption() + " not rebinding in user " + userid
+ + " as a previous rebind attempt was made: " + name);
}
}
}
};
if (!mContext.bindServiceAsUser(intent,
- serviceConnection,
- BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT,
- new UserHandle(userid))) {
+ serviceConnection,
+ getBindFlags(),
+ new UserHandle(userid))) {
mServicesBound.remove(servicesBindingTag);
- Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
+ Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent
+ + " in user " + userid);
return;
}
} catch (SecurityException ex) {
@@ -1232,9 +1285,9 @@
if (!isEnabledForCurrentProfiles()) {
return false;
}
- if (this.userid == UserHandle.USER_ALL) return true;
+ if (this.userid == USER_ALL) return true;
if (this.isSystem) return true;
- if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
+ if (nid == USER_ALL || nid == this.userid) return true;
return supportsProfiles()
&& mUserProfiles.isCurrentProfile(nid)
&& isPermittedForProfile(nid);
@@ -1280,6 +1333,24 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ManagedServiceInfo that = (ManagedServiceInfo) o;
+ return userid == that.userid
+ && isSystem == that.isSystem
+ && targetSdkVersion == that.targetSdkVersion
+ && Objects.equals(service, that.service)
+ && Objects.equals(component, that.component)
+ && Objects.equals(connection, that.connection);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(service, component, userid, isSystem, connection, targetSdkVersion);
+ }
}
/** convenience method for looking in mEnabledServicesForCurrentProfiles */
@@ -1305,12 +1376,15 @@
}
}
- public int[] getCurrentProfileIds() {
+ /**
+ * Returns the currently active users (generally one user and its work profile).
+ */
+ public IntArray getCurrentProfileIds() {
synchronized (mCurrentProfiles) {
- int[] users = new int[mCurrentProfiles.size()];
+ IntArray users = new IntArray(mCurrentProfiles.size());
final int N = mCurrentProfiles.size();
for (int i = 0; i < N; ++i) {
- users[i] = mCurrentProfiles.keyAt(i);
+ users.add(mCurrentProfiles.keyAt(i));
}
return users;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6e4d9e0..e53eeb0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -33,6 +33,10 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.content.Context.BIND_ADJUST_BELOW_PERCEPTIBLE;
+import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -174,6 +178,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -1562,7 +1567,7 @@
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- getContext().registerReceiver(mIntentReceiver, filter);
+ getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -1694,10 +1699,10 @@
UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
null);
if (isUidSystemOrPhone(uid)) {
- int[] profileIds = mUserProfiles.getCurrentProfileIds();
- int N = profileIds.length;
+ IntArray profileIds = mUserProfiles.getCurrentProfileIds();
+ int N = profileIds.size();
for (int i = 0; i < N; i++) {
- int profileId = profileIds[i];
+ int profileId = profileIds.get(i);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
profileId, REASON_CHANNEL_BANNED,
null);
@@ -4084,25 +4089,30 @@
public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
int userId) {
checkCallerIsSystem();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mNotificationLock) {
- removeForegroundServiceFlagByListLocked(
- mEnqueuedNotifications, pkg, notificationId, userId);
- removeForegroundServiceFlagByListLocked(
- mNotificationList, pkg, notificationId, userId);
+ mHandler.post(() -> {
+ synchronized (mNotificationLock) {
+ // strip flag from all enqueued notifications. listeners will be informed
+ // in post runnable.
+ List<NotificationRecord> enqueued = findNotificationsByListLocked(
+ mEnqueuedNotifications, pkg, null, notificationId, userId);
+ for (int i = 0; i < enqueued.size(); i++) {
+ removeForegroundServiceFlagLocked(enqueued.get(i));
+ }
+
+ // if posted notification exists, strip its flag and tell listeners
+ NotificationRecord r = findNotificationByListLocked(
+ mNotificationList, pkg, null, notificationId, userId);
+ if (r != null) {
+ removeForegroundServiceFlagLocked(r);
+ mRankingHelper.sort(mNotificationList);
+ mListeners.notifyPostedLocked(r, r);
}
}
});
}
@GuardedBy("mNotificationLock")
- private void removeForegroundServiceFlagByListLocked(
- ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
- int userId) {
- NotificationRecord r = findNotificationByListLocked(
- notificationList, pkg, null, notificationId, userId);
+ private void removeForegroundServiceFlagLocked(NotificationRecord r) {
if (r == null) {
return;
}
@@ -4113,8 +4123,6 @@
// initially *and* force remove FLAG_FOREGROUND_SERVICE.
sbn.getNotification().flags =
(r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
- mRankingHelper.sort(mNotificationList);
- mListeners.notifyPostedLocked(r, r);
}
};
@@ -4395,22 +4403,12 @@
}
protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
- final String pkg = r.sbn.getPackageName();
- final int callingUid = r.sbn.getUid();
-
- final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
- if (isPackageSuspended) {
- Slog.e(TAG, "Suppressing notification from package due to package "
- + "suspended by administrator.");
- usageStats.registerSuspendedByAdmin(r);
- return isPackageSuspended;
- }
- final boolean isBlocked = isBlocked(r);
- if (isBlocked) {
+ if (isBlocked(r)) {
Slog.e(TAG, "Suppressing notification from package by user request.");
usageStats.registerBlocked(r);
+ return true;
}
- return isBlocked;
+ return false;
}
private boolean isBlocked(NotificationRecord r) {
@@ -4684,7 +4682,11 @@
return;
}
- r.setHidden(isPackageSuspendedLocked(r));
+ final boolean isPackageSuspended = isPackageSuspendedLocked(r);
+ r.setHidden(isPackageSuspended);
+ if (isPackageSuspended) {
+ mUsageStats.registerSuspendedByAdmin(r);
+ }
NotificationRecord old = mNotificationsByKey.get(key);
final StatusBarNotification n = r.sbn;
final Notification notification = n.getNotification();
@@ -6267,6 +6269,21 @@
}
@GuardedBy("mNotificationLock")
+ private List<NotificationRecord> findNotificationsByListLocked(
+ ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) {
+ List<NotificationRecord> matching = new ArrayList<>();
+ final int len = list.size();
+ for (int i = 0; i < len; i++) {
+ NotificationRecord r = list.get(i);
+ if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
+ TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
+ matching.add(r);
+ }
+ }
+ return matching;
+ }
+
+ @GuardedBy("mNotificationLock")
private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
String key) {
final int N = list.size();
@@ -6675,7 +6692,9 @@
@Override
public void onUserUnlocked(int user) {
if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
- rebindServices(true);
+ // force rebind the assistant, as it might be keeping its own state in user locked
+ // storage
+ rebindServices(true, user);
}
protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
@@ -6800,6 +6819,16 @@
}
@Override
+ protected int getBindFlags() {
+ // Most of the same flags as the base, but also add BIND_ADJUST_BELOW_PERCEPTIBLE
+ // because too many 3P apps could be kept in memory as notification listeners and
+ // cause extreme memory pressure.
+ // TODO: Change the binding lifecycle of NotificationListeners to avoid this situation.
+ return BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE
+ | BIND_ADJUST_BELOW_PERCEPTIBLE | BIND_ALLOW_WHITELIST_MANAGEMENT;
+ }
+
+ @Override
protected Config getConfig() {
Config c = new Config();
c.caption = "notification listener";
@@ -6890,7 +6919,6 @@
if (!oldSbnVisible && !sbnVisible) {
continue;
}
-
// If the notification is hidden, don't notifyPosted listeners targeting < P.
// Instead, those listeners will receive notifyPosted when the notification is
// unhidden.
@@ -7324,7 +7352,7 @@
new String[]{pkg});
final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED;
+ : Intent.ACTION_PACKAGES_UNSUSPENDED;
final Intent intent = new Intent(action);
intent.putExtras(extras);
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index a178a52..2b581d6 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -15,17 +15,8 @@
*/
package com.android.server.notification;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.NonNull;
import android.app.AlarmManager;
-import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -37,9 +28,18 @@
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -105,12 +105,12 @@
protected @NonNull List<NotificationRecord> getSnoozed() {
List<NotificationRecord> snoozedForUser = new ArrayList<>();
- int[] userIds = mUserProfiles.getCurrentProfileIds();
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
if (userIds != null) {
- final int N = userIds.length;
+ final int N = userIds.size();
for (int i = 0; i < N; i++) {
final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
- mSnoozedNotifications.get(userIds[i]);
+ mSnoozedNotifications.get(userIds.get(i));
if (snoozedPkgs != null) {
final int M = snoozedPkgs.size();
for (int j = 0; j < M; j++) {
@@ -179,7 +179,7 @@
protected boolean cancel(int userId, boolean includeCurrentProfiles) {
int[] userIds = {userId};
if (includeCurrentProfiles) {
- userIds = mUserProfiles.getCurrentProfileIds();
+ userIds = mUserProfiles.getCurrentProfileIds().toArray();
}
final int N = userIds.length;
for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4dd2bf2..25f52e7 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -52,8 +52,6 @@
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.EventInfo;
-import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeProto;
import android.util.AndroidRuntimeException;
@@ -119,8 +117,6 @@
public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS
| SUPPRESSED_EFFECT_NOTIFICATIONS;
- protected String mDefaultRuleEveryNightName;
- protected String mDefaultRuleEventsName;
@VisibleForTesting protected boolean mIsBootComplete;
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
@@ -130,9 +126,9 @@
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mNotificationManager = context.getSystemService(NotificationManager.class);
- mDefaultConfig = new ZenModeConfig();
- setDefaultZenRules(mContext);
- mConfig = mDefaultConfig;
+ mDefaultConfig = readDefaultConfig(mContext.getResources());
+ updateDefaultAutomaticRuleNames();
+ mConfig = mDefaultConfig.copy();
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
mSettingsObserver = new SettingsObserver(mHandler);
@@ -311,7 +307,9 @@
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
- newConfig.automaticRules.put(rule.id, rule);
+ if (newConfig.automaticRules.put(rule.id, rule) != null) {
+ rule.modified = true;
+ }
if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
} else {
@@ -341,7 +339,9 @@
}
}
populateZenRule(automaticZenRule, rule, false);
- newConfig.automaticRules.put(ruleId, rule);
+ if (newConfig.automaticRules.put(ruleId, rule) != null) {
+ rule.modified = true;
+ }
return setConfigLocked(newConfig, reason, rule.component, true);
}
}
@@ -413,17 +413,6 @@
}
}
- public void setDefaultZenRules(Context context) {
- mDefaultConfig = readDefaultConfig(context.getResources());
- appendDefaultRules(mDefaultConfig);
- }
-
- private void appendDefaultRules (ZenModeConfig config) {
- getDefaultRuleNames();
- appendDefaultEveryNightRule(config);
- appendDefaultEventRules(config);
- }
-
// Checks zen rule properties are the same (doesn't check creation time, name nor enabled)
// used to check if default rules were customized or not
private boolean ruleValuesEqual(AutomaticZenRule rule, ZenRule defaultRule) {
@@ -437,22 +426,16 @@
}
protected void updateDefaultZenRules() {
- ZenModeConfig configDefaultRules = new ZenModeConfig();
- appendDefaultRules(configDefaultRules); // "new" localized default rules
- for (String ruleId : ZenModeConfig.DEFAULT_RULE_IDS) {
- AutomaticZenRule currRule = getAutomaticZenRule(ruleId);
- ZenRule defaultRule = configDefaultRules.automaticRules.get(ruleId);
- // if default rule wasn't customized, use localized name instead of previous
- if (ruleValuesEqual(currRule, defaultRule) &&
- !defaultRule.name.equals(currRule.getName())) {
+ updateDefaultAutomaticRuleNames();
+ for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+ ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+ // if default rule wasn't modified, use localized name instead of previous
+ if (!currRule.modified && !defaultRule.name.equals(currRule.name)) {
if (canManageAutomaticZenRule(defaultRule)) {
if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
- + "from " + currRule.getName() + " to " + defaultRule.name);
+ + "from " + currRule.name + " to " + defaultRule.name);
// update default rule (if locale changed, name of rule will change)
- AutomaticZenRule defaultAutoRule = createAutomaticZenRule(defaultRule);
- // ensure enabled state is carried over from current rule
- defaultAutoRule.setEnabled(currRule.isEnabled());
- updateAutomaticZenRule(ruleId, defaultAutoRule,
+ updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(defaultRule),
"locale changed");
}
}
@@ -642,7 +625,9 @@
// - doesn't already have default rules and
// - all previous automatic rules were disabled
config.automaticRules = new ArrayMap<>();
- appendDefaultRules(config);
+ for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ config.automaticRules.put(rule.id, rule);
+ }
reason += ", reset to default rules";
}
@@ -854,12 +839,16 @@
}
}
- private void getDefaultRuleNames() {
- // on locale-change, these values differ
- mDefaultRuleEveryNightName = mContext.getResources()
- .getString(R.string.zen_mode_default_every_night_name);
- mDefaultRuleEventsName = mContext.getResources()
- .getString(R.string.zen_mode_default_events_name);
+ private void updateDefaultAutomaticRuleNames() {
+ for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) {
+ rule.name = mContext.getResources()
+ .getString(R.string.zen_mode_default_events_name);
+ } else if (ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID.equals(rule.id)) {
+ rule.name = mContext.getResources()
+ .getString(R.string.zen_mode_default_every_night_name);
+ }
+ }
}
@VisibleForTesting
@@ -1001,42 +990,6 @@
return new ZenModeConfig();
}
- private void appendDefaultEveryNightRule(ZenModeConfig config) {
- if (config == null) return;
-
- final ScheduleInfo weeknights = new ScheduleInfo();
- weeknights.days = ZenModeConfig.ALL_DAYS;
- weeknights.startHour = 22;
- weeknights.endHour = 7;
- weeknights.exitAtAlarm = true;
- final ZenRule rule = new ZenRule();
- rule.enabled = false;
- rule.name = mDefaultRuleEveryNightName;
- rule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
- rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- rule.component = ScheduleConditionProvider.COMPONENT;
- rule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
- rule.creationTime = System.currentTimeMillis();
- config.automaticRules.put(rule.id, rule);
- }
-
- private void appendDefaultEventRules(ZenModeConfig config) {
- if (config == null) return;
-
- final EventInfo events = new EventInfo();
- events.calendar = null; // any calendar
- events.reply = EventInfo.REPLY_YES_OR_MAYBE;
- final ZenRule rule = new ZenRule();
- rule.enabled = false;
- rule.name = mDefaultRuleEventsName;
- rule.conditionId = ZenModeConfig.toEventConditionId(events);
- rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- rule.component = EventConditionProvider.COMPONENT;
- rule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
- rule.creationTime = System.currentTimeMillis();
- config.automaticRules.put(rule.id, rule);
- }
-
private static int zenSeverity(int zen) {
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return 1;
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 7d762d9..3b11525 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -378,7 +378,7 @@
for (int i = newIntents.size() - 1; i >= 0; --i) {
final PackageParser.ActivityIntentInfo intentInfo = newIntents.get(i);
final PackageParser.Package disabledPkg = sPackageManagerInternal
- .getDisabledPackage(intentInfo.activity.info.packageName);
+ .getDisabledSystemPackage(intentInfo.activity.info.packageName);
final List<PackageParser.Activity> systemActivities =
disabledPkg != null ? disabledPkg.activities : null;
adjustPriority(systemActivities, intentInfo, setupWizardPackage);
@@ -1048,11 +1048,14 @@
final String otherPackageName =
(other != null && other.getComponentName() != null)
? other.getComponentName().getPackageName() : "?";
- throw new PackageManagerException(
- INSTALL_FAILED_CONFLICTING_PROVIDER,
- "Can't install because provider name " + names[j]
- + " (in package " + pkg.applicationInfo.packageName
- + ") is already used by " + otherPackageName);
+ // if we're installing over the same already-installed package, this is ok
+ if (!otherPackageName.equals(pkg.packageName)) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_CONFLICTING_PROVIDER,
+ "Can't install because provider name " + names[j]
+ + " (in package " + pkg.applicationInfo.packageName
+ + ") is already used by " + otherPackageName);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index bca3ca7..5810e30 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -53,16 +53,13 @@
private final static String TAG = "OTADexopt";
private final static boolean DEBUG_DEXOPT = true;
- // The synthetic library dependencies denoting "no checks."
- private final static String[] NO_LIBRARIES =
- new String[] { PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK };
-
// The amount of "available" (free - low threshold) space necessary at the start of an OTA to
// not bulk-delete unused apps' odex files.
private final static long BULK_DELETE_THRESHOLD = 1024 * 1024 * 1024; // 1GB.
private final Context mContext;
private final PackageManagerService mPackageManagerService;
+ private final MetricsLogger metricsLogger;
// TODO: Evaluate the need for WeakReferences here.
@@ -95,6 +92,7 @@
public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
this.mContext = context;
this.mPackageManagerService = packageManagerService;
+ metricsLogger = new MetricsLogger();
}
public static OtaDexoptService main(Context context,
@@ -286,8 +284,8 @@
throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The current version.
- builder.append("9 ");
+ // The current version. For v10, see b/115993344.
+ builder.append("10 ");
builder.append("dexopt");
@@ -336,11 +334,6 @@
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
String[] libraryDependencies = pkg.usesLibraryFiles;
- if (pkg.isSystem()) {
- // For system apps, we want to avoid classpaths checks.
- libraryDependencies = NO_LIBRARIES;
- }
-
optimizer.performDexOpt(pkg, libraryDependencies,
null /* ISAs */,
@@ -445,24 +438,22 @@
private void performMetricsLogging() {
long finalTime = System.nanoTime();
- MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb",
+ metricsLogger.histogram("ota_dexopt_available_space_before_mb",
inMegabytes(availableSpaceBefore));
- MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb",
+ metricsLogger.histogram("ota_dexopt_available_space_after_bulk_delete_mb",
inMegabytes(availableSpaceAfterBulkDelete));
- MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb",
+ metricsLogger.histogram("ota_dexopt_available_space_after_dexopt_mb",
inMegabytes(availableSpaceAfterDexopt));
- MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages",
- importantPackageCount);
- MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount);
+ metricsLogger.histogram("ota_dexopt_num_important_packages", importantPackageCount);
+ metricsLogger.histogram("ota_dexopt_num_other_packages", otherPackageCount);
- MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal);
- MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed",
- dexoptCommandCountExecuted);
+ metricsLogger.histogram("ota_dexopt_num_commands", dexoptCommandCountTotal);
+ metricsLogger.histogram("ota_dexopt_num_commands_executed", dexoptCommandCountExecuted);
final int elapsedTimeSeconds =
(int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart);
- MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds);
+ metricsLogger.histogram("ota_dexopt_time_s", elapsedTimeSeconds);
}
private static class OTADexoptPackageDexOptimizer extends
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index adf95dc..91af0ec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,8 @@
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.server.pm.permission.PermissionsState
+ .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import android.Manifest;
import android.annotation.IntDef;
@@ -303,7 +304,8 @@
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy
+ .DefaultPermissionGrantedCallback;
import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionManagerService;
@@ -716,6 +718,8 @@
@GuardedBy("mPackages")
final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
+ private PackageManager mPackageManager;
+
class PackageParserCallback implements PackageParser.Callback {
@Override public final boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
@@ -8582,7 +8586,10 @@
null /* originalPkgSetting */, null, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
- scanPackageOnlyLI(request, mFactoryTest, -1L);
+ final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L);
+ if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
+ scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
+ }
}
}
}
@@ -9805,6 +9812,11 @@
/** Whether or not the package scan was successful */
public final boolean success;
/**
+ * Whether or not the original PackageSetting needs to be updated with this result on
+ * commit.
+ */
+ public final boolean existingSettingCopied;
+ /**
* The final package settings. This may be the same object passed in
* the {@link ScanRequest}, but, with modified values.
*/
@@ -9814,11 +9826,12 @@
public ScanResult(
ScanRequest request, boolean success,
@Nullable PackageSetting pkgSetting,
- @Nullable List<String> changedAbiCodePath) {
+ @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied) {
this.request = request;
this.success = success;
this.pkgSetting = pkgSetting;
this.changedAbiCodePath = changedAbiCodePath;
+ this.existingSettingCopied = existingSettingCopied;
}
}
@@ -9895,26 +9908,34 @@
private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
PackageParser.Package pkg) {
- if (disabledPkgSetting != null) {
+
+ // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+ // the correct isSystem value now that we don't disable system packages before scan.
+ final PackageSetting systemPkgSetting =
+ (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+ && pkgSetting != null && pkgSetting.isSystem()
+ ? pkgSetting
+ : disabledPkgSetting;
+ if (systemPkgSetting != null) {
// updated system application, must at least have SCAN_AS_SYSTEM
scanFlags |= SCAN_AS_SYSTEM;
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
scanFlags |= SCAN_AS_OEM;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
scanFlags |= SCAN_AS_VENDOR;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
scanFlags |= SCAN_AS_PRODUCT;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0) {
scanFlags |= SCAN_AS_PRODUCT_SERVICES;
}
@@ -10044,11 +10065,18 @@
final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
final UserHandle user = request.user;
final String realPkgName = request.realPkgName;
- final PackageSetting pkgSetting = result.pkgSetting;
final List<String> changedAbiCodePath = result.changedAbiCodePath;
- final boolean newPkgSettingCreated = (result.pkgSetting != request.pkgSetting);
-
- if (newPkgSettingCreated) {
+ final PackageSetting pkgSetting;
+ if (result.existingSettingCopied) {
+ pkgSetting = request.pkgSetting;
+ pkgSetting.updateFrom(result.pkgSetting);
+ pkg.mExtras = pkgSetting;
+ if (pkgSetting.sharedUser != null
+ && pkgSetting.sharedUser.removePackage(result.pkgSetting)) {
+ pkgSetting.sharedUser.addPackage(pkgSetting);
+ }
+ } else {
+ pkgSetting = result.pkgSetting;
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
}
@@ -10355,7 +10383,6 @@
String primaryCpuAbiFromSettings = null;
String secondaryCpuAbiFromSettings = null;
boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
-
if (!needToDeriveAbi) {
if (pkgSetting != null) {
primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
@@ -10399,6 +10426,11 @@
UserManagerService.getInstance(), usesStaticLibraries,
pkg.usesStaticLibrariesVersions);
} else {
+ if (!createNewPackage) {
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.pkg = pkg;
+ }
// REMOVE SharedUserSetting from method; update in a separate call.
//
// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
@@ -10431,8 +10463,10 @@
final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
setInstantAppForUser(pkgSetting, userId, instantApp, fullApp);
}
-
- if (disabledPkgSetting != null) {
+ // TODO(patb): see if we can do away with disabled check here.
+ if (disabledPkgSetting != null
+ || (0 != (scanFlags & SCAN_NEW_INSTALL)
+ && pkgSetting != null && pkgSetting.isSystem())) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
@@ -10615,7 +10649,8 @@
pkgSetting.volumeUuid = volumeUuid;
}
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath);
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */);
}
/**
@@ -10830,7 +10865,7 @@
}
// A package name must be unique; don't allow duplicates
- if (mPackages.containsKey(pkg.packageName)) {
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0 && mPackages.containsKey(pkg.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Application package " + pkg.packageName
+ " already installed. Skipping duplicate.");
@@ -10839,7 +10874,8 @@
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// but we still want the base name to be unique.
- if (mPackages.containsKey(pkg.manifestPackageName)) {
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPackages.containsKey(pkg.manifestPackageName)) {
throw new PackageManagerException(
"Duplicate static shared lib provider package");
}
@@ -11928,7 +11964,9 @@
// Remove the parent package setting
PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps != null) {
- removePackageLI(ps, chatty);
+ removePackageLI(ps.name, chatty);
+ } else if (DEBUG_REMOVE && chatty) {
+ Log.d(TAG, "Not removing package " + pkg.packageName + "; mExtras == null");
}
// Remove the child package setting
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
@@ -11936,23 +11974,22 @@
PackageParser.Package childPkg = pkg.childPackages.get(i);
ps = (PackageSetting) childPkg.mExtras;
if (ps != null) {
- removePackageLI(ps, chatty);
+ removePackageLI(ps.name, chatty);
}
}
}
- void removePackageLI(PackageSetting ps, boolean chatty) {
+ void removePackageLI(String packageName, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
- Log.d(TAG, "Removing package " + ps.name);
+ Log.d(TAG, "Removing package " + packageName);
}
// writer
synchronized (mPackages) {
- mPackages.remove(ps.name);
- final PackageParser.Package pkg = ps.pkg;
- if (pkg != null) {
- cleanPackageDataStructuresLILPw(pkg, chatty);
+ final PackageParser.Package removedPackage = mPackages.remove(packageName);
+ if (removedPackage != null) {
+ cleanPackageDataStructuresLILPw(removedPackage, chatty);
}
}
}
@@ -14653,68 +14690,6 @@
String origPermission;
}
- /*
- * Install a non-existing package.
- */
- private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
- String volumeUuid, PackageInstalledInfo res, int installReason) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
-
- // Remember this for later, in case we need to rollback this install
- String pkgName = pkg.packageName;
-
- if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
-
- synchronized(mPackages) {
- final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
- if (renamedPackage != null) {
- // A package with the same name is already installed, though
- // it has been renamed to an older name. The package we
- // are trying to install should be installed as an update to
- // the existing one, but that has not been requested, so bail.
- res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
- + " without first uninstalling package running as "
- + renamedPackage);
- return;
- }
- if (mPackages.containsKey(pkgName)) {
- // Don't allow installation over an existing package with the same name.
- res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
- + " without first uninstalling.");
- return;
- }
- }
-
- try {
- 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
- deletePackageLIF(pkgName, UserHandle.ALL, false, null,
- 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);
- }
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
private static void updateDigest(MessageDigest digest, File file) throws IOException {
try (DigestInputStream digestStream =
new DigestInputStream(new FileInputStream(file), digest)) {
@@ -14722,482 +14697,6 @@
}
}
- private void replacePackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
- PackageInstalledInfo res, int installReason) {
- final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
-
- final PackageParser.Package oldPackage;
- final PackageSetting ps;
- final String pkgName = pkg.packageName;
- final int[] allUsers;
- final int[] installedUsers;
-
- synchronized(mPackages) {
- oldPackage = mPackages.get(pkgName);
- if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
-
- // don't allow upgrade to target a release SDK from a pre-release SDK
- final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion
- == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
- final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion
- == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
- if (oldTargetsPreRelease
- && !newTargetsPreRelease
- && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {
- Slog.w(TAG, "Can't install package targeting released sdk");
- res.setReturnCode(PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);
- return;
- }
-
- ps = mSettings.mPackages.get(pkgName);
-
- // verify signatures are valid
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
- if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
- if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package not signed by keys specified by upgrade-keysets: "
- + pkgName);
- return;
- }
- } else {
-
- // default to original signature matching
- if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !oldPackage.mSigningDetails.checkCapability(
- pkg.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package has a different signature: " + pkgName);
- return;
- }
- }
-
- // don't allow a system upgrade unless the upgrade hash matches
- if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
- final byte[] digestBytes;
- try {
- final MessageDigest digest = MessageDigest.getInstance("SHA-512");
- updateDigest(digest, new File(pkg.baseCodePath));
- if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
- for (String path : pkg.splitCodePaths) {
- updateDigest(digest, new File(path));
- }
- }
- digestBytes = digest.digest();
- } catch (NoSuchAlgorithmException | IOException e) {
- res.setError(INSTALL_FAILED_INVALID_APK,
- "Could not compute hash: " + pkgName);
- return;
- }
- if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
- res.setError(INSTALL_FAILED_INVALID_APK,
- "New package fails restrict-update check: " + pkgName);
- return;
- }
- // retain upgrade restriction
- pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
- }
-
- // Check for shared user id changes
- String invalidPackageName =
- getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
- if (invalidPackageName != null) {
- res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
- "Package " + invalidPackageName + " tried to change user "
- + oldPackage.mSharedUserId);
- return;
- }
-
- // check if the new package supports all of the abis which the old package supports
- boolean oldPkgSupportMultiArch = oldPackage.applicationInfo.secondaryCpuAbi != null;
- boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
- if (isSystemApp(oldPackage) && oldPkgSupportMultiArch && !newPkgSupportMultiArch) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Update to package " + pkgName + " doesn't support multi arch");
- return;
- }
-
- // In case of rollback, remember per-user/profile install state
- allUsers = sUserManager.getUserIds();
- installedUsers = ps.queryInstalledUsers(allUsers, true);
-
- // don't allow an upgrade from full to ephemeral
- if (isInstantApp) {
- if (user == null || user.getIdentifier() == UserHandle.USER_ALL) {
- for (int currentUser : allUsers) {
- if (!ps.getInstantApp(currentUser)) {
- // can't downgrade from full to instant
- Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
- + " for user: " + currentUser);
- res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
- return;
- }
- }
- } else if (!ps.getInstantApp(user.getIdentifier())) {
- // can't downgrade from full to instant
- Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
- + " for user: " + user.getIdentifier());
- res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
- return;
- }
- }
- }
-
- // Update what is removed
- res.removedInfo = new PackageRemovedInfo(this);
- res.removedInfo.uid = oldPackage.applicationInfo.uid;
- res.removedInfo.removedPackage = oldPackage.packageName;
- res.removedInfo.installerPackageName = ps.installerPackageName;
- res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
- res.removedInfo.isUpdate = true;
- res.removedInfo.origUsers = installedUsers;
- res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
- for (int i = 0; i < installedUsers.length; i++) {
- final int userId = installedUsers[i];
- res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
- }
-
- final int childCount = (oldPackage.childPackages != null)
- ? oldPackage.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- boolean childPackageUpdated = false;
- PackageParser.Package childPkg = oldPackage.childPackages.get(i);
- final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
- if (res.addedChildPackages != null) {
- PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
- if (childRes != null) {
- childRes.removedInfo.uid = childPkg.applicationInfo.uid;
- childRes.removedInfo.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRes.removedInfo.installerPackageName = childPs.installerPackageName;
- }
- childRes.removedInfo.isUpdate = true;
- childRes.removedInfo.installReasons = res.removedInfo.installReasons;
- childPackageUpdated = true;
- }
- }
- if (!childPackageUpdated) {
- PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
- childRemovedRes.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRemovedRes.installerPackageName = childPs.installerPackageName;
- }
- childRemovedRes.isUpdate = false;
- childRemovedRes.dataRemoved = true;
- synchronized (mPackages) {
- if (childPs != null) {
- childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, true);
- }
- }
- if (res.removedInfo.removedChildPackages == null) {
- res.removedInfo.removedChildPackages = new ArrayMap<>();
- }
- res.removedInfo.removedChildPackages.put(childPkg.packageName, childRemovedRes);
- }
- }
-
- boolean sysPkg = (isSystemApp(oldPackage));
- if (sysPkg) {
- // Set the system/privileged/oem/vendor/product flags as needed
- final boolean privileged = isPrivilegedApp(oldPackage);
- final boolean oem = isOemApp(oldPackage);
- final boolean vendor = isVendorApp(oldPackage);
- final boolean product = isProductApp(oldPackage);
- final boolean productServices = isProductServicesApp(oldPackage);
-
- final @ParseFlags int systemParseFlags = parseFlags;
- final @ScanFlags int systemScanFlags = scanFlags
- | SCAN_AS_SYSTEM
- | (privileged ? SCAN_AS_PRIVILEGED : 0)
- | (oem ? SCAN_AS_OEM : 0)
- | (vendor ? SCAN_AS_VENDOR : 0)
- | (product ? SCAN_AS_PRODUCT : 0)
- | (productServices ? SCAN_AS_PRODUCT_SERVICES : 0);
-
- replaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags,
- user, allUsers, installerPackageName, res, installReason);
- } else {
- replaceNonSystemPackageLIF(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, installerPackageName, res, installReason);
- }
- }
-
- private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage,
- PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user, int[] allUsers,
- String installerPackageName, PackageInstalledInfo res, int installReason) {
- if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
- + deletedPackage);
-
- String pkgName = deletedPackage.packageName;
- boolean deletedPkg = true;
- boolean addedPkg = false;
- boolean updatedSettings = false;
- final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA
- | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
-
- final long origUpdateTime = (pkg.mExtras != null)
- ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
-
- // First delete the existing package while retaining the data directory
- if (!deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
- res.removedInfo, true, pkg)) {
- // If the existing package wasn't successfully deleted
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
- deletedPkg = false;
- } else {
- // Successfully deleted the old package; proceed with replace.
-
- // If deleted package lived in a container, give users a chance to
- // relinquish resources before killing.
- if (deletedPackage.isForwardLocked() || isExternal(deletedPackage)) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
- }
- final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
- final ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(deletedPackage.applicationInfo.packageName);
- sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
- }
-
- clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-
- try {
- 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);
-
- // Update the in-memory copy of the previous code paths.
- PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (!killApp) {
- if (ps.oldCodePaths == null) {
- ps.oldCodePaths = new ArraySet<>();
- }
- Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath);
- if (deletedPackage.splitCodePaths != null) {
- Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths);
- }
- } else {
- ps.oldCodePaths = null;
- }
- if (ps.childPackageNames != null) {
- for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) {
- final String childPkgName = ps.childPackageNames.get(i);
- final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
- childPs.oldCodePaths = ps.oldCodePaths;
- }
- }
- prepareAppDataAfterInstallLIF(newPackage);
- addedPkg = true;
- mDexManager.notifyPackageUpdated(newPackage.packageName,
- newPackage.baseCodePath, newPackage.splitCodePaths);
- } catch (PackageManagerException e) {
- res.setError("Package couldn't be installed in " + pkg.codePath, e);
- }
- }
-
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
-
- // Revert all internal state mutations and added folders for the failed install
- if (addedPkg) {
- deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
- res.removedInfo, true, null);
- }
-
- // Restore the old package
- if (deletedPkg) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
- File restoreFile = new File(deletedPackage.codePath);
- // Parse old package
- boolean oldExternal = isExternal(deletedPackage);
- int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY |
- (deletedPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) |
- (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
- int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
- try {
- scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
- null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
- + e.getMessage());
- return;
- }
-
- synchronized (mPackages) {
- // Ensure the installer package name up to date
- setInstallerPackageNameLPw(deletedPackage, installerPackageName);
-
- // Update permissions for restored package
- mPermissionManager.updatePermissions(
- deletedPackage.packageName, deletedPackage, false, mPackages.values(),
- mPermissionCallback);
-
- mSettings.writeLPr();
- }
-
- Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
- }
- } else {
- synchronized (mPackages) {
- PackageSetting ps = mSettings.getPackageLPr(pkg.packageName);
- if (ps != null) {
- res.removedInfo.removedForAllUsers = mPackages.get(ps.name) == null;
- if (res.removedInfo.removedChildPackages != null) {
- final int childCount = res.removedInfo.removedChildPackages.size();
- // Iterate in reverse as we may modify the collection
- for (int i = childCount - 1; i >= 0; i--) {
- String childPackageName = res.removedInfo.removedChildPackages.keyAt(i);
- if (res.addedChildPackages.containsKey(childPackageName)) {
- res.removedInfo.removedChildPackages.removeAt(i);
- } else {
- PackageRemovedInfo childInfo = res.removedInfo
- .removedChildPackages.valueAt(i);
- childInfo.removedForAllUsers = mPackages.get(
- childInfo.removedPackage) == null;
- }
- }
- }
- }
- }
- }
- }
-
- private void replaceSystemPackageLIF(PackageParser.Package deletedPackage,
- PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user,
- int[] allUsers, String installerPackageName, PackageInstalledInfo res,
- int installReason) {
- if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
- + ", old=" + deletedPackage);
-
- final boolean disabledSystem;
-
- // Remove existing system package
- removePackageLI(deletedPackage, true);
-
- synchronized (mPackages) {
- disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);
- }
- if (!disabledSystem) {
- // We didn't need to disable the .apk as a current system package,
- // which means we are replacing another update that is already
- // installed. We need to make sure to delete the older one's .apk.
- res.removedInfo.args = createInstallArgsForExisting(0,
- deletedPackage.applicationInfo.getCodePath(),
- deletedPackage.applicationInfo.getResourcePath(),
- getAppDexInstructionSets(deletedPackage.applicationInfo));
- } else {
- res.removedInfo.args = null;
- }
-
- // Successfully disabled the old package. Now proceed with re-installation
- clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
- ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
-
- PackageParser.Package newPackage = null;
- try {
- final List<ScanResult> scanResults =
- scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
- // Add the package to the internal data structures
- commitSuccessfulScanResults(scanResults);
- newPackage = scanResults.get(0).pkgSetting.pkg;
-
- // Set the update and install times
- PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
- setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
- System.currentTimeMillis());
-
- // Update the package dynamic state if succeeded
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- // Now that the install succeeded make sure we remove data
- // directories for any child package the update removed.
- final int deletedChildCount = (deletedPackage.childPackages != null)
- ? deletedPackage.childPackages.size() : 0;
- final int newChildCount = (newPackage.childPackages != null)
- ? newPackage.childPackages.size() : 0;
- for (int i = 0; i < deletedChildCount; i++) {
- PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i);
- boolean childPackageDeleted = true;
- for (int j = 0; j < newChildCount; j++) {
- PackageParser.Package newChildPkg = newPackage.childPackages.get(j);
- if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
- childPackageDeleted = false;
- break;
- }
- }
- if (childPackageDeleted) {
- PackageSetting ps = mSettings.getDisabledSystemPkgLPr(
- deletedChildPkg.packageName);
- if (ps != null && res.removedInfo.removedChildPackages != null) {
- PackageRemovedInfo removedChildRes = res.removedInfo
- .removedChildPackages.get(deletedChildPkg.packageName);
- removePackageDataLIF(ps, allUsers, removedChildRes, 0, false);
- removedChildRes.removedForAllUsers = mPackages.get(ps.name) == null;
- }
- }
- }
-
- updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
- installReason);
- prepareAppDataAfterInstallLIF(newPackage);
-
- mDexManager.notifyPackageUpdated(newPackage.packageName,
- newPackage.baseCodePath, newPackage.splitCodePaths);
- }
- } catch (PackageManagerException e) {
- res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
- res.setError("Package couldn't be installed in " + pkg.codePath, e);
- }
-
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // Re installation failed. Restore old information
- // Remove new pkg information
- if (newPackage != null) {
- removeInstalledPackageLI(newPackage, true);
- }
- // Add back the old system package
- try {
- 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());
- }
-
- synchronized (mPackages) {
- if (disabledSystem) {
- enableSystemPackageLPw(deletedPackage);
- }
-
- // Ensure the installer package name up to date
- setInstallerPackageNameLPw(deletedPackage, installerPackageName);
-
- // Update permissions for restored package
- mPermissionManager.updatePermissions(
- deletedPackage.packageName, deletedPackage, false, mPackages.values(),
- mPermissionCallback);
-
- mSettings.writeLPr();
- }
-
- Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName
- + " after failed upgrade");
- }
- }
-
/**
* Checks whether the parent or any of the child packages have a change shared
* user. For a package to be a valid update the shred users of the parent and
@@ -15394,92 +14893,683 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+
private static class InstallRequest {
- final InstallArgs args;
- final PackageInstalledInfo res;
+ public final InstallArgs args;
+ public final PackageInstalledInfo installResult;
private InstallRequest(InstallArgs args, PackageInstalledInfo res) {
this.args = args;
- this.res = res;
+ this.installResult = res;
}
}
@GuardedBy({"mInstallLock", "mPackages"})
- private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
+ private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo installResult) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
- installPackagesLI(Collections.singletonList(new InstallRequest(args, res)));
+ installPackagesLI(Collections.singletonList(new InstallRequest(args, installResult)));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
+ /**
+ * Package state to commit to memory and disk after reconciliation has completed.
+ */
private static class CommitRequest {
final Map<String, ReconciledPackage> reconciledPackages;
+ final int[] mAllUsers;
- private CommitRequest(Map<String, ReconciledPackage> reconciledPackages) {
+ private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, int[] allUsers) {
this.reconciledPackages = reconciledPackages;
+ this.mAllUsers = allUsers;
}
}
- private static class ReconcileRequest {
- final Map<String, ScanResult> scannedPackages;
- private ReconcileRequest(Map<String, ScanResult> scannedPackages) {
+ /**
+ * Package scan results and related request details used to reconcile the potential addition of
+ * one or more packages to the system.
+ *
+ * Reconcile will take a set of package details that need to be committed to the system and make
+ * sure that they are valid in the context of the system and the other installing apps. Any
+ * invalid state or app will result in a failed reconciliation and thus whatever operation (such
+ * as install) led to the request.
+ */
+ private static class ReconcileRequest {
+ public final Map<String, ScanResult> scannedPackages;
+
+ // TODO: Remove install-specific details from reconcile request; make them generic types
+ // that can be used for scanDir for example.
+ public final Map<String, InstallArgs> installArgs;
+ public final Map<String, PackageInstalledInfo> installResults;
+ public final Map<String, PrepareResult> preparedPackages;
+
+ private ReconcileRequest(Map<String, ScanResult> scannedPackages,
+ Map<String, InstallArgs> installArgs,
+ Map<String, PackageInstalledInfo> installResults,
+ Map<String, PrepareResult> preparedPackages) {
this.scannedPackages = scannedPackages;
+ this.installArgs = installArgs;
+ this.installResults = installResults;
+ this.preparedPackages = preparedPackages;
}
}
private static class ReconcileFailure extends PackageManagerException {
public ReconcileFailure(String message) {
super("Invalid reconcile request: " + message);
}
- };
+ }
/**
* A container of all data needed to commit a package to in-memory data structures and to disk.
- * Ideally most of the data contained in this class will move into a PackageSetting it contains.
+ * TODO: move most of the data contained her into a PackageSetting for commit.
*/
- private static class ReconciledPackage {}
+ private static class ReconciledPackage {
+ public final PackageSetting pkgSetting;
+ public final ScanResult scanResult;
+ public final UserHandle installForUser;
+ public final String volumeUuid;
+ // TODO: Remove install-specific details from the reconcile result
+ public final PackageInstalledInfo installResult;
+ public final PrepareResult prepareResult;
+ @PackageManager.InstallFlags
+ public final int installFlags;
+ public final InstallArgs installArgs;
+
+ private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
+ UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
+ String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) {
+ this.installArgs = installArgs;
+ this.pkgSetting = pkgSetting;
+ this.installForUser = installForUser;
+ this.installResult = installResult;
+ this.installFlags = installFlags;
+ this.volumeUuid = volumeUuid;
+ this.prepareResult = prepareResult;
+ this.scanResult = scanResult;
+ }
+ }
@GuardedBy("mPackages")
private static Map<String, ReconciledPackage> reconcilePackagesLocked(
- final ReconcileRequest request) throws ReconcileFailure {
- return Collections.emptyMap();
+ final ReconcileRequest request)
+ throws ReconcileFailure {
+ Map<String, ReconciledPackage> result = new ArrayMap<>(request.scannedPackages.size());
+ for (String installPackageName : request.installArgs.keySet()) {
+ final ScanResult scanResult = request.scannedPackages.get(installPackageName);
+ final InstallArgs installArgs = request.installArgs.get(installPackageName);
+ final PackageInstalledInfo res = request.installResults.get(installPackageName);
+ if (scanResult == null || installArgs == null || res == null) {
+ throw new ReconcileFailure(
+ "inputs not balanced; missing argument for " + installPackageName);
+ }
+ result.put(installPackageName,
+ new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
+ res, installArgs.installFlags, installArgs.volumeUuid,
+ request.preparedPackages.get(installPackageName), scanResult));
+ }
+ return result;
}
@GuardedBy("mPackages")
private boolean commitPackagesLocked(final CommitRequest request) {
+ // TODO: remove any expected failures from this method; this should only be able to fail due
+ // to unavoidable errors (I/O, etc.)
+ for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
+ final ScanResult scanResult = reconciledPkg.scanResult;
+ final ScanRequest scanRequest = scanResult.request;
+ final PackageParser.Package pkg = scanRequest.pkg;
+ final String packageName = pkg.packageName;
+ final PackageInstalledInfo res = reconciledPkg.installResult;
+
+ if (reconciledPkg.prepareResult.replace) {
+ PackageParser.Package oldPackage = mPackages.get(packageName);
+ if (reconciledPkg.prepareResult.system) {
+ // Remove existing system package
+ removePackageLI(oldPackage, true);
+ if (!disableSystemPackageLPw(oldPackage, pkg)) {
+ // We didn't need to disable the .apk as a current system package,
+ // which means we are replacing another update that is already
+ // installed. We need to make sure to delete the older one's .apk.
+ res.removedInfo.args = createInstallArgsForExisting(0,
+ oldPackage.applicationInfo.getCodePath(),
+ oldPackage.applicationInfo.getResourcePath(),
+ getAppDexInstructionSets(oldPackage.applicationInfo));
+ } else {
+ res.removedInfo.args = null;
+ }
+
+ // Set the update and install times
+ PackageSetting deletedPkgSetting = (PackageSetting) oldPackage.mExtras;
+ setInstallAndUpdateTime(pkg, deletedPkgSetting.firstInstallTime,
+ System.currentTimeMillis());
+
+ // Update the package dynamic state if succeeded
+ // Now that the install succeeded make sure we remove data
+ // directories for any child package the update removed.
+ final int deletedChildCount = (oldPackage.childPackages != null)
+ ? oldPackage.childPackages.size() : 0;
+ final int newChildCount = (pkg.childPackages != null)
+ ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < deletedChildCount; i++) {
+ PackageParser.Package deletedChildPkg = oldPackage.childPackages.get(i);
+ boolean childPackageDeleted = true;
+ for (int j = 0; j < newChildCount; j++) {
+ PackageParser.Package newChildPkg = pkg.childPackages.get(j);
+ if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
+ childPackageDeleted = false;
+ break;
+ }
+ }
+ if (childPackageDeleted) {
+ PackageSetting ps1 = mSettings.getDisabledSystemPkgLPr(
+ deletedChildPkg.packageName);
+ if (ps1 != null && res.removedInfo.removedChildPackages != null) {
+ PackageRemovedInfo removedChildRes = res.removedInfo
+ .removedChildPackages.get(deletedChildPkg.packageName);
+ removePackageDataLIF(ps1, request.mAllUsers, removedChildRes, 0,
+ false);
+ removedChildRes.removedForAllUsers = mPackages.get(ps1.name)
+ == null;
+ }
+ }
+ }
+ } else {
+ final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
+ // First delete the existing package while retaining the data directory
+ if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags,
+ res.removedInfo, true, pkg)) {
+ // If the existing package wasn't successfully deleted
+ res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+ "replaceNonSystemPackageLI");
+ return false;
+ } else {
+ // Successfully deleted the old package; proceed with replace.
+
+ // If deleted package lived in a container, give users a chance to
+ // relinquish resources before killing.
+ if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + oldPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+ final ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(oldPackage.applicationInfo.packageName);
+ sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ }
+
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE
+ | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+
+
+
+ // Update the in-memory copy of the previous code paths.
+ PackageSetting ps1 = mSettings.mPackages.get(
+ reconciledPkg.prepareResult.existingPackage.packageName);
+ if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
+ == 0) {
+ if (ps1.mOldCodePaths == null) {
+ ps1.mOldCodePaths = new ArraySet<>();
+ }
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.baseCodePath);
+ if (oldPackage.splitCodePaths != null) {
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.splitCodePaths);
+ }
+ } else {
+ ps1.mOldCodePaths = null;
+ }
+ if (ps1.childPackageNames != null) {
+ for (int i = ps1.childPackageNames.size() - 1; i >= 0; --i) {
+ final String childPkgName = ps1.childPackageNames.get(i);
+ final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
+ childPs.mOldCodePaths = ps1.mOldCodePaths;
+ }
+ }
+
+ if (reconciledPkg.installResult.returnCode
+ == PackageManager.INSTALL_SUCCEEDED) {
+ PackageSetting ps2 = mSettings.getPackageLPr(pkg.packageName);
+ if (ps2 != null) {
+ res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
+ if (res.removedInfo.removedChildPackages != null) {
+ final int childCount1 = res.removedInfo.removedChildPackages.size();
+ // Iterate in reverse as we may modify the collection
+ for (int i = childCount1 - 1; i >= 0; i--) {
+ String childPackageName =
+ res.removedInfo.removedChildPackages.keyAt(i);
+ if (res.addedChildPackages.containsKey(childPackageName)) {
+ res.removedInfo.removedChildPackages.removeAt(i);
+ } else {
+ PackageRemovedInfo childInfo = res.removedInfo
+ .removedChildPackages.valueAt(i);
+ childInfo.removedForAllUsers = mPackages.get(
+ childInfo.removedPackage) == null;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ try {
+ commitScanResultLocked(scanResult);
+ } catch (PackageManagerException e) {
+ res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
+ res.setError("Package couldn't be installed in " + pkg.codePath, e);
+ return false;
+ }
+ updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
+ res, reconciledPkg.installForUser, reconciledPkg.installArgs.installReason);
+
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
+ ps.setUpdateAvailable(false /*updateAvailable*/);
+ }
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ PackageInstalledInfo childRes = res.addedChildPackages.get(
+ childPkg.packageName);
+ PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
+ if (childPs != null) {
+ childRes.newUsers = childPs.queryInstalledUsers(
+ sUserManager.getUserIds(), true);
+ }
+ }
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ updateSequenceNumberLP(ps, res.newUsers);
+ updateInstantAppInstallerLocked(packageName);
+ }
+ }
return true;
}
- @GuardedBy({"mInstallLock", "mPackages", "PackageManagerService.mPackages"})
+ /**
+ * Installs one or more packages atomically. This operation is broken up into four phases:
+ * <ul>
+ * <li><b>Prepare</b>
+ * <br/>Analyzes any current install state, parses the package and does initial
+ * validation on it.</li>
+ * <li><b>Scan</b>
+ * <br/>Interrogates the parsed packages given the context collected in prepare.</li>
+ * <li><b>Reconcile</b>
+ * <br/>Validates scanned packages in the context of each other and the current system
+ * state to ensure that the install will be successful.
+ * <li><b>Commit</b>
+ * <br/>Commits all scanned packages and updates system state. This is the only place
+ * that system state may be modified in the install flow and all predictable errors
+ * must be determined before this phase.</li>
+ * </ul>
+ *
+ * Failure at any phase will result in a full failure to install all packages.
+ */
+ @GuardedBy({"mInstallLock", "mPackages"})
private void installPackagesLI(List<InstallRequest> requests) {
- Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
- for (InstallRequest request : requests) {
- // TODO(b/109941548): remove this once we've pulled everything from it and into scan,
- // reconcile or commit.
- preparePackageLI(request.args, request.res);
-
- // TODO(b/109941548): scan package and get result
- }
- ReconcileRequest reconcileRequest = new ReconcileRequest(scans);
- Map<String, ReconciledPackage> reconciledPackages;
+ final Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
+ final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
+ final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
+ final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
try {
- reconciledPackages = reconcilePackagesLocked(reconcileRequest);
- } catch (ReconcileFailure e) {
- // TODO(b/109941548): set install args error
- return;
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
+ for (InstallRequest request : requests) {
+ // TODO(b/109941548): remove this once we've pulled everything from it and into
+ // scan, reconcile or commit.
+ final PrepareResult prepareResult;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
+ prepareResult = preparePackageLI(request.args, request.installResult);
+ } catch (PrepareFailure prepareFailure) {
+ request.installResult.setError(prepareFailure.error,
+ prepareFailure.getMessage());
+ request.installResult.origPackage = prepareFailure.conflictingPackage;
+ request.installResult.origPermission = prepareFailure.conflictingPermission;
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ request.installResult.installerPackageName = request.args.installerPackageName;
+
+ final String packageName = prepareResult.packageToScan.packageName;
+ prepareResults.put(packageName, prepareResult);
+ installResults.put(packageName, request.installResult);
+ installArgs.put(packageName, request.args);
+ try {
+ final List<ScanResult> scanResults = scanPackageTracedLI(
+ prepareResult.packageToScan, prepareResult.parseFlags,
+ prepareResult.scanFlags, System.currentTimeMillis(),
+ request.args.user);
+ for (ScanResult result : scanResults) {
+ if (null != scans.put(result.pkgSetting.pkg.packageName, result)) {
+ request.installResult.setError(
+ PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Duplicate package " + result.pkgSetting.pkg.packageName
+ + " in atomic install request.");
+ return;
+ }
+ }
+ } catch (PackageManagerException e) {
+ request.installResult.setError("Scanning Failed.", e);
+ return;
+ }
+ }
+ ReconcileRequest reconcileRequest = new ReconcileRequest(scans, installArgs,
+ installResults,
+ prepareResults);
+ CommitRequest commitRequest = null;
+ synchronized (mPackages) {
+ Map<String, ReconciledPackage> reconciledPackages;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
+ reconciledPackages = reconcilePackagesLocked(reconcileRequest);
+ } catch (ReconcileFailure e) {
+ for (InstallRequest request : requests) {
+ // TODO(b/109941548): add more concrete failure reasons
+ request.installResult.setError("Reconciliation failed...", e);
+ // TODO: return any used system resources
+ }
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
+ commitRequest = new CommitRequest(reconciledPackages,
+ sUserManager.getUserIds());
+ if (!commitPackagesLocked(commitRequest)) {
+ cleanUpCommitFailuresLocked(commitRequest);
+ return;
+ }
+ } finally {
+ for (PrepareResult result : prepareResults.values()) {
+ if (result.freezer != null) {
+ result.freezer.close();
+ }
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+ executePostCommitSteps(commitRequest);
+ } finally {
+ for (PrepareResult result : prepareResults.values()) {
+ if (result.freezer != null) {
+ result.freezer.close();
+ }
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- CommitRequest request = new CommitRequest(reconciledPackages);
- if (!commitPackagesLocked(request)) {
- // TODO(b/109941548): set install args error
- return;
- }
- // TODO(b/109941548) post-commit actions (dex-opt, etc.)
}
- @Deprecated
+ /**
+ * On successful install, executes remaining steps after commit completes and the package lock
+ * is released.
+ */
+ private void executePostCommitSteps(CommitRequest commitRequest) {
+ for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
+ final boolean forwardLocked =
+ ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
+ final boolean instantApp =
+ ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+ final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
+ final String packageName = pkg.packageName;
+ prepareAppDataAfterInstallLIF(pkg);
+ if (reconciledPkg.prepareResult.clearCodeCache) {
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+ if (reconciledPkg.prepareResult.replace) {
+ mDexManager.notifyPackageUpdated(pkg.packageName,
+ pkg.baseCodePath, pkg.splitCodePaths);
+ }
+
+ // Prepare the application profiles for the new code paths.
+ // This needs to be done before invoking dexopt so that any install-time profile
+ // can be used for optimizations.
+ mArtManagerService.prepareAppProfiles(
+ pkg, resolveUserIds(reconciledPkg.installForUser.getIdentifier()));
+
+ // Check whether we need to dexopt the app.
+ //
+ // NOTE: it is IMPORTANT to call dexopt:
+ // - after doRename which will sync the package data from PackageParser.Package and
+ // its corresponding ApplicationInfo.
+ // - after installNewPackageLIF or replacePackageLIF which will update result with the
+ // uid of the application (pkg.applicationInfo.uid).
+ // This update happens in place!
+ //
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not forward locked.
+ // 2) it is not on on an external ASEC container.
+ // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 4) it is not debuggable.
+ //
+ // Note that we do not dexopt instant apps by default. dexopt can take some time to
+ // complete, so we skip this step during installation. Instead, we'll take extra time
+ // the first time the instant app starts. It's preferred to do it this way to provide
+ // continuous progress to the useur instead of mysteriously blocking somewhere in the
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ final boolean performDexopt = !forwardLocked
+ && !pkg.applicationInfo.isExternalAsec()
+ && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
+ && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+
+ if (performDexopt) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ // Do not run PackageDexOptimizer through the local performDexOpt
+ // method because `pkg` may not be in `mPackages` yet.
+ //
+ // Also, don't fail application installs if the dexopt step fails.
+ DexoptOptions dexoptOptions = new DexoptOptions(packageName,
+ REASON_INSTALL,
+ DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
+ mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+ null /* instructionSets */,
+ getOrCreateCompilerPackageStats(pkg),
+ mDexManager.getPackageUseInfoOrDefault(packageName),
+ dexoptOptions);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Notify BackgroundDexOptService that the package has been changed.
+ // If this is an update of a package which used to fail to compile,
+ // BackgroundDexOptService will remove it from its blacklist.
+ // TODO: Layering violation
+ BackgroundDexOptService.notifyPackageChanged(packageName);
+ }
+
+ }
+
+ private void cleanUpCommitFailuresLocked(CommitRequest request) {
+ final Map<String, ReconciledPackage> reconciledPackages = request.reconciledPackages;
+ final int[] allUsers = request.mAllUsers;
+ for (ReconciledPackage reconciledPackage : reconciledPackages.values()) {
+ final String pkgName1 = reconciledPackage.pkgSetting.pkg.packageName;
+ if (reconciledPackage.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ reconciledPackage.installResult.setError(
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, "Commit failed...");
+ }
+ final PackageParser.Package oldPackage =
+ reconciledPackage.prepareResult.existingPackage;
+ final PackageParser.Package newPackage = reconciledPackage.pkgSetting.pkg;
+ if (reconciledPackage.prepareResult.system) {
+ // Re installation failed. Restore old information
+ // Remove new pkg information
+ if (newPackage != null) {
+ removeInstalledPackageLI(newPackage, true);
+ }
+ // Add back the old system package
+ PackageParser.Package restoredPkg = null;
+ try {
+ final List<ScanResult> restoreResults = scanPackageTracedLI(oldPackage,
+ reconciledPackage.prepareResult.parseFlags, SCAN_UPDATE_SIGNATURE, 0,
+ reconciledPackage.installForUser);
+ commitSuccessfulScanResults(restoreResults);
+ restoredPkg = restoreResults.get(0).pkgSetting.pkg;
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
+ }
+ synchronized (mPackages) {
+ final boolean disabledSystem;
+ // Remove existing system package
+ removePackageLI(reconciledPackage.scanResult.pkgSetting.pkg, true);
+ disabledSystem = disableSystemPackageLPw(
+ reconciledPackage.scanResult.pkgSetting.pkg, restoredPkg);
+ if (disabledSystem) {
+ enableSystemPackageLPw(restoredPkg);
+ }
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(reconciledPackage.scanResult.pkgSetting.pkg,
+ reconciledPackage.installArgs.installerPackageName);
+ // Update permissions for restored package
+ mPermissionManager.updatePermissions(
+ restoredPkg.packageName, restoredPkg, false, mPackages.values(),
+ mPermissionCallback);
+ mSettings.writeLPr();
+ }
+ Slog.i(TAG, "Successfully restored package : " + restoredPkg.packageName
+ + " after failed upgrade");
+ } else if (reconciledPackage.prepareResult.replace) {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName1);
+
+ // Revert all internal state mutations and added folders for the failed install
+ boolean deletedPkg = deletePackageLIF(pkgName1, null, true,
+ allUsers, /*TODO: deleteFlags*/ 0,
+ reconciledPackage.installResult.removedInfo, true, null);
+
+ // Restore the old package
+ if (deletedPkg) {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + oldPackage);
+ File restoreFile = new File(oldPackage.codePath);
+ // Parse old package
+ boolean oldExternal = isExternal(oldPackage);
+ int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+ | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
+ | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+ int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
+ try {
+ scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags,
+ /* origUpdateTime */ System.currentTimeMillis(), null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to restore package : " + pkgName1
+ + " after failed upgrade: "
+ + e.getMessage());
+ return;
+ }
+
+ synchronized (mPackages) {
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(oldPackage,
+ reconciledPackage.installArgs.installerPackageName);
+
+ // Update permissions for restored package
+ mPermissionManager.updatePermissions(
+ oldPackage.packageName, oldPackage, false, mPackages.values(),
+ mPermissionCallback);
+
+ mSettings.writeLPr();
+ }
+
+ Slog.i(TAG, "Successfully restored package : " + pkgName1
+ + " after failed upgrade");
+ }
+ } else {
+ // Remove package from internal structures, but keep around any
+ // data that might have already existed
+ deletePackageLIF(pkgName1, UserHandle.ALL, false, null,
+ PackageManager.DELETE_KEEP_DATA,
+ reconciledPackage.installResult.removedInfo, true, null);
+ }
+ }
+ }
+
+ /**
+ * The set of data needed to successfully install the prepared package. This includes data that
+ * will be used to scan and reconcile the package.
+ */
+ private static class PrepareResult {
+ public final int installReason;
+ public final String volumeUuid;
+ public final String installerPackageName;
+ public final UserHandle user;
+ public final boolean replace;
+ public final int scanFlags;
+ public final int parseFlags;
+ @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
+ public final PackageParser.Package existingPackage;
+ public final PackageParser.Package packageToScan;
+ public final boolean clearCodeCache;
+ public final boolean system;
+ /* The original package name if it was changed during an update, otherwise {@code null}. */
+ @Nullable
+ public final String renamedPackage;
+ public final PackageFreezer freezer;
+
+ private PrepareResult(int installReason, String volumeUuid,
+ String installerPackageName, UserHandle user, boolean replace, int scanFlags,
+ int parseFlags, PackageParser.Package existingPackage,
+ PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
+ String renamedPackage,
+ PackageFreezer freezer) {
+ this.installReason = installReason;
+ this.volumeUuid = volumeUuid;
+ this.installerPackageName = installerPackageName;
+ this.user = user;
+ this.replace = replace;
+ this.scanFlags = scanFlags;
+ this.parseFlags = parseFlags;
+ this.existingPackage = existingPackage;
+ this.packageToScan = packageToScan;
+ this.clearCodeCache = clearCodeCache;
+ this.system = system;
+ this.renamedPackage = renamedPackage;
+ this.freezer = freezer;
+ }
+ }
+
+ private static class PrepareFailure extends PackageManagerException {
+
+ public String conflictingPackage;
+ public String conflictingPermission;
+
+ PrepareFailure(int error) {
+ super(error, "Failed to prepare for install.");
+ }
+
+ PrepareFailure(int error, String detailMessage) {
+ super(error, detailMessage);
+ }
+
+ PrepareFailure(String message, Exception e) {
+ super(e instanceof PackageParserException
+ ? ((PackageParserException) e).error
+ : ((PackageManagerException) e).error,
+ ExceptionUtils.getCompleteMessage(message, e));
+ }
+
+ PrepareFailure conflictsWithExistingPermission(String conflictingPermission,
+ String conflictingPackage) {
+ this.conflictingPermission = conflictingPermission;
+ this.conflictingPackage = conflictingPackage;
+ return this;
+ }
+ }
+
@GuardedBy("mInstallLock")
- private void preparePackageLI(InstallArgs args, PackageInstalledInfo res) {
+ private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
+ throws PrepareFailure {
final int installFlags = args.installFlags;
final String installerPackageName = args.installerPackageName;
final String volumeUuid = args.volumeUuid;
@@ -15492,7 +15582,6 @@
final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
final boolean virtualPreload =
((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
- boolean replace = false;
@ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
// moving a complete application; perform an initial scan on the new install location
@@ -15511,18 +15600,13 @@
scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
}
- // Result object to be returned
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- res.installerPackageName = installerPackageName;
-
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Sanity check
if (instantApp && (forwardLocked || onExternal)) {
Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
+ " external=" + onExternal);
- res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
- return;
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
// Retrieve PackageSettings and parse package
@@ -15531,6 +15615,7 @@
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
+
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
@@ -15542,8 +15627,7 @@
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
- res.setError("Failed parse during installPackageLI", e);
- return;
+ throw new PrepareFailure("Failed parse during installPackageLI", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -15553,16 +15637,14 @@
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
Slog.w(TAG,
"Instant app package " + pkg.packageName + " does not target at least O");
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package must target at least O");
- return;
}
if (pkg.mSharedUserId != null) {
Slog.w(TAG, "Instant app package " + pkg.packageName
+ " may not declare sharedUserId.");
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package may not declare a sharedUserId");
- return;
}
}
@@ -15573,9 +15655,8 @@
// No static shared libs on external storage
if (onExternal) {
Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
- res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Packages declaring static-shared libs cannot be updated");
- return;
}
}
@@ -15614,10 +15695,9 @@
}
String pkgName = res.name = pkg.packageName;
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
- res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
}
}
@@ -15629,22 +15709,21 @@
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
} catch (PackageParserException e) {
- res.setError("Failed collect during installPackageLI", e);
- return;
+ throw new PrepareFailure("Failed collect during installPackageLI", e);
}
if (instantApp && pkg.mSigningDetails.signatureSchemeVersion
< SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Slog.w(TAG, "Instant app package " + pkg.packageName
+ " is not signed with at least APK Signature Scheme v2");
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package must be signed with APK Signature Scheme v2 or greater");
- return;
}
// Get rid of all references to package scan path via parser.
pp = null;
boolean systemApp = false;
+ boolean replace = false;
synchronized (mPackages) {
// Check if installing already existing package
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
@@ -15659,8 +15738,10 @@
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
replace = true;
- if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
- + oldName + " pkgName=" + pkgName);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Replacing existing renamed package: oldName="
+ + oldName + " pkgName=" + pkgName);
+ }
} else if (mPackages.containsKey(pkgName)) {
// This package, under its official name, already exists
// on the device; we should replace it.
@@ -15670,11 +15751,11 @@
// Child packages are installed through the parent package
if (pkg.parentPackage != null) {
- res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ throw new PrepareFailure(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Package " + pkg.packageName + " is child of package "
+ pkg.parentPackage.parentPackage + ". Child packages "
+ "can be updated only through the parent package.");
- return;
}
if (replace) {
@@ -15684,26 +15765,25 @@
final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
"Package " + pkg.packageName + " new target SDK " + newTargetSdk
+ " doesn't support runtime permissions but the old"
+ " target SDK " + oldTargetSdk + " does.");
- return;
}
// Prevent persistent apps from being updated
if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) {
- res.setError(PackageManager.INSTALL_FAILED_INVALID_APK,
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
"Package " + oldPackage.packageName + " is a persistent app. "
+ "Persistent apps are not updateable.");
- return;
}
// Prevent installing of child packages
if (oldPackage.parentPackage != null) {
- res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ throw new PrepareFailure(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Package " + pkg.packageName + " is child of package "
+ oldPackage.parentPackage + ". Child packages "
+ "can be updated only through the parent package.");
- return;
}
}
}
@@ -15730,10 +15810,9 @@
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
- return;
}
} else {
try {
@@ -15750,8 +15829,7 @@
}
}
} catch (PackageManagerException e) {
- res.setError(e.error, e.getMessage());
- return;
+ throw new PrepareFailure(e.error, e.getMessage());
}
}
@@ -15762,8 +15840,9 @@
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
+
int N = pkg.permissions.size();
- for (int i = N-1; i >= 0; i--) {
+ for (int i = N - 1; i >= 0; i--) {
final PackageParser.Permission perm = pkg.permissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
@@ -15788,7 +15867,7 @@
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (sourcePackageName.equals(pkg.packageName)
&& (ksms.shouldCheckUpgradeKeySetLocked(
- sourcePackageSetting, scanFlags))) {
+ sourcePackageSetting, scanFlags))) {
sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
} else {
@@ -15796,12 +15875,12 @@
// package's certificate has rotated from the current one, or if it is an
// older certificate with which the current is ok with sharing permissions
if (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
- pkg.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ pkg.mSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
sigsOk = true;
} else if (pkg.mSigningDetails.checkCapability(
- sourcePackageSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ sourcePackageSetting.signatures.mSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
@@ -15816,12 +15895,13 @@
// install to proceed; we fail the install on all other permission
// redefinitions.
if (!sourcePackageName.equals("android")) {
- res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
- + pkg.packageName + " attempting to redeclare permission "
- + perm.info.name + " already owned by " + sourcePackageName);
- res.origPermission = perm.info.name;
- res.origPackage = sourcePackageName;
- return;
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+ + pkg.packageName
+ + " attempting to redeclare permission "
+ + perm.info.name + " already owned by "
+ + sourcePackageName)
+ .conflictsWithExistingPermission(perm.info.name,
+ sourcePackageName);
} else {
Slog.w(TAG, "Package " + pkg.packageName
+ " attempting to redeclare system permission "
@@ -15850,14 +15930,12 @@
if (systemApp) {
if (onExternal) {
// Abort update; system app can't be replaced with app on sdcard
- res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
- return;
} else if (instantApp) {
// Abort update; system app can't be replaced with an instant app
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Cannot update a system app with an instant app");
- return;
}
}
@@ -15885,13 +15963,13 @@
try {
String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
- args.abiOverride : pkg.cpuAbiOverride);
+ args.abiOverride : pkg.cpuAbiOverride);
final boolean extractNativeLibs = !pkg.isLibrary();
derivePackageAbi(pkg, abiOverride, extractNativeLibs);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
- res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Error deriving application ABI");
}
// Shared libraries for the package need to be updated.
@@ -15905,8 +15983,7 @@
}
if (!args.doRename(res.returnCode, pkg)) {
- res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
}
if (PackageManagerServiceUtils.isApkVerityEnabled()) {
@@ -15920,7 +15997,6 @@
apkPath = pkg.baseCodePath;
}
}
-
if (apkPath != null) {
final VerityUtils.SetupResult result =
VerityUtils.generateApkVeritySetupData(apkPath);
@@ -15932,16 +16008,15 @@
mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
} catch (InstallerException | IOException | DigestException |
- NoSuchAlgorithmException e) {
- res.setError(INSTALL_FAILED_INTERNAL_ERROR,
+ NoSuchAlgorithmException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
"Failed to set up verity: " + e);
- return;
} finally {
IoUtils.closeQuietly(fd);
}
} else if (result.isFailed()) {
- res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to generate verity");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to generate verity");
} else {
// Do nothing if verity is skipped. Will fall back to full apk verification on
// reboot.
@@ -15956,109 +16031,293 @@
Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
}
}
+ final PackageFreezer freezer =
+ freezePackageForInstall(pkgName, installFlags, "installPackageLI");
+ boolean shouldCloseFreezerBeforeReturn = true;
+ try {
+ final PackageParser.Package existingPackage;
+ String renamedPackage = null;
+ boolean clearCodeCache = false;
+ boolean sysPkg = false;
+ String targetVolumeUuid = volumeUuid;
+ int targetScanFlags = scanFlags;
+ int targetParseFlags = parseFlags;
- try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
- "installPackageLI")) {
if (replace) {
+ targetVolumeUuid = null;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// and cannot be updated as an update would get a new package name,
// unless this is the exact same version code which is useful for
// development.
PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
- if (existingPkg != null &&
- existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
- res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
- + "static-shared libs cannot be updated");
- return;
+ if (existingPkg != null
+ && existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Packages declaring "
+ + "static-shared libs cannot be updated");
}
}
- replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
- installerPackageName, res, args.installReason);
- } else {
- installNewPackageLIF(pkg, parseFlags, scanFlags,
- args.user, installerPackageName, volumeUuid, res, args.installReason);
- }
- }
- // Prepare the application profiles for the new code paths.
- // This needs to be done before invoking dexopt so that any install-time profile
- // can be used for optimizations.
- mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
+ final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- // Check whether we need to dexopt the app.
- //
- // NOTE: it is IMPORTANT to call dexopt:
- // - after doRename which will sync the package data from PackageParser.Package and its
- // corresponding ApplicationInfo.
- // - after installNewPackageLIF or replacePackageLIF which will update result with the
- // uid of the application (pkg.applicationInfo.uid).
- // This update happens in place!
- //
- // We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not forward locked.
- // 2) it is not on on an external ASEC container.
- // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 4) it is not debuggable.
- //
- // Note that we do not dexopt instant apps by default. dexopt can take some time to
- // complete, so we skip this step during installation. Instead, we'll take extra time
- // the first time the instant app starts. It's preferred to do it this way to provide
- // continuous progress to the useur instead of mysteriously blocking somewhere in the
- // middle of running an instant app. The default behaviour can be overridden
- // via gservices.
- final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
- && !forwardLocked
- && !pkg.applicationInfo.isExternalAsec()
- && (!instantApp || Global.getInt(mContext.getContentResolver(),
- Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
- && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+ final PackageParser.Package oldPackage;
+ final PackageSetting ps;
+ final String pkgName11 = pkg.packageName;
+ final int[] allUsers;
+ final int[] installedUsers;
- if (performDexopt) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
- DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
- REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE |
- DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
- mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
- null /* instructionSets */,
- getOrCreateCompilerPackageStats(pkg),
- mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
- dexoptOptions);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
+ synchronized (mPackages) {
+ oldPackage = mPackages.get(pkgName11);
+ existingPackage = oldPackage;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
+ }
- // Notify BackgroundDexOptService that the package has been changed.
- // If this is an update of a package which used to fail to compile,
- // BackgroundDexOptService will remove it from its blacklist.
- // TODO: Layering violation
- BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
+ // don't allow upgrade to target a release SDK from a pre-release SDK
+ final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion
+ == Build.VERSION_CODES.CUR_DEVELOPMENT;
+ final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion
+ == Build.VERSION_CODES.CUR_DEVELOPMENT;
+ if (oldTargetsPreRelease
+ && !newTargetsPreRelease
+ && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {
+ Slog.w(TAG, "Can't install package targeting released sdk");
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);
+ }
- synchronized (mPackages) {
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (ps != null) {
- res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
- ps.setUpdateAvailable(false /*updateAvailable*/);
- }
+ ps = mSettings.mPackages.get(pkgName11);
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- PackageParser.Package childPkg = pkg.childPackages.get(i);
- PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
- PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
- if (childPs != null) {
- childRes.newUsers = childPs.queryInstalledUsers(
- sUserManager.getUserIds(), true);
+ // verify signatures are valid
+ final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
+ if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package not signed by keys specified by upgrade-keysets: "
+ + pkgName11);
+ }
+ } else {
+ // default to original signature matching
+ if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !oldPackage.mSigningDetails.checkCapability(
+ pkg.mSigningDetails,
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package has a different signature: " + pkgName11);
+ }
+ }
+
+ // don't allow a system upgrade unless the upgrade hash matches
+ if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
+ final byte[] digestBytes;
+ try {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ updateDigest(digest, new File(pkg.baseCodePath));
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (String path : pkg.splitCodePaths) {
+ updateDigest(digest, new File(path));
+ }
+ }
+ digestBytes = digest.digest();
+ } catch (NoSuchAlgorithmException | IOException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+ "Could not compute hash: " + pkgName11);
+ }
+ if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+ "New package fails restrict-update check: " + pkgName11);
+ }
+ // retain upgrade restriction
+ pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
+ }
+
+ // Check for shared user id changes
+ String invalidPackageName =
+ getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
+ if (invalidPackageName != null) {
+ throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+ "Package " + invalidPackageName + " tried to change user "
+ + oldPackage.mSharedUserId);
+ }
+
+ // check if the new package supports all of the abis which the old package
+ // supports
+ boolean oldPkgSupportMultiArch =
+ oldPackage.applicationInfo.secondaryCpuAbi != null;
+ boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
+ if (isSystemApp(oldPackage) && oldPkgSupportMultiArch
+ && !newPkgSupportMultiArch) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Update to package " + pkgName11 + " doesn't support multi arch");
+ }
+
+ // In case of rollback, remember per-user/profile install state
+ allUsers = sUserManager.getUserIds();
+ installedUsers = ps.queryInstalledUsers(allUsers, true);
+
+
+ // don't allow an upgrade from full to ephemeral
+ if (isInstantApp) {
+ if (args.user == null || args.user.getIdentifier() == UserHandle.USER_ALL) {
+ for (int currentUser : allUsers) {
+ if (!ps.getInstantApp(currentUser)) {
+ // can't downgrade from full to instant
+ Slog.w(TAG,
+ "Can't replace full app with instant app: " + pkgName11
+ + " for user: " + currentUser);
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ }
+ }
+ } else if (!ps.getInstantApp(args.user.getIdentifier())) {
+ // can't downgrade from full to instant
+ Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
+ + " for user: " + args.user.getIdentifier());
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ }
+ }
+ }
+
+ // Update what is removed
+ res.removedInfo = new PackageRemovedInfo(this);
+ res.removedInfo.uid = oldPackage.applicationInfo.uid;
+ res.removedInfo.removedPackage = oldPackage.packageName;
+ res.removedInfo.installerPackageName = ps.installerPackageName;
+ res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
+ res.removedInfo.isUpdate = true;
+ res.removedInfo.origUsers = installedUsers;
+ res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
+ for (int i = 0; i < installedUsers.length; i++) {
+ final int userId = installedUsers[i];
+ res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
+ }
+
+ final int childCount = (oldPackage.childPackages != null)
+ ? oldPackage.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ boolean childPackageUpdated = false;
+ PackageParser.Package childPkg = oldPackage.childPackages.get(i);
+ final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
+ if (res.addedChildPackages != null) {
+ PackageInstalledInfo childRes = res.addedChildPackages.get(
+ childPkg.packageName);
+ if (childRes != null) {
+ childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+ childRes.removedInfo.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRes.removedInfo.installerPackageName =
+ childPs.installerPackageName;
+ }
+ childRes.removedInfo.isUpdate = true;
+ childRes.removedInfo.installReasons = res.removedInfo.installReasons;
+ childPackageUpdated = true;
+ }
+ }
+ if (!childPackageUpdated) {
+ PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
+ childRemovedRes.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRemovedRes.installerPackageName = childPs.installerPackageName;
+ }
+ childRemovedRes.isUpdate = false;
+ childRemovedRes.dataRemoved = true;
+ synchronized (mPackages) {
+ if (childPs != null) {
+ childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers,
+ true);
+ }
+ }
+ if (res.removedInfo.removedChildPackages == null) {
+ res.removedInfo.removedChildPackages = new ArrayMap<>();
+ }
+ res.removedInfo.removedChildPackages.put(childPkg.packageName,
+ childRemovedRes);
+ }
+ }
+
+ sysPkg = (isSystemApp(oldPackage));
+ if (sysPkg) {
+ // Set the system/privileged/oem/vendor/product flags as needed
+ final boolean privileged = isPrivilegedApp(oldPackage);
+ final boolean oem = isOemApp(oldPackage);
+ final boolean vendor = isVendorApp(oldPackage);
+ final boolean product = isProductApp(oldPackage);
+ final @ParseFlags int systemParseFlags = parseFlags;
+ final @ScanFlags int systemScanFlags = scanFlags
+ | SCAN_AS_SYSTEM
+ | (privileged ? SCAN_AS_PRIVILEGED : 0)
+ | (oem ? SCAN_AS_OEM : 0)
+ | (vendor ? SCAN_AS_VENDOR : 0)
+ | (product ? SCAN_AS_PRODUCT : 0);
+
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ + ", old=" + oldPackage);
+ }
+ clearCodeCache = true;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+ targetParseFlags = systemParseFlags;
+ targetScanFlags = systemScanFlags;
+ } else { // non system replace
+ replace = true;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replaceNonSystemPackageLI: new=" + pkg + ", old="
+ + oldPackage);
+ }
+
+ String pkgName1 = oldPackage.packageName;
+ boolean deletedPkg = true;
+ boolean addedPkg = false;
+ boolean updatedSettings = false;
+
+ final long origUpdateTime = (pkg.mExtras != null)
+ ? ((PackageSetting) pkg.mExtras).lastUpdateTime : 0;
+
+ }
+ } else { // new package install
+ replace = false;
+ existingPackage = null;
+ // Remember this for later, in case we need to rollback this install
+ String pkgName1 = pkg.packageName;
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
+
+ // TODO(patb): MOVE TO RECONCILE
+ synchronized (mPackages) {
+ renamedPackage = mSettings.getRenamedPackageLPr(pkgName1);
+ if (renamedPackage != null) {
+ // A package with the same name is already installed, though
+ // it has been renamed to an older name. The package we
+ // are trying to install should be installed as an update to
+ // the existing one, but that has not been requested, so bail.
+ throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+ "Attempt to re-install " + pkgName1
+ + " without first uninstalling package running as "
+ + renamedPackage);
+ }
+ if (mPackages.containsKey(pkgName1)) {
+ // Don't allow installation over an existing package with the same name.
+ throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+ "Attempt to re-install " + pkgName1
+ + " without first uninstalling.");
+ }
}
}
-
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSequenceNumberLP(ps, res.newUsers);
- updateInstantAppInstallerLocked(pkgName);
+ // we're passing the freezer back to be closed in a later phase of install
+ shouldCloseFreezerBeforeReturn = false;
+ return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
+ args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
+ clearCodeCache, sysPkg, renamedPackage, freezer);
+ } finally {
+ if (shouldCloseFreezerBeforeReturn) {
+ freezer.close();
}
}
}
@@ -16868,7 +17127,7 @@
}
}
- removePackageLI(ps, (flags & PackageManager.DELETE_CHATTY) != 0);
+ removePackageLI(ps.name, (flags & PackageManager.DELETE_CHATTY) != 0);
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final PackageParser.Package resolvedPkg;
@@ -21979,6 +22238,22 @@
}
@Override
+ public boolean isPlatformSigned(String packageName) {
+ PackageSetting packageSetting = mSettings.mPackages.get(packageName);
+ if (packageSetting == null) {
+ return false;
+ }
+ PackageParser.Package pkg = packageSetting.pkg;
+ if (pkg == null) {
+ // May happen if package in on a removable sd card
+ return false;
+ }
+ return pkg.mSigningDetails.hasAncestorOrSelf(mPlatformPackage.mSigningDetails)
+ || mPlatformPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ }
+
+ @Override
public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) {
SigningDetails sd = getSigningDetails(packageName);
if (sd == null) {
@@ -22090,7 +22365,7 @@
}
@Override
- public PackageParser.Package getDisabledPackage(String packageName) {
+ public PackageParser.Package getDisabledSystemPackage(String packageName) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
return (ps != null) ? ps.pkg : null;
@@ -22098,6 +22373,12 @@
}
@Override
+ public @Nullable String getDisabledSystemPackageName(@NonNull String packageName) {
+ PackageParser.Package pkg = getDisabledSystemPackage(packageName);
+ return pkg == null ? null : pkg.packageName;
+ }
+
+ @Override
public String getKnownPackageName(int knownPackage, int userId) {
switch(knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
@@ -22135,21 +22416,6 @@
}
@Override
- public void setSmsAppPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionPolicy.setSmsAppPackagesProvider(provider);
- }
-
- @Override
- public void setDialerAppPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionPolicy.setDialerAppPackagesProvider(provider);
- }
-
- @Override
- public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionPolicy.setSimCallManagerPackagesProvider(provider);
- }
-
- @Override
public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
mDefaultPermissionPolicy.setUseOpenWifiAppPackagesProvider(provider);
}
@@ -22160,22 +22426,10 @@
}
@Override
- public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsApp(packageName, userId);
- }
-
- @Override
- public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
+ public void onDefaultDialerAppChanged(String packageName, int userId) {
synchronized (mPackages) {
mSettings.setDefaultDialerPackageNameLPw(packageName, userId);
}
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerApp(packageName, userId);
- }
-
- @Override
- public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManager(
- packageName, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 361416a..a9f1b5c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2234,6 +2234,9 @@
case "--install-location":
sessionParams.installLocation = Integer.parseInt(getNextArg());
break;
+ case "--install-reason":
+ sessionParams.installReason = Integer.parseInt(getNextArg());
+ break;
case "--force-uuid":
sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
sessionParams.volumeUuid = getNextArg();
@@ -2742,8 +2745,8 @@
pw.println("");
pw.println(" install [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
- pw.println(" [--originating-uri URI] [---referrer URI]");
- pw.println(" [--abi ABI_NAME] [--force-sdk]");
+ pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
+ pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]");
pw.println(" Install an application. Must provide the apk data to install, either as a");
@@ -2768,14 +2771,17 @@
pw.println(" --full: cause the app to be installed as a non-ephemeral full app");
pw.println(" --install-location: force the install location:");
pw.println(" 0=auto, 1=internal only, 2=prefer external");
+ pw.println(" --install-reason: indicates why the app is being installed:");
+ pw.println(" 0=unknown, 1=admin policy, 2=device restore,");
+ pw.println(" 3=device setup, 4=user request");
pw.println(" --force-uuid: force install on to disk volume with given UUID");
pw.println(" --force-sdk: allow install even when existing app targets platform");
pw.println(" codename but new one targets a final API level");
pw.println("");
pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
- pw.println(" [--originating-uri URI] [---referrer URI]");
- pw.println(" [--abi ABI_NAME] [--force-sdk]");
+ pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
+ pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 727fb15..b850613 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -207,4 +207,13 @@
writeUsersInfoToProto(proto, PackageProto.USERS);
proto.end(packageToken);
}
+
+ /** Updates all fields in the current setting from another. */
+ public void updateFrom(PackageSetting other) {
+ super.updateFrom(other);
+ appId = other.appId;
+ pkg = other.pkg;
+ sharedUserId = other.sharedUserId;
+ sharedUser = other.sharedUser;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 138594c..fd6aceb 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,7 +26,6 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
-import android.os.BaseBundle;
import android.os.PersistableBundle;
import android.service.pm.PackageProto;
import android.util.ArraySet;
@@ -109,7 +108,7 @@
// Whether this package is currently stopped, thus can not be
// started until explicitly launched by the user.
- private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
+ private final SparseArray<PackageUserState> mUserState = new SparseArray<>();
/**
* Non-persisted value. During an "upgrade without restart", we need the set
@@ -118,7 +117,7 @@
* restart, this field will be cleared since the classloader would be created
* using the full set of code paths when the package's process is started.
*/
- Set<String> oldCodePaths;
+ Set<String> mOldCodePaths;
/** Package name of the app that installed this package */
String installerPackageName;
@@ -223,7 +222,7 @@
/**
* Makes a shallow copy of the given package settings.
*
- * NOTE: For some fields [such as keySetData, signatures, userState, verificationInfo, etc...],
+ * NOTE: For some fields [such as keySetData, signatures, mUserState, verificationInfo, etc...],
* the original object is copied and a new one is not created.
*/
public void copyFrom(PackageSettingBase orig) {
@@ -244,7 +243,7 @@
keySetData = orig.keySetData;
lastUpdateTime = orig.lastUpdateTime;
legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
- // Intentionally skip oldCodePaths; it's not relevant for copies
+ // Intentionally skip mOldCodePaths; it's not relevant for copies
parentPackageName = orig.parentPackageName;
primaryCpuAbiString = orig.primaryCpuAbiString;
resourcePath = orig.resourcePath;
@@ -253,9 +252,9 @@
signatures = orig.signatures;
timeStamp = orig.timeStamp;
uidError = orig.uidError;
- userState.clear();
- for (int i=0; i<orig.userState.size(); i++) {
- userState.put(orig.userState.keyAt(i), orig.userState.valueAt(i));
+ mUserState.clear();
+ for (int i = 0; i < orig.mUserState.size(); i++) {
+ mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i));
}
verificationInfo = orig.verificationInfo;
versionCode = orig.versionCode;
@@ -271,16 +270,16 @@
}
private PackageUserState modifyUserState(int userId) {
- PackageUserState state = userState.get(userId);
+ PackageUserState state = mUserState.get(userId);
if (state == null) {
state = new PackageUserState();
- userState.put(userId, state);
+ mUserState.put(userId, state);
}
return state;
}
public PackageUserState readUserState(int userId) {
- PackageUserState state = userState.get(userId);
+ PackageUserState state = mUserState.get(userId);
if (state == null) {
return DEFAULT_USER_STATE;
}
@@ -330,7 +329,7 @@
/** Only use for testing. Do NOT use in production code. */
@VisibleForTesting
SparseArray<PackageUserState> getUserState() {
- return userState;
+ return mUserState;
}
boolean isAnyInstalled(int[] users) {
@@ -536,14 +535,14 @@
}
void removeUser(int userId) {
- userState.delete(userId);
+ mUserState.delete(userId);
}
public int[] getNotInstalledUserIds() {
int count = 0;
- int userStateCount = userState.size();
+ int userStateCount = mUserState.size();
for (int i = 0; i < userStateCount; i++) {
- if (userState.valueAt(i).installed == false) {
+ if (!mUserState.valueAt(i).installed) {
count++;
}
}
@@ -551,8 +550,8 @@
int[] excludedUserIds = new int[count];
int idx = 0;
for (int i = 0; i < userStateCount; i++) {
- if (userState.valueAt(i).installed == false) {
- excludedUserIds[idx++] = userState.keyAt(i);
+ if (!mUserState.valueAt(i).installed) {
+ excludedUserIds[idx++] = mUserState.keyAt(i);
}
}
return excludedUserIds;
@@ -591,11 +590,11 @@
}
protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
- int count = userState.size();
+ int count = mUserState.size();
for (int i = 0; i < count; i++) {
final long userToken = proto.start(fieldId);
- final int userId = userState.keyAt(i);
- final PackageUserState state = userState.valueAt(i);
+ final int userId = mUserState.keyAt(i);
+ final PackageUserState state = mUserState.valueAt(i);
proto.write(PackageProto.UserInfoProto.ID, userId);
final int installType;
if (state.instantApp) {
@@ -630,4 +629,48 @@
PackageUserState userState = readUserState(userId);
return userState.harmfulAppWarning;
}
+
+ protected PackageSettingBase updateFrom(PackageSettingBase other) {
+ super.copyFrom(other);
+ this.parentPackageName = other.parentPackageName;
+ this.childPackageNames = other.childPackageNames;
+ this.codePath = other.codePath;
+ this.codePathString = other.codePathString;
+ this.resourcePath = other.resourcePath;
+ this.resourcePathString = other.resourcePathString;
+ this.usesStaticLibraries = other.usesStaticLibraries;
+ this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
+ this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
+ this.primaryCpuAbiString = other.primaryCpuAbiString;
+ this.secondaryCpuAbiString = other.secondaryCpuAbiString;
+ this.cpuAbiOverrideString = other.cpuAbiOverrideString;
+ this.timeStamp = other.timeStamp;
+ this.firstInstallTime = other.firstInstallTime;
+ this.lastUpdateTime = other.lastUpdateTime;
+ this.versionCode = other.versionCode;
+ this.uidError = other.uidError;
+ this.signatures = other.signatures;
+ this.installPermissionsFixed = other.installPermissionsFixed;
+ this.keySetData = other.keySetData;
+ this.installerPackageName = other.installerPackageName;
+ this.isOrphaned = other.isOrphaned;
+ this.volumeUuid = other.volumeUuid;
+ this.categoryHint = other.categoryHint;
+ this.updateAvailable = other.updateAvailable;
+ this.verificationInfo = other.verificationInfo;
+
+ if (mOldCodePaths != null) {
+ if (other.mOldCodePaths != null) {
+ mOldCodePaths.clear();
+ mOldCodePaths.addAll(other.mOldCodePaths);
+ } else {
+ mOldCodePaths = null;
+ }
+ }
+ mUserState.clear();
+ for (int i = 0; i < other.mUserState.size(); i++) {
+ mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
+ }
+ return this;
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6d242f4..5c88e06 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -539,16 +539,18 @@
if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- mDisabledSysPackages.put(name, p);
-
+ final PackageSetting disabled;
if (replaced) {
// a little trick... when we install the new package, we don't
// want to modify the existing PackageSetting for the built-in
- // version. so at this point we need a new PackageSetting that
- // is okay to muck with.
- PackageSetting newp = new PackageSetting(p);
- replacePackageLPw(name, newp);
+ // version. so at this point we make a copy to place into the
+ // disabled set.
+ disabled = new PackageSetting(p);
+ } else {
+ disabled = p;
}
+ mDisabledSysPackages.put(name, disabled);
+
return true;
}
return false;
@@ -1105,19 +1107,6 @@
mInstallerPackages.remove(packageName);
}
- private void replacePackageLPw(String name, PackageSetting newp) {
- final PackageSetting p = mPackages.get(name);
- if (p != null) {
- if (p.sharedUser != null) {
- p.sharedUser.removePackage(p);
- p.sharedUser.addPackage(newp);
- } else {
- replaceUserIdLPw(p.appId, newp);
- }
- }
- mPackages.put(name, newp);
- }
-
private boolean addUserIdLPw(int uid, Object obj, Object name) {
if (uid > Process.LAST_APPLICATION_UID) {
return false;
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index c94d209..1a8b2af 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -24,7 +24,6 @@
import android.util.proto.ProtoOutputStream;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
/**
@@ -69,24 +68,26 @@
proto.end(token);
}
- void removePackage(PackageSetting packageSetting) {
- if (packages.remove(packageSetting)) {
- // recalculate the pkgFlags for this shared user if needed
- if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
- int aggregatedFlags = uidFlags;
- for (PackageSetting ps : packages) {
- aggregatedFlags |= ps.pkgFlags;
- }
- setFlags(aggregatedFlags);
- }
- if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
- int aggregatedPrivateFlags = uidPrivateFlags;
- for (PackageSetting ps : packages) {
- aggregatedPrivateFlags |= ps.pkgPrivateFlags;
- }
- setPrivateFlags(aggregatedPrivateFlags);
- }
+ boolean removePackage(PackageSetting packageSetting) {
+ if (!packages.remove(packageSetting)) {
+ return false;
}
+ // recalculate the pkgFlags for this shared user if needed
+ if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
+ int aggregatedFlags = uidFlags;
+ for (PackageSetting ps : packages) {
+ aggregatedFlags |= ps.pkgFlags;
+ }
+ setFlags(aggregatedFlags);
+ }
+ if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
+ int aggregatedPrivateFlags = uidPrivateFlags;
+ for (PackageSetting ps : packages) {
+ aggregatedPrivateFlags |= ps.pkgPrivateFlags;
+ }
+ setPrivateFlags(aggregatedPrivateFlags);
+ }
+ return true;
}
void addPackage(PackageSetting packageSetting) {
@@ -143,4 +144,16 @@
}
}
+ /** Updates all fields in this shared user setting from another. */
+ public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
+ copyFrom(sharedUser);
+ this.userId = sharedUser.userId;
+ this.uidFlags = sharedUser.uidFlags;
+ this.uidPrivateFlags = sharedUser.uidPrivateFlags;
+ this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion;
+ this.packages.clear();
+ this.packages.addAll(sharedUser.packages);
+ this.signaturesChanged = sharedUser.signaturesChanged;
+ return this;
+ }
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 846c7b7..3c9dd63 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -23,12 +23,13 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.DownloadManager;
+import android.app.SearchManager;
import android.app.admin.DevicePolicyManager;
import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageList;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackagesProvider;
@@ -53,6 +54,7 @@
import android.provider.MediaStore;
import android.provider.Telephony.Sms.Intents;
import android.security.Credentials;
+import android.speech.RecognitionService;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -61,6 +63,7 @@
import android.util.Slog;
import android.util.Xml;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
@@ -73,6 +76,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -94,10 +98,15 @@
private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
private static final boolean DEBUG = false;
- private static final int DEFAULT_FLAGS =
+ @PackageManager.ResolveInfoFlags
+ private static final int DEFAULT_INTENT_QUERY_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ @PackageManager.PackageInfoFlags
+ private static final int DEFAULT_PACKAGE_INFO_QUERY_FLAGS =
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS;
+
private static final String AUDIO_MIME_TYPE = "audio/mpeg";
private static final String TAG_EXCEPTIONS = "exceptions";
@@ -108,6 +117,8 @@
private static final String ATTR_FIXED = "fixed";
private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
+
+
static {
PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
@@ -219,7 +230,7 @@
private final DefaultPermissionGrantedCallback mPermissionGrantedCallback;
public interface DefaultPermissionGrantedCallback {
/** Callback when permissions have been granted */
- public void onDefaultRuntimePermissionsGranted(int userId);
+ void onDefaultRuntimePermissionsGranted(int userId);
}
public DefaultPermissionGrantPolicy(Context context, Looper looper,
@@ -291,9 +302,9 @@
grantDefaultPermissionExceptions(userId);
}
- private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
+ private void grantRuntimePermissionsForSystemPackage(int userId, PackageInfo pkg) {
Set<String> permissions = new ArraySet<>();
- for (String permission : pkg.requestedPermissions) {
+ for (String permission : pkg.requestedPermissions) {
final BasePermission bp = mPermissionManager.getPermission(permission);
if (bp == null) {
continue;
@@ -307,36 +318,71 @@
}
}
- private void grantAllRuntimePermissions(int userId) {
- Log.i(TAG, "Granting all runtime permissions for user " + userId);
- final PackageList packageList = mServiceInternal.getPackageList();
- for (String packageName : packageList.getPackageNames()) {
- final PackageParser.Package pkg = mServiceInternal.getPackage(packageName);
- if (pkg == null) {
- continue;
- }
- grantRuntimePermissionsForPackage(userId, pkg);
- }
- }
-
public void scheduleReadDefaultPermissionExceptions() {
mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
}
private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
Log.i(TAG, "Granting permissions to platform components for user " + userId);
- final PackageList packageList = mServiceInternal.getPackageList();
- for (String packageName : packageList.getPackageNames()) {
- final PackageParser.Package pkg = mServiceInternal.getPackage(packageName);
+ List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser(
+ DEFAULT_PACKAGE_INFO_QUERY_FLAGS, UserHandle.USER_SYSTEM);
+ for (PackageInfo pkg : packages) {
if (pkg == null) {
continue;
}
if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
|| !doesPackageSupportRuntimePermissions(pkg)
- || pkg.requestedPermissions.isEmpty()) {
+ || ArrayUtils.isEmpty(pkg.requestedPermissions)) {
continue;
}
- grantRuntimePermissionsForPackage(userId, pkg);
+ grantRuntimePermissionsForSystemPackage(userId, pkg);
+ }
+ }
+
+ @SafeVarargs
+ private final void grantIgnoringSystemPackage(String packageName, int userId,
+ Set<String>... permissionGroups) {
+ grantPermissionsToSystemPackage(packageName, userId, false, true, permissionGroups);
+ }
+
+ @SafeVarargs
+ private final void grantSystemFixedPermissionsToSystemPackage(String packageName, int userId,
+ Set<String>... permissionGroups) {
+ grantPermissionsToSystemPackage(packageName, userId, true, false, permissionGroups);
+ }
+
+ @SafeVarargs
+ private final void grantPermissionsToSystemPackage(
+ String packageName, int userId, Set<String>... permissionGroups) {
+ grantPermissionsToSystemPackage(packageName, userId, false, false, permissionGroups);
+ }
+
+ @SafeVarargs
+ private final void grantPermissionsToSystemPackage(String packageName, int userId,
+ boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
+ if (!ignoreSystemPackage && !isSystemPackage(packageName)) {
+ return;
+ }
+ grantRuntimePermissionsToPackage(getSystemPackageInfo(packageName),
+ userId, systemFixed, ignoreSystemPackage, permissionGroups);
+ }
+
+ @SafeVarargs
+ private final void grantRuntimePermissionsToPackage(String packageName, int userId,
+ boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
+ grantRuntimePermissionsToPackage(getPackageInfo(packageName),
+ userId, systemFixed, ignoreSystemPackage, permissionGroups);
+ }
+
+ @SafeVarargs
+ private final void grantRuntimePermissionsToPackage(PackageInfo packageName, int userId,
+ boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
+ if (packageName == null) return;
+ if (doesPackageSupportRuntimePermissions(packageName)) {
+ for (Set<String> permissionGroup : permissionGroups) {
+ grantRuntimePermissions(packageName, permissionGroup, systemFixed,
+ ignoreSystemPackage, userId);
+ }
}
}
@@ -379,594 +425,373 @@
syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
// Installer
- final String installerPackageName = mServiceInternal.getKnownPackageName(
- PackageManagerInternal.PACKAGE_INSTALLER, userId);
- PackageParser.Package installerPackage = getSystemPackage(installerPackageName);
- if (installerPackage != null
- && doesPackageSupportRuntimePermissions(installerPackage)) {
- grantRuntimePermissions(installerPackage, STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getKnownPackage(PackageManagerInternal.PACKAGE_INSTALLER, userId),
+ userId, STORAGE_PERMISSIONS);
// Verifier
- final String verifierPackageName = mServiceInternal.getKnownPackageName(
- PackageManagerInternal.PACKAGE_VERIFIER, userId);
- PackageParser.Package verifierPackage = getSystemPackage(verifierPackageName);
- if (verifierPackage != null
- && doesPackageSupportRuntimePermissions(verifierPackage)) {
- grantRuntimePermissions(verifierPackage, STORAGE_PERMISSIONS, true, userId);
- grantRuntimePermissions(verifierPackage, PHONE_PERMISSIONS, false, userId);
- grantRuntimePermissions(verifierPackage, SMS_PERMISSIONS, false, userId);
- }
+ final String verifier = getKnownPackage(PackageManagerInternal.PACKAGE_VERIFIER, userId);
+ grantSystemFixedPermissionsToSystemPackage(verifier, userId, STORAGE_PERMISSIONS);
+ grantPermissionsToSystemPackage(verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
// SetupWizard
- final String setupWizardPackageName = mServiceInternal.getKnownPackageName(
- PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId);
- PackageParser.Package setupPackage = getSystemPackage(setupWizardPackageName);
- if (setupPackage != null
- && doesPackageSupportRuntimePermissions(setupPackage)) {
- grantRuntimePermissions(setupPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissions(setupPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(setupPackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissions(setupPackage, CAMERA_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getKnownPackage(PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId), userId,
+ PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
// Camera
- Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackage(
- cameraIntent, userId);
- if (cameraPackage != null
- && doesPackageSupportRuntimePermissions(cameraPackage)) {
- grantRuntimePermissions(cameraPackage, CAMERA_PERMISSIONS, userId);
- grantRuntimePermissions(cameraPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissions(cameraPackage, STORAGE_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(MediaStore.ACTION_IMAGE_CAPTURE, userId),
+ userId, CAMERA_PERMISSIONS, MICROPHONE_PERMISSIONS, STORAGE_PERMISSIONS);
// Media provider
- PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackage(
- MediaStore.AUTHORITY, userId);
- if (mediaStorePackage != null) {
- grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
- grantRuntimePermissions(mediaStorePackage, MEDIA_AURAL_PERMISSIONS, true, userId);
- grantRuntimePermissions(mediaStorePackage, MEDIA_VISUAL_PERMISSIONS, true, userId);
- grantRuntimePermissions(mediaStorePackage, PHONE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultProviderAuthorityPackage(MediaStore.AUTHORITY, userId), userId,
+ STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS, MEDIA_VISUAL_PERMISSIONS,
+ PHONE_PERMISSIONS);
// Downloads provider
- PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackage(
- "downloads", userId);
- if (downloadsPackage != null) {
- grantRuntimePermissions(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultProviderAuthorityPackage("downloads", userId), userId,
+ STORAGE_PERMISSIONS);
// Downloads UI
- Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
- PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackage(
- downloadsUiIntent, userId);
- if (downloadsUiPackage != null
- && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
- grantRuntimePermissions(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(
+ DownloadManager.ACTION_VIEW_DOWNLOADS, userId),
+ userId, STORAGE_PERMISSIONS);
// Storage provider
- PackageParser.Package storagePackage = getDefaultProviderAuthorityPackage(
- "com.android.externalstorage.documents", userId);
- if (storagePackage != null) {
- grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultProviderAuthorityPackage("com.android.externalstorage.documents", userId),
+ userId, STORAGE_PERMISSIONS);
// CertInstaller
- Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
- PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage(
- certInstallerIntent, userId);
- if (certInstallerPackage != null
- && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
- grantRuntimePermissions(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(Credentials.INSTALL_ACTION, userId), userId,
+ STORAGE_PERMISSIONS);
// Dialer
if (dialerAppPackageNames == null) {
- Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
- PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackage(
- dialerIntent, userId);
- if (dialerPackage != null) {
- grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
- }
+ String dialerPackage =
+ getDefaultSystemHandlerActivityPackage(Intent.ACTION_DIAL, userId);
+ grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
} else {
for (String dialerAppPackageName : dialerAppPackageNames) {
- PackageParser.Package dialerPackage = getSystemPackage(dialerAppPackageName);
- if (dialerPackage != null) {
- grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
- }
+ grantDefaultPermissionsToDefaultSystemDialerApp(dialerAppPackageName, userId);
}
}
// Sim call manager
if (simCallManagerPackageNames != null) {
for (String simCallManagerPackageName : simCallManagerPackageNames) {
- PackageParser.Package simCallManagerPackage =
- getSystemPackage(simCallManagerPackageName);
- if (simCallManagerPackage != null) {
- grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage,
- userId);
- }
+ grantDefaultPermissionsToDefaultSystemSimCallManager(
+ simCallManagerPackageName, userId);
}
}
// Use Open Wifi
if (useOpenWifiAppPackageNames != null) {
for (String useOpenWifiPackageName : useOpenWifiAppPackageNames) {
- PackageParser.Package useOpenWifiPackage =
- getSystemPackage(useOpenWifiPackageName);
- if (useOpenWifiPackage != null) {
- grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(useOpenWifiPackage,
- userId);
- }
+ grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
+ useOpenWifiPackageName, userId);
}
}
// SMS
if (smsAppPackageNames == null) {
- Intent smsIntent = new Intent(Intent.ACTION_MAIN);
- smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
- PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackage(
- smsIntent, userId);
- if (smsPackage != null) {
- grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
- }
+ String smsPackage = getDefaultSystemHandlerActivityPackageForCategory(
+ Intent.CATEGORY_APP_MESSAGING, userId);
+ grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
} else {
- for (String smsPackageName : smsAppPackageNames) {
- PackageParser.Package smsPackage = getSystemPackage(smsPackageName);
- if (smsPackage != null) {
- grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
- }
+ for (String smsPackage : smsAppPackageNames) {
+ grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
}
}
// Cell Broadcast Receiver
- Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
- PackageParser.Package cbrPackage =
- getDefaultSystemHandlerActivityPackage(cbrIntent, userId);
- if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
- grantRuntimePermissions(cbrPackage, SMS_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(Intents.SMS_CB_RECEIVED_ACTION, userId),
+ userId, SMS_PERMISSIONS);
// Carrier Provisioning Service
- Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
- PackageParser.Package carrierProvPackage =
- getDefaultSystemHandlerServicePackage(carrierProvIntent, userId);
- if (carrierProvPackage != null
- && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
- grantRuntimePermissions(carrierProvPackage, SMS_PERMISSIONS, false, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerServicePackage(Intents.SMS_CARRIER_PROVISION_ACTION, userId),
+ userId, SMS_PERMISSIONS);
// Calendar
- Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
- calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
- PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackage(
- calendarIntent, userId);
- if (calendarPackage != null
- && doesPackageSupportRuntimePermissions(calendarPackage)) {
- grantRuntimePermissions(calendarPackage, CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissions(calendarPackage, CONTACTS_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackageForCategory(
+ Intent.CATEGORY_APP_CALENDAR, userId),
+ userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS);
// Calendar provider
- PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackage(
- CalendarContract.AUTHORITY, userId);
- if (calendarProviderPackage != null) {
- grantRuntimePermissions(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(calendarProviderPackage, CALENDAR_PERMISSIONS,
- true, userId);
- grantRuntimePermissions(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
- }
+ String calendarProvider =
+ getDefaultProviderAuthorityPackage(CalendarContract.AUTHORITY, userId);
+ grantPermissionsToSystemPackage(calendarProvider, userId,
+ CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
+ grantSystemFixedPermissionsToSystemPackage(calendarProvider, userId, CALENDAR_PERMISSIONS);
// Calendar provider sync adapters
- List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackages(
- calendarSyncAdapterPackages, userId);
- final int calendarSyncAdapterCount = calendarSyncAdapters.size();
- for (int i = 0; i < calendarSyncAdapterCount; i++) {
- PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
- if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
- grantRuntimePermissions(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
- }
- }
+ grantPermissionToEachSystemPackage(
+ getHeadlessSyncAdapterPackages(calendarSyncAdapterPackages, userId),
+ userId, CALENDAR_PERMISSIONS);
// Contacts
- Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
- contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
- PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackage(
- contactsIntent, userId);
- if (contactsPackage != null
- && doesPackageSupportRuntimePermissions(contactsPackage)) {
- grantRuntimePermissions(contactsPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(contactsPackage, PHONE_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackageForCategory(
+ Intent.CATEGORY_APP_CONTACTS, userId),
+ userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
// Contacts provider sync adapters
- List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackages(
- contactsSyncAdapterPackages, userId);
- final int contactsSyncAdapterCount = contactsSyncAdapters.size();
- for (int i = 0; i < contactsSyncAdapterCount; i++) {
- PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
- if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
- grantRuntimePermissions(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
- }
- }
+ grantPermissionToEachSystemPackage(
+ getHeadlessSyncAdapterPackages(contactsSyncAdapterPackages, userId),
+ userId, CONTACTS_PERMISSIONS);
// Contacts provider
- PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackage(
- ContactsContract.AUTHORITY, userId);
- if (contactsProviderPackage != null) {
- grantRuntimePermissions(contactsProviderPackage, CONTACTS_PERMISSIONS,
- true, userId);
- grantRuntimePermissions(contactsProviderPackage, PHONE_PERMISSIONS,
- true, userId);
- grantRuntimePermissions(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
- }
+ String contactsProviderPackage =
+ getDefaultProviderAuthorityPackage(ContactsContract.AUTHORITY, userId);
+ grantSystemFixedPermissionsToSystemPackage(contactsProviderPackage, userId,
+ CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
+ grantPermissionsToSystemPackage(contactsProviderPackage, userId, STORAGE_PERMISSIONS);
// Device provisioning
- Intent deviceProvisionIntent = new Intent(
- DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
- PackageParser.Package deviceProvisionPackage =
- getDefaultSystemHandlerActivityPackage(deviceProvisionIntent, userId);
- if (deviceProvisionPackage != null
- && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
- grantRuntimePermissions(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(
+ DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, userId),
+ userId, CONTACTS_PERMISSIONS);
// Maps
- Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
- mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
- PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackage(
- mapsIntent, userId);
- if (mapsPackage != null
- && doesPackageSupportRuntimePermissions(mapsPackage)) {
- grantRuntimePermissions(mapsPackage, LOCATION_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackageForCategory(Intent.CATEGORY_APP_MAPS, userId),
+ userId, LOCATION_PERMISSIONS);
// Gallery
- Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
- galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
- PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackage(
- galleryIntent, userId);
- if (galleryPackage != null
- && doesPackageSupportRuntimePermissions(galleryPackage)) {
- grantRuntimePermissions(galleryPackage, STORAGE_PERMISSIONS, userId);
- grantRuntimePermissions(galleryPackage, MEDIA_VISUAL_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackageForCategory(
+ Intent.CATEGORY_APP_GALLERY, userId),
+ userId, STORAGE_PERMISSIONS, MEDIA_VISUAL_PERMISSIONS);
// Email
- Intent emailIntent = new Intent(Intent.ACTION_MAIN);
- emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
- PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackage(
- emailIntent, userId);
- if (emailPackage != null
- && doesPackageSupportRuntimePermissions(emailPackage)) {
- grantRuntimePermissions(emailPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(emailPackage, CALENDAR_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackageForCategory(
+ Intent.CATEGORY_APP_EMAIL, userId),
+ userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS);
// Browser
- PackageParser.Package browserPackage = null;
- String defaultBrowserPackage = mServiceInternal.getKnownPackageName(
- PackageManagerInternal.PACKAGE_BROWSER, userId);
- if (defaultBrowserPackage != null) {
- browserPackage = getPackage(defaultBrowserPackage);
- }
+ String browserPackage = getKnownPackage(PackageManagerInternal.PACKAGE_BROWSER, userId);
if (browserPackage == null) {
- Intent browserIntent = new Intent(Intent.ACTION_MAIN);
- browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
- browserPackage = getDefaultSystemHandlerActivityPackage(
- browserIntent, userId);
+ browserPackage = getDefaultSystemHandlerActivityPackageForCategory(
+ Intent.CATEGORY_APP_BROWSER, userId);
+ if (!isSystemPackage(browserPackage)) {
+ browserPackage = null;
+ }
}
- if (browserPackage != null
- && doesPackageSupportRuntimePermissions(browserPackage)) {
- grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, userId);
- }
+ grantRuntimePermissionsToPackage(browserPackage, userId,
+ false /* systemFixed */, false /* ignoreSystemPackage */,
+ LOCATION_PERMISSIONS);
// Voice interaction
if (voiceInteractPackageNames != null) {
for (String voiceInteractPackageName : voiceInteractPackageNames) {
- PackageParser.Package voiceInteractPackage = getSystemPackage(
- voiceInteractPackageName);
- if (voiceInteractPackage != null
- && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
- grantRuntimePermissions(voiceInteractPackage,
- CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(voiceInteractPackage,
- CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissions(voiceInteractPackage,
- MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissions(voiceInteractPackage,
- PHONE_PERMISSIONS, userId);
- grantRuntimePermissions(voiceInteractPackage,
- SMS_PERMISSIONS, userId);
- grantRuntimePermissions(voiceInteractPackage,
- LOCATION_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(voiceInteractPackageName, userId,
+ CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
+ PHONE_PERMISSIONS, SMS_PERMISSIONS, LOCATION_PERMISSIONS);
}
}
if (ActivityManager.isLowRamDeviceStatic()) {
// Allow voice search on low-ram devices
- Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
- PackageParser.Package globalSearchPickerPackage =
- getDefaultSystemHandlerActivityPackage(globalSearchIntent, userId);
-
- if (globalSearchPickerPackage != null
- && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
- grantRuntimePermissions(globalSearchPickerPackage,
- MICROPHONE_PERMISSIONS, false, userId);
- grantRuntimePermissions(globalSearchPickerPackage,
- LOCATION_PERMISSIONS, false, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(
+ SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
+ userId, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS);
}
// Voice recognition
- Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
- voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
- PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackage(
- voiceRecoIntent, userId);
- if (voiceRecoPackage != null
- && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
- grantRuntimePermissions(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
- }
+ Intent voiceRecoIntent = new Intent(RecognitionService.SERVICE_INTERFACE)
+ .addCategory(Intent.CATEGORY_DEFAULT);
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerServicePackage(voiceRecoIntent, userId), userId,
+ MICROPHONE_PERMISSIONS);
// Location
if (locationPackageNames != null) {
for (String packageName : locationPackageNames) {
- PackageParser.Package locationPackage = getSystemPackage(packageName);
- if (locationPackage != null
- && doesPackageSupportRuntimePermissions(locationPackage)) {
- grantRuntimePermissions(locationPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(locationPackage, CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissions(locationPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissions(locationPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissions(locationPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissions(locationPackage, LOCATION_PERMISSIONS,
- true, userId);
- grantRuntimePermissions(locationPackage, CAMERA_PERMISSIONS, userId);
- grantRuntimePermissions(locationPackage, SENSORS_PERMISSIONS, userId);
- grantRuntimePermissions(locationPackage, STORAGE_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(packageName, userId,
+ CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
+ PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
+ SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
+ grantSystemFixedPermissionsToSystemPackage(packageName, userId,
+ LOCATION_PERMISSIONS);
}
}
// Music
- Intent musicIntent = new Intent(Intent.ACTION_VIEW);
- musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
- musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
- AUDIO_MIME_TYPE);
- PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackage(
- musicIntent, userId);
- if (musicPackage != null
- && doesPackageSupportRuntimePermissions(musicPackage)) {
- grantRuntimePermissions(musicPackage, STORAGE_PERMISSIONS, userId);
- grantRuntimePermissions(musicPackage, MEDIA_AURAL_PERMISSIONS, userId);
- }
+ Intent musicIntent = new Intent(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .setDataAndType(Uri.fromFile(new File("foo.mp3")), AUDIO_MIME_TYPE);
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(musicIntent, userId), userId,
+ STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS);
// Home
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
- PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackage(
- homeIntent, userId);
- if (homePackage != null
- && doesPackageSupportRuntimePermissions(homePackage)) {
- grantRuntimePermissions(homePackage, LOCATION_PERMISSIONS, false, userId);
- }
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .addCategory(Intent.CATEGORY_LAUNCHER_APP);
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(homeIntent, userId), userId,
+ LOCATION_PERMISSIONS);
// Watches
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
// Home application on watches
- Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
- wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
- PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackage(
- wearHomeIntent, userId);
-
- if (wearHomePackage != null
- && doesPackageSupportRuntimePermissions(wearHomePackage)) {
- grantRuntimePermissions(wearHomePackage, CONTACTS_PERMISSIONS, false,
- userId);
- grantRuntimePermissions(wearHomePackage, PHONE_PERMISSIONS, true, userId);
- grantRuntimePermissions(wearHomePackage, MICROPHONE_PERMISSIONS, false,
- userId);
- grantRuntimePermissions(wearHomePackage, LOCATION_PERMISSIONS, false,
- userId);
- }
+ String wearPackage = getDefaultSystemHandlerActivityPackageForCategory(
+ Intent.CATEGORY_HOME_MAIN, userId);
+ grantPermissionsToSystemPackage(wearPackage, userId,
+ CONTACTS_PERMISSIONS, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS);
+ grantSystemFixedPermissionsToSystemPackage(wearPackage, userId, PHONE_PERMISSIONS);
// Fitness tracking on watches
- Intent trackIntent = new Intent(ACTION_TRACK);
- PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackage(
- trackIntent, userId);
- if (trackPackage != null
- && doesPackageSupportRuntimePermissions(trackPackage)) {
- grantRuntimePermissions(trackPackage, SENSORS_PERMISSIONS, false, userId);
- grantRuntimePermissions(trackPackage, LOCATION_PERMISSIONS, false, userId);
- }
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(ACTION_TRACK, userId), userId,
+ SENSORS_PERMISSIONS, LOCATION_PERMISSIONS);
}
// Print Spooler
- PackageParser.Package printSpoolerPackage = getSystemPackage(
- PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
- if (printSpoolerPackage != null
- && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
- grantRuntimePermissions(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(PrintManager.PRINT_SPOOLER_PACKAGE_NAME, userId,
+ LOCATION_PERMISSIONS);
// EmergencyInfo
- Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
- PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackage(
- emergencyInfoIntent, userId);
- if (emergencyInfoPckg != null
- && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
- grantRuntimePermissions(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
- grantRuntimePermissions(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(
+ TelephonyManager.ACTION_EMERGENCY_ASSISTANCE, userId),
+ userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
// NFC Tag viewer
- Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
- nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
- PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackage(
- nfcTagIntent, userId);
- if (nfcTagPkg != null
- && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
- grantRuntimePermissions(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
- grantRuntimePermissions(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
- }
+ Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW)
+ .setType("vnd.android.cursor.item/ndef_msg");
+ grantPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(nfcTagIntent, userId), userId,
+ CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
// Storage Manager
- Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
- PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackage(
- storageManagerIntent, userId);
- if (storageManagerPckg != null
- && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
- grantRuntimePermissions(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(
+ StorageManager.ACTION_MANAGE_STORAGE, userId),
+ userId, STORAGE_PERMISSIONS);
// Companion devices
- PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackage(
- CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
- if (companionDeviceDiscoveryPackage != null
- && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
- grantRuntimePermissions(companionDeviceDiscoveryPackage,
- LOCATION_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId,
+ LOCATION_PERMISSIONS);
// Ringtone Picker
- Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
- PackageParser.Package ringtonePickerPackage =
- getDefaultSystemHandlerActivityPackage(ringtonePickerIntent, userId);
- if (ringtonePickerPackage != null
- && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
- grantRuntimePermissions(ringtonePickerPackage,
- STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(
+ getDefaultSystemHandlerActivityPackage(
+ RingtoneManager.ACTION_RINGTONE_PICKER, userId),
+ userId, STORAGE_PERMISSIONS);
// TextClassifier Service
String textClassifierPackageName =
mContext.getPackageManager().getSystemTextClassifierPackageName();
if (!TextUtils.isEmpty(textClassifierPackageName)) {
- PackageParser.Package textClassifierPackage =
- getSystemPackage(textClassifierPackageName);
- if (textClassifierPackage != null
- && doesPackageSupportRuntimePermissions(textClassifierPackage)) {
- grantRuntimePermissions(textClassifierPackage, PHONE_PERMISSIONS, false, userId);
- grantRuntimePermissions(textClassifierPackage, SMS_PERMISSIONS, false, userId);
- grantRuntimePermissions(textClassifierPackage, CALENDAR_PERMISSIONS, false, userId);
- grantRuntimePermissions(textClassifierPackage, LOCATION_PERMISSIONS, false, userId);
- grantRuntimePermissions(textClassifierPackage, CONTACTS_PERMISSIONS, false, userId);
- }
+ grantPermissionsToSystemPackage(textClassifierPackageName, userId,
+ PHONE_PERMISSIONS, SMS_PERMISSIONS, CALENDAR_PERMISSIONS,
+ LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
// There is no real "marker" interface to identify the shared storage backup, it is
// hardcoded in BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE.
- PackageParser.Package sharedStorageBackupPackage = getSystemPackage(
- "com.android.sharedstoragebackup");
- if (sharedStorageBackupPackage != null) {
- grantRuntimePermissions(sharedStorageBackupPackage, STORAGE_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage("com.android.sharedstoragebackup", userId,
+ STORAGE_PERMISSIONS);
if (mPermissionGrantedCallback != null) {
mPermissionGrantedCallback.onDefaultRuntimePermissionsGranted(userId);
}
}
- private void grantDefaultPermissionsToDefaultSystemDialerApp(
- PackageParser.Package dialerPackage, int userId) {
- if (doesPackageSupportRuntimePermissions(dialerPackage)) {
- boolean isPhonePermFixed =
- mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
- grantRuntimePermissions(
- dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
- grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, userId);
+ private String getDefaultSystemHandlerActivityPackageForCategory(String category, int userId) {
+ return getDefaultSystemHandlerActivityPackage(
+ new Intent(Intent.ACTION_MAIN).addCategory(category), userId);
+ }
+
+ @SafeVarargs
+ private final void grantPermissionToEachSystemPackage(
+ ArrayList<String> packages, int userId, Set<String>... permissions) {
+ if (packages == null) return;
+ final int count = packages.size();
+ for (int i = 0; i < count; i++) {
+ grantPermissionsToSystemPackage(packages.get(i), userId, permissions);
}
}
- private void grantDefaultPermissionsToDefaultSystemSmsApp(
- PackageParser.Package smsPackage, int userId) {
- if (doesPackageSupportRuntimePermissions(smsPackage)) {
- grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, userId);
- grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, userId);
+ private String getKnownPackage(int knownPkgId, int userId) {
+ return mServiceInternal.getKnownPackageName(knownPkgId, userId);
+ }
+
+ private void grantDefaultPermissionsToDefaultSystemDialerApp(
+ String dialerPackage, int userId) {
+ if (dialerPackage == null) {
+ return;
}
+ boolean isPhonePermFixed =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
+ if (isPhonePermFixed) {
+ grantSystemFixedPermissionsToSystemPackage(dialerPackage, userId, PHONE_PERMISSIONS);
+ } else {
+ grantPermissionsToSystemPackage(dialerPackage, userId, PHONE_PERMISSIONS);
+ }
+ grantPermissionsToSystemPackage(dialerPackage, userId,
+ CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+ }
+
+ private void grantDefaultPermissionsToDefaultSystemSmsApp(String smsPackage, int userId) {
+ grantPermissionsToSystemPackage(smsPackage, userId,
+ PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
+ STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
}
private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
- PackageParser.Package useOpenWifiPackage, int userId) {
- if (doesPackageSupportRuntimePermissions(useOpenWifiPackage)) {
- grantRuntimePermissions(useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, userId);
- }
+ String useOpenWifiPackage, int userId) {
+ grantPermissionsToSystemPackage(
+ useOpenWifiPackage, userId, COARSE_LOCATION_PERMISSIONS);
}
public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package smsPackage = getPackage(packageName);
- if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
- grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
- }
+ grantIgnoringSystemPackage(packageName, userId,
+ PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS, STORAGE_PERMISSIONS,
+ MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
}
public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
+ mServiceInternal.onDefaultDialerAppChanged(packageName, userId);
Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package dialerPackage = getPackage(packageName);
- if (dialerPackage != null
- && doesPackageSupportRuntimePermissions(dialerPackage)) {
- grantRuntimePermissions(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
- }
+ grantIgnoringSystemPackage(packageName, userId,
+ PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
+ MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
}
public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
Log.i(TAG, "Granting permissions to default Use Open WiFi app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package useOpenWifiPackage = getPackage(packageName);
- if (useOpenWifiPackage != null
- && doesPackageSupportRuntimePermissions(useOpenWifiPackage)) {
- grantRuntimePermissions(
- useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, false, true, userId);
- }
- }
-
- private void grantDefaultPermissionsToDefaultSimCallManager(
- PackageParser.Package simCallManagerPackage, int userId) {
- Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
- if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
- grantRuntimePermissions(simCallManagerPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissions(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
- }
+ grantIgnoringSystemPackage(packageName, userId, COARSE_LOCATION_PERMISSIONS);
}
public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
if (packageName == null) {
return;
}
- PackageParser.Package simCallManagerPackage = getPackage(packageName);
- if (simCallManagerPackage != null) {
- grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage, userId);
+ Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
+ grantRuntimePermissionsToPackage(packageName, userId, false, false,
+ PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS);
+ }
+
+ private void grantDefaultPermissionsToDefaultSystemSimCallManager(
+ String packageName, int userId) {
+ if (isSystemPackage(packageName)) {
+ grantDefaultPermissionsToDefaultSimCallManager(packageName, userId);
}
}
@@ -976,13 +801,8 @@
return;
}
for (String packageName : packageNames) {
- PackageParser.Package carrierPackage = getSystemPackage(packageName);
- if (carrierPackage != null
- && doesPackageSupportRuntimePermissions(carrierPackage)) {
- grantRuntimePermissions(carrierPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissions(carrierPackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissions(carrierPackage, SMS_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(packageName, userId,
+ PHONE_PERMISSIONS, LOCATION_PERMISSIONS, SMS_PERMISSIONS);
}
}
@@ -992,15 +812,9 @@
return;
}
for (String packageName : packageNames) {
- PackageParser.Package imsServicePackage = getSystemPackage(packageName);
- if (imsServicePackage != null
- && doesPackageSupportRuntimePermissions(imsServicePackage)) {
- grantRuntimePermissions(imsServicePackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissions(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissions(imsServicePackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissions(imsServicePackage, CAMERA_PERMISSIONS, userId);
- grantRuntimePermissions(imsServicePackage, CONTACTS_PERMISSIONS, userId);
- }
+ grantPermissionsToSystemPackage(packageName, userId,
+ PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS,
+ CAMERA_PERMISSIONS, CONTACTS_PERMISSIONS);
}
}
@@ -1011,15 +825,12 @@
return;
}
for (String packageName : packageNames) {
- PackageParser.Package dataServicePackage = getSystemPackage(packageName);
- if (dataServicePackage != null
- && doesPackageSupportRuntimePermissions(dataServicePackage)) {
- // Grant these permissions as system-fixed, so that nobody can accidentally
- // break cellular data.
- grantRuntimePermissions(dataServicePackage, PHONE_PERMISSIONS, true, userId);
- grantRuntimePermissions(dataServicePackage, LOCATION_PERMISSIONS, true, userId);
- }
+ // Grant these permissions as system-fixed, so that nobody can accidentally
+ // break cellular data.
+ grantSystemFixedPermissionsToSystemPackage(packageName, userId,
+ PHONE_PERMISSIONS, LOCATION_PERMISSIONS);
}
+
}
public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
@@ -1029,25 +840,17 @@
return;
}
for (String packageName : packageNames) {
- PackageParser.Package dataServicePackage = getSystemPackage(packageName);
- if (dataServicePackage != null
- && doesPackageSupportRuntimePermissions(dataServicePackage)) {
- revokeRuntimePermissions(dataServicePackage, PHONE_PERMISSIONS, true, userId);
- revokeRuntimePermissions(dataServicePackage, LOCATION_PERMISSIONS, true, userId);
+ PackageInfo pkg = getSystemPackageInfo(packageName);
+ if (isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
+ revokeRuntimePermissions(packageName, PHONE_PERMISSIONS, true, userId);
+ revokeRuntimePermissions(packageName, LOCATION_PERMISSIONS, true, userId);
}
}
}
public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
Log.i(TAG, "Granting permissions to active LUI app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package luiAppPackage = getSystemPackage(packageName);
- if (luiAppPackage != null
- && doesPackageSupportRuntimePermissions(luiAppPackage)) {
- grantRuntimePermissions(luiAppPackage, CAMERA_PERMISSIONS, true, userId);
- }
+ grantSystemFixedPermissionsToSystemPackage(packageName, userId, CAMERA_PERMISSIONS);
}
public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
@@ -1056,123 +859,116 @@
return;
}
for (String packageName : packageNames) {
- PackageParser.Package luiAppPackage = getSystemPackage(packageName);
- if (luiAppPackage != null
- && doesPackageSupportRuntimePermissions(luiAppPackage)) {
- revokeRuntimePermissions(luiAppPackage, CAMERA_PERMISSIONS, true, userId);
+ PackageInfo pkg = getSystemPackageInfo(packageName);
+ if (isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
+ revokeRuntimePermissions(packageName, CAMERA_PERMISSIONS, true, userId);
}
}
}
public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
Log.i(TAG, "Granting permissions to default browser for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package browserPackage = getSystemPackage(packageName);
- if (browserPackage != null
- && doesPackageSupportRuntimePermissions(browserPackage)) {
- grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
- }
+ grantPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS);
}
- private PackageParser.Package getDefaultSystemHandlerActivityPackage(
- Intent intent, int userId) {
+ private String getDefaultSystemHandlerActivityPackage(String intentAction, int userId) {
+ return getDefaultSystemHandlerActivityPackage(new Intent(intentAction), userId);
+ }
+
+ private String getDefaultSystemHandlerActivityPackage(Intent intent, int userId) {
ResolveInfo handler = mServiceInternal.resolveIntent(intent,
- intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false,
- Binder.getCallingUid());
+ intent.resolveType(mContext.getContentResolver()), DEFAULT_INTENT_QUERY_FLAGS,
+ userId, false, Binder.getCallingUid());
if (handler == null || handler.activityInfo == null) {
return null;
}
if (mServiceInternal.isResolveActivityComponent(handler.activityInfo)) {
return null;
}
- return getSystemPackage(handler.activityInfo.packageName);
+ String packageName = handler.activityInfo.packageName;
+ return isSystemPackage(packageName) ? packageName : null;
}
- private PackageParser.Package getDefaultSystemHandlerServicePackage(
+ private String getDefaultSystemHandlerServicePackage(String intentAction, int userId) {
+ return getDefaultSystemHandlerServicePackage(new Intent(intentAction), userId);
+ }
+
+ private String getDefaultSystemHandlerServicePackage(
Intent intent, int userId) {
List<ResolveInfo> handlers = mServiceInternal.queryIntentServices(
- intent, DEFAULT_FLAGS, Binder.getCallingUid(), userId);
+ intent, DEFAULT_INTENT_QUERY_FLAGS, Binder.getCallingUid(), userId);
if (handlers == null) {
return null;
}
final int handlerCount = handlers.size();
for (int i = 0; i < handlerCount; i++) {
ResolveInfo handler = handlers.get(i);
- PackageParser.Package handlerPackage = getSystemPackage(
- handler.serviceInfo.packageName);
- if (handlerPackage != null) {
+ String handlerPackage = handler.serviceInfo.packageName;
+ if (isSystemPackage(handlerPackage)) {
return handlerPackage;
}
}
return null;
}
- private List<PackageParser.Package> getHeadlessSyncAdapterPackages(
+ private ArrayList<String> getHeadlessSyncAdapterPackages(
String[] syncAdapterPackageNames, int userId) {
- List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
+ ArrayList<String> syncAdapterPackages = new ArrayList<>();
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
for (String syncAdapterPackageName : syncAdapterPackageNames) {
homeIntent.setPackage(syncAdapterPackageName);
ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
- homeIntent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS,
+ homeIntent.resolveType(mContext.getContentResolver()),
+ DEFAULT_INTENT_QUERY_FLAGS,
userId, false, Binder.getCallingUid());
if (homeActivity != null) {
continue;
}
- PackageParser.Package syncAdapterPackage = getSystemPackage(syncAdapterPackageName);
- if (syncAdapterPackage != null) {
- syncAdapterPackages.add(syncAdapterPackage);
+ if (isSystemPackage(syncAdapterPackageName)) {
+ syncAdapterPackages.add(syncAdapterPackageName);
}
}
return syncAdapterPackages;
}
- private PackageParser.Package getDefaultProviderAuthorityPackage(
- String authority, int userId) {
- ProviderInfo provider =
- mServiceInternal.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
+ private String getDefaultProviderAuthorityPackage(String authority, int userId) {
+ ProviderInfo provider = mServiceInternal.resolveContentProvider(
+ authority, DEFAULT_INTENT_QUERY_FLAGS, userId);
if (provider != null) {
- return getSystemPackage(provider.packageName);
+ return provider.packageName;
}
return null;
}
- private PackageParser.Package getPackage(String packageName) {
- return mServiceInternal.getPackage(packageName);
+ private boolean isSystemPackage(String packageName) {
+ return isSystemPackage(getPackageInfo(packageName));
}
- private PackageParser.Package getSystemPackage(String packageName) {
- PackageParser.Package pkg = getPackage(packageName);
- if (pkg != null && pkg.isSystem()) {
- return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null;
+ private boolean isSystemPackage(PackageInfo pkg) {
+ if (pkg == null) {
+ return false;
}
- return null;
+ return pkg.applicationInfo.isSystemApp()
+ && !isSysComponentOrPersistentPlatformSignedPrivApp(pkg);
}
- private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
- int userId) {
- grantRuntimePermissions(pkg, permissions, false, false, userId);
- }
-
- private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissions,
boolean systemFixed, int userId) {
grantRuntimePermissions(pkg, permissions, systemFixed, false, userId);
}
- private void revokeRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ private void revokeRuntimePermissions(String packageName, Set<String> permissions,
boolean systemFixed, int userId) {
- if (pkg.requestedPermissions.isEmpty()) {
+ PackageInfo pkg = getSystemPackageInfo(packageName);
+ if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
return;
}
- Set<String> revokablePermissions = new ArraySet<>(pkg.requestedPermissions);
+ Set<String> revokablePermissions = new ArraySet<>(Arrays.asList(pkg.requestedPermissions));
for (String permission : permissions) {
// We can't revoke what wasn't requested.
@@ -1181,7 +977,7 @@
}
final int flags = mServiceInternal.getPermissionFlagsTEMP(
- permission, pkg.packageName, userId);
+ permission, packageName, userId);
// We didn't get this through the default grant policy. Move along.
if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) == 0) {
@@ -1197,29 +993,35 @@
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 && !systemFixed) {
continue;
}
- mServiceInternal.revokeRuntimePermission(pkg.packageName, permission, userId, false);
+ mServiceInternal.revokeRuntimePermission(packageName, permission, userId, false);
if (DEBUG) {
Log.i(TAG, "revoked " + (systemFixed ? "fixed " : "not fixed ")
- + permission + " to " + pkg.packageName);
+ + permission + " to " + packageName);
}
// Remove the GRANTED_BY_DEFAULT flag without touching the others.
// Note that we do not revoke FLAG_PERMISSION_SYSTEM_FIXED. That bit remains
// sticky once set.
- mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
+ mServiceInternal.updatePermissionFlagsTEMP(permission, packageName,
PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, userId);
}
}
- private void grantRuntimePermissions(PackageParser.Package pkg,
+ private void grantRuntimePermissions(PackageInfo pkg,
Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage,
int userId) {
- if (pkg.requestedPermissions.isEmpty()) {
+ if (pkg == null) {
+ return;
+ }
+
+ String[] requestedPermissions = pkg.requestedPermissions;
+ if (ArrayUtils.isEmpty(requestedPermissions)) {
return;
}
final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
+ ApplicationInfo applicationInfo = pkg.applicationInfo;
// Automatically attempt to grant split permissions to older APKs
final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
@@ -1227,13 +1029,13 @@
final PackageParser.SplitPermissionInfo splitPerm =
PackageParser.SPLIT_PERMISSIONS[splitPermNum];
- if (pkg.applicationInfo.targetSdkVersion < splitPerm.targetSdk
+ if (applicationInfo != null
+ && applicationInfo.targetSdkVersion < splitPerm.targetSdk
&& permissionsWithoutSplits.contains(splitPerm.rootPerm)) {
Collections.addAll(permissions, splitPerm.newPerms);
}
}
- List<String> requestedPermissions = pkg.requestedPermissions;
Set<String> grantablePermissions = null;
// In some cases, like for the Phone or SMS app, we grant permissions regardless
@@ -1242,23 +1044,25 @@
// choice to grant this app the permissions needed to function. For all other
// apps, (default grants on first boot and user creation) we don't grant default
// permissions if the version on the system image does not declare them.
- if (!ignoreSystemPackage && pkg.isUpdatedSystemApp()) {
- final PackageParser.Package disabledPkg =
- mServiceInternal.getDisabledPackage(pkg.packageName);
+ if (!ignoreSystemPackage
+ && applicationInfo != null
+ && applicationInfo.isUpdatedSystemApp()) {
+ final PackageInfo disabledPkg = getSystemPackageInfo(
+ mServiceInternal.getDisabledSystemPackageName(pkg.packageName));
if (disabledPkg != null) {
- if (disabledPkg.requestedPermissions.isEmpty()) {
+ if (ArrayUtils.isEmpty(disabledPkg.requestedPermissions)) {
return;
}
if (!requestedPermissions.equals(disabledPkg.requestedPermissions)) {
- grantablePermissions = new ArraySet<>(requestedPermissions);
+ grantablePermissions = new ArraySet<>(Arrays.asList(requestedPermissions));
requestedPermissions = disabledPkg.requestedPermissions;
}
}
}
- final int grantablePermissionCount = requestedPermissions.size();
+ final int grantablePermissionCount = requestedPermissions.length;
for (int i = 0; i < grantablePermissionCount; i++) {
- String permission = requestedPermissions.get(i);
+ String permission = requestedPermissions[i];
// If there is a disabled system app it may request a permission the updated
// version ot the data partition doesn't, In this case skip the permission.
@@ -1288,7 +1092,7 @@
pkg.packageName, permission, userId, false);
if (DEBUG) {
Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
- + permission + " to default handler " + pkg.packageName);
+ + permission + " to default handler " + pkg);
}
int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -1307,7 +1111,7 @@
&& !systemFixed) {
if (DEBUG) {
Log.i(TAG, "Granted not fixed " + permission + " to default handler "
- + pkg.packageName);
+ + pkg);
}
mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
@@ -1316,28 +1120,42 @@
}
}
- private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageParser.Package pkg) {
+ private PackageInfo getSystemPackageInfo(String pkg) {
+ //TODO not MATCH_SYSTEM_ONLY?
+ return getPackageInfo(pkg, PackageManager.MATCH_FACTORY_ONLY);
+ }
+
+ private PackageInfo getPackageInfo(String pkg) {
+ return getPackageInfo(pkg, 0 /* extraFlags */);
+ }
+
+ private PackageInfo getPackageInfo(String pkg,
+ @PackageManager.PackageInfoFlags int extraFlags) {
+ return mServiceInternal.getPackageInfo(pkg,
+ DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags,
+ //TODO is this the right filterCallingUid?
+ UserHandle.USER_SYSTEM, UserHandle.USER_SYSTEM);
+ }
+
+ private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageInfo pkg) {
if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
return true;
}
- if (!pkg.isPrivileged()) {
+ if (!pkg.applicationInfo.isPrivilegedApp()) {
return false;
}
- final PackageParser.Package disabledPkg =
- mServiceInternal.getDisabledPackage(pkg.packageName);
+ final PackageInfo disabledPkg = getSystemPackageInfo(
+ mServiceInternal.getDisabledSystemPackageName(pkg.applicationInfo.packageName));
if (disabledPkg != null) {
- if ((disabledPkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ ApplicationInfo disabledPackageAppInfo = disabledPkg.applicationInfo;
+ if (disabledPackageAppInfo != null
+ && (disabledPackageAppInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
return false;
}
} else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
return false;
}
- final String systemPackageName = mServiceInternal.getKnownPackageName(
- PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
- final PackageParser.Package systemPackage = getPackage(systemPackageName);
- return pkg.mSigningDetails.hasAncestorOrSelf(systemPackage.mSigningDetails)
- || systemPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ return mServiceInternal.isPlatformSigned(pkg.packageName);
}
private void grantDefaultPermissionExceptions(int userId) {
@@ -1357,7 +1175,7 @@
final int exceptionCount = mGrantExceptions.size();
for (int i = 0; i < exceptionCount; i++) {
String packageName = mGrantExceptions.keyAt(i);
- PackageParser.Package pkg = getSystemPackage(packageName);
+ PackageInfo pkg = getSystemPackageInfo(packageName);
List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
final int permissionGrantCount = permissionGrants.size();
for (int j = 0; j < permissionGrantCount; j++) {
@@ -1368,8 +1186,7 @@
permissions.clear();
}
permissions.add(permissionGrant.name);
- grantRuntimePermissions(pkg, permissions,
- permissionGrant.fixed, userId);
+ grantRuntimePermissions(pkg, permissions, permissionGrant.fixed, userId);
}
}
}
@@ -1474,15 +1291,14 @@
outGrantExceptions.get(packageName);
if (packageExceptions == null) {
// The package must be on the system image
- PackageParser.Package pkg = getSystemPackage(packageName);
- if (pkg == null) {
+ if (!isSystemPackage(packageName)) {
Log.w(TAG, "Unknown package:" + packageName);
XmlUtils.skipCurrentTag(parser);
continue;
}
// The package must support runtime permissions
- if (!doesPackageSupportRuntimePermissions(pkg)) {
+ if (!doesPackageSupportRuntimePermissions(getSystemPackageInfo(packageName))) {
Log.w(TAG, "Skipping non supporting runtime permissions package:"
+ packageName);
XmlUtils.skipCurrentTag(parser);
@@ -1527,8 +1343,9 @@
}
}
- private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
- return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
+ private static boolean doesPackageSupportRuntimePermissions(PackageInfo pkg) {
+ return pkg.applicationInfo != null
+ && pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
}
private static final class DefaultPermissionGrant {
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 c4f90a12..4b6760c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1189,7 +1189,7 @@
// is granted only if it had been defined by the original application.
if (pkg.isUpdatedSystemApp()) {
final PackageParser.Package disabledPkg =
- mPackageManagerInt.getDisabledPackage(pkg.packageName);
+ mPackageManagerInt.getDisabledSystemPackage(pkg.packageName);
final PackageSetting disabledPs =
(disabledPkg != null) ? (PackageSetting) disabledPkg.mExtras : null;
if (disabledPs != null
@@ -1221,7 +1221,7 @@
// packages can also get the permission.
if (pkg.parentPackage != null) {
final PackageParser.Package disabledParentPkg = mPackageManagerInt
- .getDisabledPackage(pkg.parentPackage.packageName);
+ .getDisabledSystemPackage(pkg.parentPackage.packageName);
final PackageSetting disabledParentPs = (disabledParentPkg != null)
? (PackageSetting) disabledParentPkg.mExtras : null;
if (disabledParentPkg != null
@@ -1372,7 +1372,7 @@
return;
}
final PackageParser.Package disabledPkg =
- mPackageManagerInt.getDisabledPackage(pkg.parentPackage.packageName);
+ mPackageManagerInt.getDisabledSystemPackage(pkg.parentPackage.packageName);
if (disabledPkg == null || disabledPkg.mExtras == null) {
return;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 350d6b6..91fd8d0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -125,6 +125,7 @@
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
+
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -631,9 +632,11 @@
InputConsumer mInputConsumer = null;
- private final WindowFrames mWindowFrames = new WindowFrames();
- private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
- private static final Rect mTmpRect = new Rect();
+ private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
+ private static final Rect sTmpRect = new Rect();
+ private static final Rect sTmpDockedFrame = new Rect();
+ private static final Rect sTmpNavFrame = new Rect();
+ private static final Rect sTmpLastParentFrame = new Rect();
WindowState mTopFullscreenOpaqueWindowState;
WindowState mTopFullscreenOpaqueOrDimmingWindowState;
@@ -3543,8 +3546,6 @@
return 0;
} else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) {
return -1;
- } else if (mHasFeatureLeanback && interceptAccessibilityGestureTv(keyCode, down)) {
- return -1;
} else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) {
if (!down) {
mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
@@ -4310,9 +4311,6 @@
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
- mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
-
if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
@@ -4379,12 +4377,8 @@
return;
}
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */,
- displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */,
- displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */,
- mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */,
- displayFrames.mDock /* outsetFrame */);
+ sTmpRect.setEmpty();
+ sTmpDockedFrame.set(displayFrames.mDock);
final int displayId = displayFrames.mDisplayId;
final Rect dockFrame = displayFrames.mDock;
@@ -4398,7 +4392,13 @@
continue;
}
- w.computeFrameLw(mWindowFrames);
+ w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
+ sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
+ sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
+ sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
+ sTmpDockedFrame /* outsetFrame */);
+ w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ w.computeFrameLw();
final Rect frame = w.getFrameLw();
if (frame.left <= 0 && frame.top <= 0) {
@@ -4450,17 +4450,17 @@
return false;
}
// apply any navigation bar insets
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ sTmpRect.setEmpty();
+ mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
displayFrames.mUnrestricted /* displayFrame */,
displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
- displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */,
+ displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
-
+ mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
mStatusBarLayer = mStatusBar.getSurfaceLayer();
// Let the status bar determine its size.
- mStatusBar.computeFrameLw(mWindowFrames);
+ mStatusBar.computeFrameLw();
// For layout, the status bar is always at the top with our fixed height.
displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4470,11 +4470,11 @@
displayFrames.mDisplayCutoutSafe.top);
// Tell the bar controller where the collapsed status bar content is
- mTmpRect.set(mStatusBar.getContentFrameLw());
- mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- mTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
- mTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
- mStatusBarController.setContentFrame(mTmpRect);
+ sTmpRect.set(mStatusBar.getContentFrameLw());
+ sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset
+ sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+ mStatusBarController.setContentFrame(sTmpRect);
boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
boolean statusBarTranslucent = (sysui
@@ -4514,7 +4514,7 @@
return false;
}
- final Rect navigationFrame = mWindowFrames.mParentFrame;
+ final Rect navigationFrame = sTmpNavFrame;
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
@@ -4525,7 +4525,7 @@
final Rect dockFrame = displayFrames.mDock;
mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
- final Rect cutoutSafeUnrestricted = mTmpRect;
+ final Rect cutoutSafeUnrestricted = sTmpRect;
cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
@@ -4607,15 +4607,15 @@
displayFrames.mContent.set(dockFrame);
mStatusBarLayer = mNavigationBar.getSurfaceLayer();
// And compute the final frame.
- mTmpRect.setEmpty();
- mWindowFrames.setFrames(navigationFrame /* parentFrame */,
+ sTmpRect.setEmpty();
+ mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
displayFrames.mDisplayCutoutSafe /* contentFrame */,
- navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */,
+ navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
navigationFrame /* stableFrame */,
displayFrames.mDisplayCutoutSafe /* outsetFrame */);
-
- mNavigationBar.computeFrameLw(mWindowFrames);
+ mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ mNavigationBar.computeFrameLw();
mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
@@ -4746,17 +4746,20 @@
final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
- final Rect pf = mWindowFrames.mParentFrame;
- final Rect df = mWindowFrames.mDisplayFrame;
- final Rect of = mWindowFrames.mOverscanFrame;
- final Rect cf = mWindowFrames.mContentFrame;
- final Rect vf = mWindowFrames.mVisibleFrame;
- final Rect dcf = mWindowFrames.mDecorFrame;
- final Rect sf = mWindowFrames.mStableFrame;
+ final WindowFrames windowFrames = win.getWindowFrames();
+
+ windowFrames.setHasOutsets(false);
+ sTmpLastParentFrame.set(windowFrames.mParentFrame);
+ final Rect pf = windowFrames.mParentFrame;
+ final Rect df = windowFrames.mDisplayFrame;
+ final Rect of = windowFrames.mOverscanFrame;
+ final Rect cf = windowFrames.mContentFrame;
+ final Rect vf = windowFrames.mVisibleFrame;
+ final Rect dcf = windowFrames.mDecorFrame;
+ final Rect sf = windowFrames.mStableFrame;
dcf.setEmpty();
- mWindowFrames.mOutsetFrame.setEmpty();
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
- mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+ windowFrames.setParentFrameWasClippedByDisplayCutout(false);
+ windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
@@ -4776,7 +4779,7 @@
cf.set(displayFrames.mDock);
of.set(displayFrames.mDock);
df.set(displayFrames.mDock);
- pf.set(displayFrames.mDock);
+ windowFrames.mParentFrame.set(displayFrames.mDock);
// IM dock windows layout below the nav bar...
pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
// ...with content insets above the nav bar
@@ -4878,9 +4881,7 @@
? displayFrames.mRestricted.bottom
: displayFrames.mUnrestricted.bottom;
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out status bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
&& type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
// Asking to layout into the overscan region, so give it that pure
@@ -4952,17 +4953,13 @@
pf.bottom = df.bottom = of.bottom = cf.bottom =
displayFrames.mRestricted.bottom;
}
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
} else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
// The navigation bar has Real Ultimate Power.
of.set(displayFrames.mUnrestricted);
df.set(displayFrames.mUnrestricted);
pf.set(displayFrames.mUnrestricted);
- if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
- "Laying out navigation bar window: (%d,%d - %d,%d)",
- pf.left, pf.top, pf.right, pf.bottom));
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
} else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
// Fullscreen secure system overlays get what they ask for. Screenshot region
@@ -5089,7 +5086,7 @@
// Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
// the cutout safe zone.
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
- final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect;
+ final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
&& cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
@@ -5124,9 +5121,9 @@
// They will later be cropped or shifted using the displayFrame in WindowState,
// which prevents overlap with the DisplayCutout.
if (!attachedInParent && !floatingInScreenWindow) {
- mTmpRect.set(pf);
+ sTmpRect.set(pf);
pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(!mTmpRect.equals(pf));
+ windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
}
// Make sure that NO_LIMITS windows clipped to the display don't extend under the
// cutout.
@@ -5154,8 +5151,9 @@
// apply the outsets to floating dialogs, because they wouldn't make sense there.
final boolean useOutsets = shouldUseOutsets(attrs, fl);
if (isDefaultDisplay && useOutsets) {
- final Rect osf = mWindowFrames.mOutsetFrame;
+ final Rect osf = windowFrames.mOutsetFrame;
osf.set(cf.left, cf.top, cf.right, cf.bottom);
+ windowFrames.setHasOutsets(true);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
int rotation = displayFrames.mRotation;
@@ -5182,9 +5180,13 @@
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+ " dcf=" + dcf.toShortString()
+ " sf=" + sf.toShortString()
- + " osf=" + mWindowFrames.mOutsetFrame.toShortString());
+ + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
- win.computeFrameLw(mWindowFrames);
+ if (!sTmpLastParentFrame.equals(pf)) {
+ windowFrames.setContentChanged(true);
+ }
+
+ win.computeFrameLw();
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
@@ -6037,6 +6039,22 @@
}
}
+ // Intercept the Accessibility keychord for TV (DPAD_DOWN + Back) before the keyevent is
+ // processed through interceptKeyEventBeforeDispatch since Talkback may consume this event
+ // before it has a chance to reach that method.
+ if (mHasFeatureLeanback) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_BACK: {
+ boolean handled = interceptAccessibilityGestureTv(keyCode, down);
+ if (handled) {
+ result &= ~ACTION_PASS_TO_USER;
+ }
+ break;
+ }
+ }
+ }
+
if (useHapticFeedback) {
performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
"Virtual Key - Press");
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
new file mode 100644
index 0000000..50c81f5
--- /dev/null
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -0,0 +1,28 @@
+{
+ "presubmit": [
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.policy."
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b55adeb..1fcdd63c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -63,12 +63,10 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -95,7 +93,6 @@
import com.android.server.wm.DisplayFrames;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowFrames;
-import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -201,10 +198,8 @@
* getFrame() if so desired. Must be called with the window manager
* lock held.
*
- * @param windowFrames Container for all the window frames that affect how the window is
- * laid out.
*/
- public void computeFrameLw(WindowFrames windowFrames);
+ public void computeFrameLw();
/**
* Retrieve the current frame of the window that has been assigned by
@@ -477,6 +472,11 @@
* Writes {@link com.android.server.wm.IdentifierProto} to stream.
*/
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId);
+
+ /**
+ * @return The {@link WindowFrames} associated with this {@link WindowState}
+ */
+ WindowFrames getWindowFrames();
}
/**
@@ -1167,7 +1167,6 @@
default void layoutWindowLw(
WindowState win, WindowState attached, DisplayFrames displayFrames) {}
-
/**
* Return the layout hints for a newly added window. These values are computed on the
* most recent layout, so they are not guaranteed to be correct.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5981ab0..68e636a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -49,6 +49,7 @@
import android.util.Slog;
import android.util.StatsLog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -75,7 +76,8 @@
* tell the system when we go to sleep so that it can lock the keyguard if needed.
* </p>
*/
-final class Notifier {
+@VisibleForTesting
+public class Notifier {
private static final String TAG = "PowerManagerNotifier";
private static final boolean DEBUG = false;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bdf12ca..9f6b3dd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -64,7 +64,6 @@
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
import android.service.vr.IVrManager;
@@ -84,7 +83,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.EventLogTags;
import com.android.server.LockGuard;
@@ -230,6 +228,10 @@
private final BatterySaverController mBatterySaverController;
private final BatterySaverStateMachine mBatterySaverStateMachine;
private final BatterySavingStats mBatterySavingStats;
+ private final BinderService mBinderService;
+ private final LocalService mLocalService;
+ private final NativeWrapper mNativeWrapper;
+ private final Injector mInjector;
private LightsManager mLightsManager;
private BatteryManagerInternal mBatteryManagerInternal;
@@ -636,10 +638,76 @@
}
}
+ /**
+ * Wrapper around the static-native methods of PowerManagerService.
+ *
+ * This class exists to allow us to mock static native methods in our tests. If mocking static
+ * methods becomes easier than this in the future, we can delete this class.
+ */
+ @VisibleForTesting
+ public static class NativeWrapper {
+ /** Wrapper for PowerManager.nativeInit */
+ public void nativeInit(PowerManagerService service) {
+ service.nativeInit();
+ }
+
+ /** Wrapper for PowerManager.nativeAcquireSuspectBlocker */
+ public void nativeAcquireSuspendBlocker(String name) {
+ PowerManagerService.nativeAcquireSuspendBlocker(name);
+ }
+
+ /** Wrapper for PowerManager.nativeReleaseSuspendBlocker */
+ public void nativeReleaseSuspendBlocker(String name) {
+ PowerManagerService.nativeReleaseSuspendBlocker(name);
+ }
+
+ /** Wrapper for PowerManager.nativeSetInteractive */
+ public void nativeSetInteractive(boolean enable) {
+ PowerManagerService.nativeSetInteractive(enable);
+ }
+
+ /** Wrapper for PowerManager.nativeSetAutoSuspend */
+ public void nativeSetAutoSuspend(boolean enable) {
+ PowerManagerService.nativeSetAutoSuspend(enable);
+ }
+
+ /** Wrapper for PowerManager.nativeSendPowerHint */
+ public void nativeSendPowerHint(int hintId, int data) {
+ PowerManagerService.nativeSendPowerHint(hintId, data);
+ }
+
+ /** Wrapper for PowerManager.nativeSetFeature */
+ public void nativeSetFeature(int featureId, int data) {
+ PowerManagerService.nativeSetFeature(featureId, data);
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ return new Notifier(looper, context, batteryStats, suspendBlocker, policy);
+ }
+
+ SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+ SuspendBlocker suspendBlocker = service.new SuspendBlockerImpl(name);
+ service.mSuspendBlockers.add(suspendBlocker);
+ return suspendBlocker;
+ }
+
+ BatterySaverPolicy createBatterySaverPolicy(
+ Object lock, Context context, BatterySavingStats batterySavingStats) {
+ return new BatterySaverPolicy(lock, context, batterySavingStats);
+ }
+
+ NativeWrapper createNativeWrapper() {
+ return new NativeWrapper();
+ }
+ }
+
final Constants mConstants;
private native void nativeInit();
-
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
private static native void nativeSetInteractive(boolean enable);
@@ -648,8 +716,19 @@
private static native void nativeSetFeature(int featureId, int data);
public PowerManagerService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ PowerManagerService(Context context, Injector injector) {
super(context);
+
mContext = context;
+ mBinderService = new BinderService();
+ mLocalService = new LocalService();
+ mNativeWrapper = injector.createNativeWrapper();
+ mInjector = injector;
+
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
mHandlerThread.start();
@@ -658,57 +737,40 @@
mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
mBatterySavingStats = new BatterySavingStats(mLock);
- mBatterySaverPolicy = new BatterySaverPolicy(mLock, mContext, mBatterySavingStats);
+ mBatterySaverPolicy =
+ mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
mBatterySaverController = new BatterySaverController(mLock, mContext,
- BackgroundThread.get().getLooper(), mBatterySaverPolicy, mBatterySavingStats);
+ BackgroundThread.get().getLooper(), mBatterySaverPolicy,
+ mBatterySavingStats);
mBatterySaverStateMachine = new BatterySaverStateMachine(
mLock, mContext, mBatterySaverController);
synchronized (mLock) {
- mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
- mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
- mDisplaySuspendBlocker.acquire();
- mHoldingDisplaySuspendBlocker = true;
+ mWakeLockSuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
+ mDisplaySuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+ if (mDisplaySuspendBlocker != null) {
+ mDisplaySuspendBlocker.acquire();
+ mHoldingDisplaySuspendBlocker = true;
+ }
mHalAutoSuspendModeEnabled = false;
mHalInteractiveModeEnabled = true;
mWakefulness = WAKEFULNESS_AWAKE;
-
sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
- nativeInit();
- nativeSetAutoSuspend(false);
- nativeSetInteractive(true);
- nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
+ mNativeWrapper.nativeInit(this);
+ mNativeWrapper.nativeSetAutoSuspend(false);
+ mNativeWrapper.nativeSetInteractive(true);
+ mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
}
}
- @VisibleForTesting
- PowerManagerService(Context context, BatterySaverPolicy batterySaverPolicy) {
- super(context);
-
- mContext = context;
- mHandlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
- mHandlerThread.start();
- mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
- mConstants = new Constants(mHandler);
- mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
- mDisplaySuspendBlocker = null;
- mWakeLockSuspendBlocker = null;
-
- mBatterySavingStats = new BatterySavingStats(mLock);
- mBatterySaverPolicy = batterySaverPolicy;
- mBatterySaverController = new BatterySaverController(mLock, context,
- BackgroundThread.getHandler().getLooper(), batterySaverPolicy, mBatterySavingStats);
- mBatterySaverStateMachine = new BatterySaverStateMachine(
- mLock, mContext, mBatterySaverController);
- }
-
@Override
public void onStart() {
- publishBinderService(Context.POWER_SERVICE, new BinderService());
- publishLocalService(PowerManagerInternal.class, new LocalService());
+ publishBinderService(Context.POWER_SERVICE, mBinderService);
+ publishLocalService(PowerManagerInternal.class, mLocalService);
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
@@ -752,11 +814,13 @@
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
mBatteryStats = BatteryStatsService.getService();
- mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
- createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
+ mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
+ mPolicy);
mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
- createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
+ mInjector.createSuspendBlocker(
+ this, "PowerManagerService.WirelessChargerDetector"),
mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
@@ -824,7 +888,7 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.DEVICE_DEMO_MODE),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
- IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
+ IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE));
if (vrManager != null) {
try {
vrManager.registerListener(mVrStateCallbacks);
@@ -927,7 +991,8 @@
UserHandle.USER_CURRENT) != 0;
if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) {
mDoubleTapWakeEnabled = doubleTapWakeEnabled;
- nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
+ mNativeWrapper.nativeSetFeature(
+ POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
}
}
@@ -1509,6 +1574,11 @@
}
}
+ @VisibleForTesting
+ int getWakefulness() {
+ return mWakefulness;
+ }
+
/**
* Logs the time the device would have spent awake before user activity timeout,
* had the system not been told the user was inactive.
@@ -2640,7 +2710,7 @@
mHalAutoSuspendModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
try {
- nativeSetAutoSuspend(enable);
+ mNativeWrapper.nativeSetAutoSuspend(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -2655,7 +2725,7 @@
mHalInteractiveModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
try {
- nativeSetInteractive(enable);
+ mNativeWrapper.nativeSetInteractive(enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -3127,7 +3197,7 @@
break;
}
- nativeSendPowerHint(hintId, data);
+ mNativeWrapper.nativeSendPowerHint(hintId, data);
}
/**
@@ -3718,12 +3788,6 @@
proto.flush();
}
- private SuspendBlocker createSuspendBlockerLocked(String name) {
- SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
- mSuspendBlockers.add(suspendBlocker);
- return suspendBlocker;
- }
-
private void incrementBootCount() {
synchronized (mLock) {
int count;
@@ -4022,7 +4086,7 @@
Slog.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was finalized without being released!");
mReferenceCount = 0;
- nativeReleaseSuspendBlocker(mName);
+ mNativeWrapper.nativeReleaseSuspendBlocker(mName);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
}
} finally {
@@ -4039,7 +4103,7 @@
Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
}
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
- nativeAcquireSuspendBlocker(mName);
+ mNativeWrapper.nativeAcquireSuspendBlocker(mName);
}
}
}
@@ -4052,7 +4116,7 @@
if (DEBUG_SPEW) {
Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\".");
}
- nativeReleaseSuspendBlocker(mName);
+ mNativeWrapper.nativeReleaseSuspendBlocker(mName);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
} else if (mReferenceCount < 0) {
Slog.wtf(TAG, "Suspend blocker \"" + mName
@@ -4090,7 +4154,8 @@
}
}
- private final class BinderService extends IPowerManager.Stub {
+ @VisibleForTesting
+ final class BinderService extends IPowerManager.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -4592,6 +4657,16 @@
}
@VisibleForTesting
+ BinderService getBinderServiceInstance() {
+ return mBinderService;
+ }
+
+ @VisibleForTesting
+ LocalService getLocalServiceInstance() {
+ return mLocalService;
+ }
+
+ @VisibleForTesting
// lastRebootReasonProperty argument to permit testing
int getLastShutdownReasonInternal(String lastRebootReasonProperty) {
String line = SystemProperties.get(lastRebootReasonProperty);
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 180f343..9f69702 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -23,11 +23,11 @@
import android.os.SharedMemory;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.Pair;
+import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ByteBufferFactory;
import android.util.apk.SignatureNotFoundException;
-import android.util.Pair;
-import android.util.Slog;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -85,7 +85,7 @@
*/
public static byte[] generateFsverityRootHash(@NonNull String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
- return ApkSignatureVerifier.generateFsverityRootHash(apkPath);
+ return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
}
/**
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 6f9d803..bfa03ca 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -32,6 +32,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.hardware.fingerprint.FingerprintManager;
import android.net.NetworkStats;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
@@ -44,6 +45,8 @@
import android.os.IStatsCompanionService;
import android.os.IStatsManager;
import android.os.IStoraged;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
@@ -53,6 +56,7 @@
import android.os.StatsLogEventWrapper;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
+import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -119,7 +123,7 @@
public static final String CONFIG_DIR = "/data/misc/stats-service";
static final String TAG = "StatsCompanionService";
- static final boolean DEBUG = false;
+ static final boolean DEBUG = true;
public static final int CODE_DATA_BROADCAST = 1;
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
@@ -169,6 +173,8 @@
private KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader =
new KernelUidCpuClusterTimeReader();
+ private static IThermalService sThermalService;
+
public StatsCompanionService(Context context) {
super();
mContext = context;
@@ -213,6 +219,24 @@
long[] freqs = mKernelUidCpuFreqTimeReader.readFreqs(powerProfile);
mKernelUidCpuClusterTimeReader.setThrottleInterval(0);
mKernelUidCpuActiveTimeReader.setThrottleInterval(0);
+
+ // Enable push notifications of throttling from vendor thermal
+ // management subsystem via thermalservice.
+ IBinder b = ServiceManager.getService("thermalservice");
+
+ if (b != null) {
+ sThermalService = IThermalService.Stub.asInterface(b);
+ try {
+ sThermalService.registerThermalEventListener(
+ new ThermalEventListener());
+ Slog.i(TAG, "register thermal listener successfully");
+ } catch (RemoteException e) {
+ // Should never happen.
+ Slog.e(TAG, "register thermal listener error");
+ }
+ } else {
+ Slog.e(TAG, "cannot find thermalservice, no throttling push notifications");
+ }
}
@Override
@@ -230,34 +254,38 @@
@Override
public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
- long subscriptionId, long subscriptionRuleId,
- String[] cookies,
- StatsDimensionsValue dimensionsValue) {
+ long subscriptionId, long subscriptionRuleId, String[] cookies,
+ StatsDimensionsValue dimensionsValue) {
enforceCallingPermission();
IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent = new Intent()
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
- .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+ Intent intent =
+ new Intent()
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
+ .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
ArrayList<String> cookieList = new ArrayList<>(cookies.length);
- for (String cookie : cookies) { cookieList.add(cookie); }
+ for (String cookie : cookies) {
+ cookieList.add(cookie);
+ }
intent.putStringArrayListExtra(
StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
if (DEBUG) {
- Slog.d(TAG, String.format(
- "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
- configUid, configKey, subscriptionId, subscriptionRuleId,
- Arrays.toString(cookies), dimensionsValue));
+ Slog.d(TAG,
+ String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
+ configUid, configKey, subscriptionId, subscriptionRuleId,
+ Arrays.toString(cookies),
+ dimensionsValue));
}
try {
intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
} catch (IntentSender.SendIntentException e) {
- Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
- + "; presumably it had been cancelled.");
+ Slog.w(TAG,
+ "Unable to send using IntentSender from uid " + configUid
+ + "; presumably it had been cancelled.");
}
}
@@ -294,7 +322,7 @@
// Add in all the apps for every user/profile.
for (UserInfo profile : users) {
List<PackageInfo> pi =
- pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
+ pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
for (int j = 0; j < pi.size(); j++) {
if (pi.get(j).applicationInfo != null) {
uids.add(pi.get(j).applicationInfo.uid);
@@ -379,8 +407,9 @@
public final static class PullingAlarmListener implements OnAlarmListener {
@Override
public void onAlarm() {
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Time to poll something.");
+ }
synchronized (sStatsdLock) {
if (sStatsd == null) {
Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
@@ -399,8 +428,9 @@
public final static class PeriodicAlarmListener implements OnAlarmListener {
@Override
public void onAlarm() {
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Time to trigger periodic alarm.");
+ }
synchronized (sStatsdLock) {
if (sStatsd == null) {
Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
@@ -436,7 +466,7 @@
return;
}
try {
- sStatsd.informDeviceShutdown();
+ sStatsd.informDeviceShutdown();
} catch (Exception e) {
Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
}
@@ -475,9 +505,11 @@
@Override // Binder call
public void setAlarmForSubscriberTriggering(long timestampMs) {
enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Setting periodic alarm in about " +
- (timestampMs - SystemClock.elapsedRealtime()));
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Setting periodic alarm in about " + (timestampMs
+ - SystemClock.elapsedRealtime()));
+ }
final long callingToken = Binder.clearCallingIdentity();
try {
// using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
@@ -492,8 +524,9 @@
@Override // Binder call
public void cancelAlarmForSubscriberTriggering() {
enforceCallingPermission();
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Cancelling periodic alarm");
+ }
final long callingToken = Binder.clearCallingIdentity();
try {
mAlarmManager.cancel(mPeriodicAlarmListener);
@@ -523,8 +556,9 @@
@Override // Binder call
public void cancelPullingAlarm() {
enforceCallingPermission();
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Cancelling pulling alarm");
+ }
final long callingToken = Binder.clearCallingIdentity();
try {
mAlarmManager.cancel(mPullingAlarmListener);
@@ -537,10 +571,11 @@
int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
int size = stats.size();
long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
for (int j = 0; j < size; j++) {
stats.getValues(j, entry);
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tag, withFGBG ? 6 : 5);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tag, elapsedNanos, wallClockNanos);
e.writeInt(entry.uid);
if (withFGBG) {
e.writeInt(entry.set);
@@ -616,14 +651,15 @@
return null;
}
- private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullKernelWakelock(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 4);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeString(name);
e.writeInt(kws.mCount);
e.writeInt(kws.mVersion);
@@ -632,7 +668,9 @@
}
}
- private void pullWifiBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullWifiBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
// TODO: Consider caching the following call to get BatteryStatsInternal.
@@ -644,7 +682,8 @@
NetworkStatsFactory nsf = new NetworkStatsFactory();
// Combine all the metrics per Uid into one record.
NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null)
.groupedByUid();
addNetworkStats(tagId, pulledData, stats, false);
} catch (java.io.IOException e) {
@@ -654,7 +693,9 @@
}
}
- private void pullWifiBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullWifiBytesTransferByFgBg(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -664,7 +705,8 @@
}
NetworkStatsFactory nsf = new NetworkStatsFactory();
NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null));
addNetworkStats(tagId, pulledData, stats, true);
} catch (java.io.IOException e) {
Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
@@ -673,7 +715,9 @@
}
}
- private void pullMobileBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullMobileBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -684,7 +728,8 @@
NetworkStatsFactory nsf = new NetworkStatsFactory();
// Combine all the metrics per Uid into one record.
NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null)
.groupedByUid();
addNetworkStats(tagId, pulledData, stats, false);
} catch (java.io.IOException e) {
@@ -694,12 +739,14 @@
}
}
- private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBluetoothBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BluetoothActivityEnergyInfo info = pullBluetoothData();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
if (info.getUidTraffic() != null) {
for (UidTraffic traffic : info.getUidTraffic()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(traffic.getUid());
e.writeLong(traffic.getRxBytes());
e.writeLong(traffic.getTxBytes());
@@ -708,7 +755,9 @@
}
}
- private void pullMobileBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullMobileBytesTransferByFgBg(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -718,7 +767,8 @@
}
NetworkStatsFactory nsf = new NetworkStatsFactory();
NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+ null));
addNetworkStats(tagId, pulledData, stats, true);
} catch (java.io.IOException e) {
Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
@@ -727,13 +777,15 @@
}
}
- private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullCpuTimePerFreq(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
if (clusterTimeMs != null) {
for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(cluster);
e.writeInt(speed);
e.writeLong(clusterTimeMs[speed]);
@@ -743,10 +795,11 @@
}
}
- private void pullKernelUidCpuTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuTimeReader.readAbsolute((uid, userTimeUs, systemTimeUs) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeLong(userTimeUs);
e.writeLong(systemTimeUs);
@@ -754,12 +807,14 @@
});
}
- private void pullKernelUidCpuFreqTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuFreqTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
- if(cpuFreqTimeMs[freqIndex] != 0) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ if (cpuFreqTimeMs[freqIndex] != 0) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(uid);
e.writeInt(freqIndex);
e.writeLong(cpuFreqTimeMs[freqIndex]);
@@ -769,11 +824,13 @@
});
}
- private void pullKernelUidCpuClusterTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuClusterTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
for (int i = 0; i < cpuClusterTimesMs.length; i++) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeInt(uid);
e.writeInt(i);
e.writeLong(cpuClusterTimesMs[i]);
@@ -782,17 +839,20 @@
});
}
- private void pullKernelUidCpuActiveTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullKernelUidCpuActiveTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
mKernelUidCpuActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
- e.writeLong((long)cpuActiveTimesMs);
+ e.writeLong((long) cpuActiveTimesMs);
pulledData.add(e);
});
}
- private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullWifiActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
if (mWifiManager == null) {
mWifiManager =
@@ -803,7 +863,8 @@
SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
mWifiManager.requestActivityInfo(wifiReceiver);
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
e.writeLong(wifiInfo.getTimeStamp());
e.writeInt(wifiInfo.getStackState());
e.writeLong(wifiInfo.getControllerTxTimeMillis());
@@ -812,14 +873,18 @@
e.writeLong(wifiInfo.getControllerEnergyUsed());
pulledData.add(e);
} catch (RemoteException e) {
- Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
+ Slog.e(TAG,
+ "Pulling wifiManager for wifi controller activity energy info has error",
+ e);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
- private void pullModemActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullModemActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
if (mTelephony == null) {
mTelephony = TelephonyManager.from(mContext);
@@ -828,7 +893,7 @@
SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
mTelephony.requestModemActivityInfo(modemReceiver);
final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 10);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(modemInfo.getTimestamp());
e.writeLong(modemInfo.getSleepTimeMillis());
e.writeLong(modemInfo.getIdleTimeMillis());
@@ -843,9 +908,11 @@
}
}
- private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBluetoothActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BluetoothActivityEnergyInfo info = pullBluetoothData();
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(info.getTimeStamp());
e.writeInt(info.getBluetoothStackState());
e.writeLong(info.getControllerTxTimeMillis());
@@ -858,7 +925,8 @@
private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
- SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
+ SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
+ "bluetooth");
adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
return awaitControllerInfo(bluetoothReceiver);
} else {
@@ -867,25 +935,29 @@
}
}
- private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+ private void pullSystemElapsedRealtime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(SystemClock.elapsedRealtime());
pulledData.add(e);
}
- private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+ private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(SystemClock.uptimeMillis());
pulledData.add(e);
}
- private void pullProcessMemoryState(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullProcessMemoryState(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
List<ProcessMemoryState> processMemoryStates =
- LocalServices.getService(ActivityManagerInternal.class)
- .getMemoryStateForProcesses();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
for (ProcessMemoryState processMemoryState : processMemoryStates) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 8 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(processMemoryState.uid);
e.writeString(processMemoryState.processName);
e.writeInt(processMemoryState.oomScore);
@@ -898,7 +970,9 @@
}
}
- private void pullBinderCallsStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBinderCallsStats(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
@@ -906,9 +980,9 @@
}
List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ binderStats.reset();
for (ExportedCallStat callStat : callStats) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 13 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(callStat.uid);
e.writeString(callStat.className);
e.writeString(callStat.methodName);
@@ -926,7 +1000,9 @@
}
}
- private void pullBinderCallsStatsExceptions(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullBinderCallsStatsExceptions(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
@@ -934,26 +1010,28 @@
}
ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
+ // can reset the exception stats.
for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeString(entry.getKey());
e.writeInt(entry.getValue());
pulledData.add(e);
}
}
- private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
LooperStats looperStats = LocalServices.getService(LooperStats.class);
if (looperStats == null) {
return;
}
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ looperStats.reset();
for (LooperStats.ExportedEntry entry : entries) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 9 /* fields */);
- e.writeLong(0); // uid collection not implemented yet
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(entry.workSourceUid);
e.writeString(entry.handlerClassName);
e.writeString(entry.threadName);
e.writeString(entry.messageName);
@@ -962,11 +1040,13 @@
e.writeLong(entry.recordedMessageCount);
e.writeLong(entry.totalLatencyMicros);
e.writeLong(entry.cpuUsageMicros);
+ e.writeBoolean(entry.isInteractive);
pulledData.add(e);
}
}
- private void pullDiskStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullDiskStats(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
// Run a quick-and-dirty performance test: write 512 bytes
byte[] junk = new byte[512];
for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
@@ -1013,41 +1093,40 @@
}
// Add info pulledData.
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(latency);
e.writeBoolean(fileBased);
e.writeInt(writeSpeed);
pulledData.add(e);
}
- private void pullDirectoryUsage(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullDirectoryUsage(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA);
e.writeLong(statFsData.getAvailableBytes());
e.writeLong(statFsData.getTotalBytes());
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE);
e.writeLong(statFsCache.getAvailableBytes());
e.writeLong(statFsCache.getTotalBytes());
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM);
e.writeLong(statFsSystem.getAvailableBytes());
e.writeLong(statFsSystem.getTotalBytes());
pulledData.add(e);
}
- private void pullAppSize(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullAppSize(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
@@ -1065,7 +1144,7 @@
}
for (int i = 0; i < length; i++) {
StatsLogEventWrapper e =
- new StatsLogEventWrapper(elapsedNanos, tagId, 5 /* fields */);
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeString(pkg_names.getString(i));
e.writeLong(app_sizes.optLong(i, -1L));
e.writeLong(app_data_sizes.optLong(i, -1L));
@@ -1078,62 +1157,62 @@
}
}
- private void pullCategorySize(int tagId, List<StatsLogEventWrapper> pulledData) {
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ private void pullCategorySize(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
- StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE);
e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE);
e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE);
e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS);
e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS);
e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO);
e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS);
e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM);
e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L));
e.writeLong(cacheTime);
pulledData.add(e);
- e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER);
e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L));
e.writeLong(cacheTime);
@@ -1143,110 +1222,139 @@
}
}
+ private void pullNumFingerprints(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ if (fingerprintManager == null) {
+ return;
+ }
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ for (UserInfo user : userManager.getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int numFingerprints = fingerprintManager.getEnrolledFingerprints(userId).size();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(userId);
+ e.writeInt(numFingerprints);
+ pulledData.add(e);
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+
/**
* Pulls various data.
*/
@Override // Binder call
public StatsLogEventWrapper[] pullData(int tagId) {
enforceCallingPermission();
- if (DEBUG)
+ if (DEBUG) {
Slog.d(TAG, "Pulling " + tagId);
+ }
List<StatsLogEventWrapper> ret = new ArrayList<>();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
switch (tagId) {
case StatsLog.WIFI_BYTES_TRANSFER: {
- pullWifiBytesTransfer(tagId, ret);
+ pullWifiBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.MOBILE_BYTES_TRANSFER: {
- pullMobileBytesTransfer(tagId, ret);
+ pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
- pullWifiBytesTransferByFgBg(tagId, ret);
+ pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
- pullMobileBytesTransferByFgBg(tagId, ret);
+ pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
- pullBluetoothBytesTransfer(tagId, ret);
+ pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.KERNEL_WAKELOCK: {
- pullKernelWakelock(tagId, ret);
+ pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_TIME_PER_FREQ: {
- pullCpuTimePerFreq(tagId, ret);
+ pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_TIME_PER_UID: {
- pullKernelUidCpuTime(tagId, ret);
+ pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_TIME_PER_UID_FREQ: {
- pullKernelUidCpuFreqTime(tagId, ret);
+ pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_CLUSTER_TIME: {
- pullKernelUidCpuClusterTime(tagId, ret);
+ pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CPU_ACTIVE_TIME: {
- pullKernelUidCpuActiveTime(tagId, ret);
+ pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.WIFI_ACTIVITY_INFO: {
- pullWifiActivityInfo(tagId, ret);
+ pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.MODEM_ACTIVITY_INFO: {
- pullModemActivityInfo(tagId, ret);
+ pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
- pullBluetoothActivityInfo(tagId, ret);
+ pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.SYSTEM_UPTIME: {
- pullSystemUpTime(tagId, ret);
+ pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.SYSTEM_ELAPSED_REALTIME: {
- pullSystemElapsedRealtime(tagId, ret);
+ pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.PROCESS_MEMORY_STATE: {
- pullProcessMemoryState(tagId, ret);
+ pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BINDER_CALLS: {
- pullBinderCallsStats(tagId, ret);
+ pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.BINDER_CALLS_EXCEPTIONS: {
- pullBinderCallsStatsExceptions(tagId, ret);
+ pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.LOOPER_STATS: {
- pullLooperStats(tagId, ret);
+ pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.DISK_STATS: {
- pullDiskStats(tagId, ret);
+ pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.DIRECTORY_USAGE: {
- pullDirectoryUsage(tagId, ret);
+ pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.APP_SIZE: {
- pullAppSize(tagId, ret);
+ pullAppSize(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
case StatsLog.CATEGORY_SIZE: {
- pullCategorySize(tagId, ret);
+ pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.NUM_FINGERPRINTS: {
+ pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
default:
@@ -1456,4 +1564,12 @@
}
}
+ // Thermal event received from vendor thermal management subsystem
+ private static final class ThermalEventListener extends IThermalEventListener.Stub {
+ @Override
+ public void notifyThrottling(boolean isThrottling, Temperature temp) {
+ StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(),
+ isThrottling ? 1 : 0, temp.getValue());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 14294ec..79eab6b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -565,10 +565,12 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ boolean requireConfirmation) {
+ enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.showBiometricDialog(bundle, receiver, type);
+ mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation);
} catch (RemoteException ex) {
}
}
@@ -576,6 +578,7 @@
@Override
public void onBiometricAuthenticated() {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.onBiometricAuthenticated();
@@ -586,6 +589,7 @@
@Override
public void onBiometricHelp(String message) {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.onBiometricHelp(message);
@@ -596,6 +600,7 @@
@Override
public void onBiometricError(String error) {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.onBiometricError(error);
@@ -606,6 +611,7 @@
@Override
public void hideBiometricDialog() {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.hideBiometricDialog();
@@ -866,6 +872,12 @@
"StatusBarManagerService");
}
+ private void enforceBiometricDialog() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG,
+ "StatusBarManagerService");
+ }
+
// ================================================================================
// Callbacks from the status bar service.
// ================================================================================
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 6c5452a..7a3f030 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
@@ -39,12 +38,13 @@
import android.util.IntArray;
import android.util.Slog;
-import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
+import com.android.server.pm.permission.PermissionManagerInternal;
/**
* Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
@@ -72,8 +72,8 @@
synchronized (mLock) {
if (mDefaultSmsAppRequests != null || mDefaultDialerAppRequests != null
|| mDefaultSimCallManagerRequests != null) {
- final PackageManagerInternal packageManagerInternal = LocalServices
- .getService(PackageManagerInternal.class);
+ final DefaultPermissionGrantPolicy permissionPolicy =
+ getDefaultPermissionGrantPolicy();
if (mDefaultSmsAppRequests != null) {
ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
@@ -83,7 +83,7 @@
for (int i = requestCount - 1; i >= 0; i--) {
final int userid = mDefaultSmsAppRequests.get(i);
mDefaultSmsAppRequests.remove(i);
- packageManagerInternal.grantDefaultPermissionsToDefaultSmsApp(
+ permissionPolicy.grantDefaultPermissionsToDefaultSmsApp(
smsComponent.getPackageName(), userid);
}
}
@@ -97,7 +97,7 @@
for (int i = requestCount - 1; i >= 0; i--) {
final int userId = mDefaultDialerAppRequests.get(i);
mDefaultDialerAppRequests.remove(i);
- packageManagerInternal.grantDefaultPermissionsToDefaultDialerApp(
+ permissionPolicy.grantDefaultPermissionsToDefaultDialerApp(
packageName, userId);
}
}
@@ -113,7 +113,7 @@
for (int i = requestCount - 1; i >= 0; i--) {
final int userId = mDefaultSimCallManagerRequests.get(i);
mDefaultSimCallManagerRequests.remove(i);
- packageManagerInternal
+ permissionPolicy
.grantDefaultPermissionsToDefaultSimCallManager(
packageName, userId);
}
@@ -132,6 +132,11 @@
}
}
+ private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
+ return LocalServices.getService(PermissionManagerInternal.class)
+ .getDefaultPermissionGrantPolicy();
+ }
+
private static final ComponentName SERVICE_COMPONENT = new ComponentName(
"com.android.server.telecom",
"com.android.server.telecom.components.TelecomService");
@@ -196,82 +201,68 @@
private void registerDefaultAppProviders() {
- final PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
+ final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy();
- // Set a callback for the package manager to query the default sms app.
- packageManagerInternal.setSmsAppPackagesProvider(
- new PackageManagerInternal.PackagesProvider() {
- @Override
- public String[] getPackages(int userId) {
- synchronized (mLock) {
- if (mServiceConnection == null) {
- if (mDefaultSmsAppRequests == null) {
- mDefaultSmsAppRequests = new IntArray();
- }
- mDefaultSmsAppRequests.add(userId);
- return null;
+ // Set a callback for the permission grant policy to query the default sms app.
+ permissionPolicy.setSmsAppPackagesProvider(userId -> {
+ synchronized (mLock) {
+ if (mServiceConnection == null) {
+ if (mDefaultSmsAppRequests == null) {
+ mDefaultSmsAppRequests = new IntArray();
}
+ mDefaultSmsAppRequests.add(userId);
+ return null;
}
- ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
- mContext, true);
- if (smsComponent != null) {
- return new String[]{smsComponent.getPackageName()};
- }
- return null;
}
+ ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
+ mContext, true);
+ if (smsComponent != null) {
+ return new String[]{smsComponent.getPackageName()};
+ }
+ return null;
});
- // Set a callback for the package manager to query the default dialer app.
- packageManagerInternal.setDialerAppPackagesProvider(
- new PackageManagerInternal.PackagesProvider() {
- @Override
- public String[] getPackages(int userId) {
- synchronized (mLock) {
- if (mServiceConnection == null) {
- if (mDefaultDialerAppRequests == null) {
- mDefaultDialerAppRequests = new IntArray();
- }
- mDefaultDialerAppRequests.add(userId);
- return null;
+ // Set a callback for the permission grant policy to query the default dialer app.
+ permissionPolicy.setDialerAppPackagesProvider(userId -> {
+ synchronized (mLock) {
+ if (mServiceConnection == null) {
+ if (mDefaultDialerAppRequests == null) {
+ mDefaultDialerAppRequests = new IntArray();
}
+ mDefaultDialerAppRequests.add(userId);
+ return null;
}
- String packageName = DefaultDialerManager.getDefaultDialerApplication(mContext);
- if (packageName != null) {
- return new String[]{packageName};
- }
- return null;
}
+ String packageName = DefaultDialerManager.getDefaultDialerApplication(mContext);
+ if (packageName != null) {
+ return new String[]{packageName};
+ }
+ return null;
});
- // Set a callback for the package manager to query the default sim call manager.
- packageManagerInternal.setSimCallManagerPackagesProvider(
- new PackageManagerInternal.PackagesProvider() {
- @Override
- public String[] getPackages(int userId) {
- synchronized (mLock) {
- if (mServiceConnection == null) {
- if (mDefaultSimCallManagerRequests == null) {
- mDefaultSimCallManagerRequests = new IntArray();
- }
- mDefaultSimCallManagerRequests.add(userId);
- return null;
+ // Set a callback for the permission grant policy to query the default sim call manager.
+ permissionPolicy.setSimCallManagerPackagesProvider(userId -> {
+ synchronized (mLock) {
+ if (mServiceConnection == null) {
+ if (mDefaultSimCallManagerRequests == null) {
+ mDefaultSimCallManagerRequests = new IntArray();
}
+ mDefaultSimCallManagerRequests.add(userId);
+ return null;
}
- TelecomManager telecomManager =
+ }
+ TelecomManager telecomManager =
(TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
- if (phoneAccount != null) {
- return new String[]{phoneAccount.getComponentName().getPackageName()};
- }
- return null;
+ PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
+ if (phoneAccount != null) {
+ return new String[]{phoneAccount.getComponentName().getPackageName()};
}
+ return null;
});
}
private void registerDefaultAppNotifier() {
- final PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
+ final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy();
// Notify the package manager on default app changes
final Uri defaultSmsAppUri = Settings.Secure.getUriFor(
@@ -287,17 +278,17 @@
ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
mContext, true);
if (smsComponent != null) {
- packageManagerInternal.grantDefaultPermissionsToDefaultSmsApp(
+ permissionPolicy.grantDefaultPermissionsToDefaultSmsApp(
smsComponent.getPackageName(), userId);
}
} else if (defaultDialerAppUri.equals(uri)) {
String packageName = DefaultDialerManager.getDefaultDialerApplication(
mContext);
if (packageName != null) {
- packageManagerInternal.grantDefaultPermissionsToDefaultDialerApp(
+ permissionPolicy.grantDefaultPermissionsToDefaultDialerApp(
packageName, userId);
}
- updateSimCallManagerPermissions(packageManagerInternal, userId);
+ updateSimCallManagerPermissions(permissionPolicy, userId);
}
}
};
@@ -310,14 +301,12 @@
private void registerCarrierConfigChangedReceiver() {
- final PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
for (int userId : UserManagerService.getInstance().getUserIds()) {
- updateSimCallManagerPermissions(packageManagerInternal, userId);
+ updateSimCallManagerPermissions(getDefaultPermissionGrantPolicy(), userId);
}
}
}
@@ -327,15 +316,15 @@
new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), null, null);
}
- private void updateSimCallManagerPermissions(PackageManagerInternal packageManagerInternal,
- int userId) {
+ private void updateSimCallManagerPermissions(
+ DefaultPermissionGrantPolicy permissionGrantPolicy, int userId) {
TelecomManager telecomManager =
(TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
if (phoneAccount != null) {
Slog.i(TAG, "updating sim call manager permissions for userId:" + userId);
String packageName = phoneAccount.getComponentName().getPackageName();
- packageManagerInternal.grantDefaultPermissionsToDefaultSimCallManager(
+ permissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager(
packageName, userId);
}
}
diff --git a/core/java/com/android/internal/textservice/LazyIntToIntMap.java b/services/core/java/com/android/server/textservices/LazyIntToIntMap.java
similarity index 91%
rename from core/java/com/android/internal/textservice/LazyIntToIntMap.java
rename to services/core/java/com/android/server/textservices/LazyIntToIntMap.java
index ca9936c..2e7f2a9 100644
--- a/core/java/com/android/internal/textservice/LazyIntToIntMap.java
+++ b/services/core/java/com/android/server/textservices/LazyIntToIntMap.java
@@ -14,21 +14,18 @@
* limitations under the License.
*/
-package com.android.internal.textservice;
+package com.android.server.textservices;
import android.annotation.NonNull;
import android.util.SparseIntArray;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.util.function.IntUnaryOperator;
/**
* Simple int-to-int key-value-store that is to be lazily initialized with the given
* {@link IntUnaryOperator}.
*/
-@VisibleForTesting
-public final class LazyIntToIntMap {
+final class LazyIntToIntMap {
private final SparseIntArray mMap = new SparseIntArray();
diff --git a/services/core/java/com/android/server/textservices/LocaleUtils.java b/services/core/java/com/android/server/textservices/LocaleUtils.java
new file mode 100644
index 0000000..89d8c1e
--- /dev/null
+++ b/services/core/java/com/android/server/textservices/LocaleUtils.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.textservices;
+
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Provides {@code Locale} related utility methods for {@link TextServicesManagerService}.
+ * <p>This class is intentionally package-private. Utility methods here are tightly coupled with
+ * implementation details in {@link TextServicesManagerService}. Hence this class is not suitable
+ * for other components to directly use.</p>
+ */
+final class LocaleUtils {
+ /**
+ * Returns a list of {@link Locale} in the order of appropriateness for the default spell
+ * checker service.
+ *
+ * <p>If the system language is English, and the region is also explicitly specified in the
+ * system locale, the following fallback order will be applied.</p>
+ * <ul>
+ * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li>
+ * <li>(system-locale-language, system-locale-region)</li>
+ * <li>("en", "US")</li>
+ * <li>("en", "GB")</li>
+ * <li>("en")</li>
+ * </ul>
+ *
+ * <p>If the system language is English, but no region is specified in the system locale,
+ * the following fallback order will be applied.</p>
+ * <ul>
+ * <li>("en")</li>
+ * <li>("en", "US")</li>
+ * <li>("en", "GB")</li>
+ * </ul>
+ *
+ * <p>If the system language is not English, the following fallback order will be applied.</p>
+ * <ul>
+ * <li>(system-locale-language, system-locale-region, system-locale-variant) (if exists)</li>
+ * <li>(system-locale-language, system-locale-region) (if exists)</li>
+ * <li>(system-locale-language) (if exists)</li>
+ * <li>("en", "US")</li>
+ * <li>("en", "GB")</li>
+ * <li>("en")</li>
+ * </ul>
+ *
+ * @param systemLocale the current system locale to be taken into consideration.
+ * @return a list of {@link Locale}. The first one is considered to be most appropriate.
+ */
+ public static ArrayList<Locale> getSuitableLocalesForSpellChecker(
+ @Nullable final Locale systemLocale) {
+ final Locale systemLocaleLanguageCountryVariant;
+ final Locale systemLocaleLanguageCountry;
+ final Locale systemLocaleLanguage;
+ if (systemLocale != null) {
+ final String language = systemLocale.getLanguage();
+ final boolean hasLanguage = !TextUtils.isEmpty(language);
+ final String country = systemLocale.getCountry();
+ final boolean hasCountry = !TextUtils.isEmpty(country);
+ final String variant = systemLocale.getVariant();
+ final boolean hasVariant = !TextUtils.isEmpty(variant);
+ if (hasLanguage && hasCountry && hasVariant) {
+ systemLocaleLanguageCountryVariant = new Locale(language, country, variant);
+ } else {
+ systemLocaleLanguageCountryVariant = null;
+ }
+ if (hasLanguage && hasCountry) {
+ systemLocaleLanguageCountry = new Locale(language, country);
+ } else {
+ systemLocaleLanguageCountry = null;
+ }
+ if (hasLanguage) {
+ systemLocaleLanguage = new Locale(language);
+ } else {
+ systemLocaleLanguage = null;
+ }
+ } else {
+ systemLocaleLanguageCountryVariant = null;
+ systemLocaleLanguageCountry = null;
+ systemLocaleLanguage = null;
+ }
+
+ final ArrayList<Locale> locales = new ArrayList<>();
+ if (systemLocaleLanguageCountryVariant != null) {
+ locales.add(systemLocaleLanguageCountryVariant);
+ }
+
+ if (Locale.ENGLISH.equals(systemLocaleLanguage)) {
+ if (systemLocaleLanguageCountry != null) {
+ // If the system language is English, and the region is also explicitly specified,
+ // following fallback order will be applied.
+ // - systemLocaleLanguageCountry [if systemLocaleLanguageCountry is non-null]
+ // - en_US [if systemLocaleLanguageCountry is non-null and not en_US]
+ // - en_GB [if systemLocaleLanguageCountry is non-null and not en_GB]
+ // - en
+ if (systemLocaleLanguageCountry != null) {
+ locales.add(systemLocaleLanguageCountry);
+ }
+ if (!Locale.US.equals(systemLocaleLanguageCountry)) {
+ locales.add(Locale.US);
+ }
+ if (!Locale.UK.equals(systemLocaleLanguageCountry)) {
+ locales.add(Locale.UK);
+ }
+ locales.add(Locale.ENGLISH);
+ } else {
+ // If the system language is English, but no region is specified, following
+ // fallback order will be applied.
+ // - en
+ // - en_US
+ // - en_GB
+ locales.add(Locale.ENGLISH);
+ locales.add(Locale.US);
+ locales.add(Locale.UK);
+ }
+ } else {
+ // If the system language is not English, the fallback order will be
+ // - systemLocaleLanguageCountry [if non-null]
+ // - systemLocaleLanguage [if non-null]
+ // - en_US
+ // - en_GB
+ // - en
+ if (systemLocaleLanguageCountry != null) {
+ locales.add(systemLocaleLanguageCountry);
+ }
+ if (systemLocaleLanguage != null) {
+ locales.add(systemLocaleLanguage);
+ }
+ locales.add(Locale.US);
+ locales.add(Locale.UK);
+ locales.add(Locale.ENGLISH);
+ }
+ return locales;
+ }
+}
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
similarity index 98%
rename from services/core/java/com/android/server/TextServicesManagerService.java
rename to services/core/java/com/android/server/textservices/TextServicesManagerService.java
index 40f81b3..23c29f8 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.textservices;
import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.inputmethod.InputMethodUtils;
+import com.android.internal.inputmethod.SubtypeLocaleUtils;
import com.android.internal.textservice.ISpellCheckerService;
import com.android.internal.textservice.ISpellCheckerServiceCallback;
import com.android.internal.textservice.ISpellCheckerSession;
import com.android.internal.textservice.ISpellCheckerSessionListener;
import com.android.internal.textservice.ITextServicesManager;
import com.android.internal.textservice.ITextServicesSessionListener;
-import com.android.internal.textservice.LazyIntToIntMap;
import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
import org.xmlpull.v1.XmlPullParserException;
@@ -461,7 +461,7 @@
// is pre-installed or not.
final Locale systemLocal = mContext.getResources().getConfiguration().locale;
final ArrayList<Locale> suitableLocales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(systemLocal);
+ LocaleUtils.getSuitableLocalesForSpellChecker(systemLocal);
if (DBG) {
Slog.w(TAG, "findAvailSystemSpellCheckerLocked suitableLocales="
+ Arrays.toString(suitableLocales.toArray(new Locale[suitableLocales.size()])));
@@ -475,7 +475,7 @@
final int subtypeCount = info.getSubtypeCount();
for (int subtypeIndex = 0; subtypeIndex < subtypeCount; ++subtypeIndex) {
final SpellCheckerSubtype subtype = info.getSubtypeAt(subtypeIndex);
- final Locale subtypeLocale = InputMethodUtils.constructLocaleFromString(
+ final Locale subtypeLocale = SubtypeLocaleUtils.constructLocaleFromString(
subtype.getLocale());
if (locale.equals(subtypeLocale)) {
// TODO: We may have more spell checkers that fall into this category.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 83075ed..524ca17 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -23,6 +23,8 @@
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.res.CompatibilityInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
@@ -267,6 +269,9 @@
public abstract void onProcessRemoved(String name, int uid);
public abstract void onCleanUpApplicationRecord(WindowProcessController proc);
public abstract int getTopProcessState();
+ public abstract boolean isHeavyWeightProcess(WindowProcessController proc);
+ public abstract void clearHeavyWeightProcessIfEquals(WindowProcessController proc);
+ public abstract void finishHeavyWeightApp();
public abstract boolean isSleeping();
public abstract boolean isShuttingDown();
@@ -281,4 +286,7 @@
public abstract void onPackageDataCleared(String name);
public abstract void onPackageUninstalled(String name);
+ public abstract void onPackageAdded(String name, boolean replacing);
+
+ public abstract CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5d0101f..eaaf804 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -76,6 +76,7 @@
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
+import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -150,7 +151,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
-import com.android.internal.view.IInputMethodClient;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -409,6 +409,9 @@
private InputMonitor mInputMonitor;
+ /** Caches the value whether told display manager that we have content. */
+ private boolean mLastHasContent;
+
/**
* The input method window for this display.
*/
@@ -569,7 +572,7 @@
if (!w.mLayoutAttached) {
if (mTmpInitial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.mContentChanged = false;
+ w.resetContentChanged();
}
if (w.mAttrs.type == TYPE_DREAM) {
// Don't layout windows behind a dream, so that if it does stuff like hide
@@ -614,7 +617,7 @@
|| w.mLayoutNeeded) {
if (mTmpInitial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.mContentChanged = false;
+ w.resetContentChanged();
}
w.mLayoutNeeded = false;
w.prelayout();
@@ -697,7 +700,7 @@
final WindowStateAnimator winAnimator = w.mWinAnimator;
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
+ w.resetContentChanged();
// Moved from updateWindowsAndWallpaperLocked().
if (w.mHasSurface) {
@@ -2407,6 +2410,7 @@
screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
+ proto.write(SURFACE_SIZE, mSurfaceSize);
proto.end(token);
}
@@ -2946,7 +2950,7 @@
}
}
- boolean inputMethodClientHasFocus(IInputMethodClient client) {
+ boolean isInputMethodClientFocus(int uid, int pid) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
return false;
@@ -2958,17 +2962,13 @@
Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
}
- final IInputMethodClient imeClient = imFocus.mSession.mClient;
-
if (DEBUG_INPUT_METHOD) {
- Slog.i(TAG_WM, "IM target client: " + imeClient);
- if (imeClient != null) {
- Slog.i(TAG_WM, "IM target client binder: " + imeClient.asBinder());
- Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder());
- }
+ Slog.i(TAG_WM, "IM target uid/pid: " + imFocus.mSession.mUid
+ + "/" + imFocus.mSession.mPid);
+ Slog.i(TAG_WM, "Requesting client uid/pid: " + uid + "/" + pid);
}
- return imeClient != null && imeClient.asBinder() == client.asBinder();
+ return imFocus.mSession.mUid == uid && imFocus.mSession.mPid == pid;
}
boolean hasSecureWindowOnScreen() {
@@ -3092,8 +3092,9 @@
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
prepareSurfaces();
+ mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
- mTmpApplySurfaceChangesTransactionState.displayHasContent,
+ mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
true /* inTraversal, must call performTraversalInTrans... below */);
@@ -3152,6 +3153,10 @@
}
}
+ int getSurfaceSize() {
+ return mSurfaceSize;
+ }
+
void performLayout(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
@@ -3252,7 +3257,7 @@
// TODO(b/68392460): We should screenshot Task controls directly
// but it's difficult at the moment as the Task doesn't have the
// correct size set.
- final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, 0, 1, inRotation, rot);
+ final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, inRotation, rot);
if (bitmap == null) {
Slog.w(TAG_WM, "Failed to take screenshot");
return null;
@@ -3606,6 +3611,18 @@
final int targetPosition = findPositionForStack(position, child, false /* adding */);
super.positionChildAt(targetPosition, child, includingParents);
+ if (includingParents) {
+ // We still want to move the display of this stack container to top because even the
+ // target position is adjusted to non-top, the intention of the condition is to have
+ // higher z-order to gain focus (e.g. moving a task of a fullscreen stack to front
+ // in a non-top display which is using picture-in-picture mode).
+ final int topChildPosition = getChildCount() - 1;
+ if (targetPosition < topChildPosition && position >= topChildPosition) {
+ getParent().positionChildAt(POSITION_TOP, this /* child */,
+ true /* includingParents */);
+ }
+ }
+
setLayoutNeeded();
}
@@ -4255,4 +4272,11 @@
InputMonitor getInputMonitor() {
return mInputMonitor;
}
+
+ /**
+ * @return Cached value whether we told display manager that we have content.
+ */
+ boolean getLastHasContent() {
+ return mLastHasContent;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 685c444..bd82553 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -732,10 +733,13 @@
boolean shouldUpdateOrientationListener = false;
// Configure rotation suggestions.
- final int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
- Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
- Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
- UserHandle.USER_CURRENT);
+ final int showRotationSuggestions =
+ ActivityManager.isLowRamDeviceStatic()
+ ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
+ : Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
+ UserHandle.USER_CURRENT);
if (mShowRotationSuggestions != showRotationSuggestions) {
mShowRotationSuggestions = showRotationSuggestions;
shouldUpdateOrientationListener = true;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 3309798..ef3a770 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -268,11 +268,11 @@
}
final InputWindowHandle dragWindowHandle =
mService.mDragDropController.getInputWindowHandleLocked();
- if (dragWindowHandle != null) {
- addInputWindowHandle(dragWindowHandle);
- } else {
+ if (dragWindowHandle == null) {
Slog.w(TAG_WM, "Drag is in progress but there is no "
+ "drag window handle.");
+ } else if (dragWindowHandle.displayId == mDisplayId) {
+ addInputWindowHandle(dragWindowHandle);
}
}
@@ -283,11 +283,11 @@
}
final InputWindowHandle dragWindowHandle =
mService.mTaskPositioningController.getDragWindowHandleLocked();
- if (dragWindowHandle != null) {
- addInputWindowHandle(dragWindowHandle);
- } else {
+ if (dragWindowHandle == null) {
Slog.e(TAG_WM,
"Repositioning is in progress but there is no drag window handle.");
+ } else if (dragWindowHandle.displayId == mDisplayId) {
+ addInputWindowHandle(dragWindowHandle);
}
}
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 6f597277..95ca0a6 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -16,12 +16,12 @@
package com.android.server.wm;
-import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.Surface.Rotation;
import android.view.SurfaceControl.Transaction;
@@ -33,10 +33,10 @@
/**
* Helper class for seamless rotation.
*
- * Works by transforming the window token back into the old display rotation.
+ * Works by transforming the {@link WindowState} back into the old display rotation.
*
- * Uses deferTransactionUntil instead of latching on the buffer size to allow for seamless 180
- * degree rotations.
+ * Uses {@link android.view.SurfaceControl#deferTransactionUntil(Surface, long)} instead of
+ * latching on the buffer size to allow for seamless 180 degree rotations.
*/
public class SeamlessRotator {
@@ -45,7 +45,7 @@
private final int mOldRotation;
private final int mNewRotation;
- public SeamlessRotator(int oldRotation, int newRotation, DisplayInfo info) {
+ public SeamlessRotator(@Rotation int oldRotation, @Rotation int newRotation, DisplayInfo info) {
mOldRotation = oldRotation;
mNewRotation = newRotation;
@@ -60,11 +60,16 @@
}
/**
- * Applies a transform to the window token's surface that undoes the effect of the global
+ * Applies a transform to the {@link WindowState} surface that undoes the effect of the global
* display rotation.
*/
- public void unrotate(Transaction transaction, WindowToken token) {
- transaction.setMatrix(token.getSurfaceControl(), mTransform, mFloat9);
+ public void unrotate(Transaction transaction, WindowState win) {
+ transaction.setMatrix(win.getSurfaceControl(), mTransform, mFloat9);
+
+ // WindowState sets the position of the window so transform the position and update it.
+ final float[] winSurfacePos = {win.mLastSurfacePosition.x, win.mLastSurfacePosition.y};
+ mTransform.mapPoints(winSurfacePos);
+ transaction.setPosition(win.getSurfaceControl(), winSurfacePos[0], winSurfacePos[1]);
}
/**
@@ -78,27 +83,23 @@
}
/**
- * Removes the transform to the window token's surface that undoes the effect of the global
- * display rotation.
+ * Removes the transform and sets the previously known surface position for {@link WindowState}
+ * surface that undoes the effect of the global display rotation.
*
- * Removing the transform and the result of the WindowState's layout are both tied to the
- * WindowState's next frame, such that they apply at the same time the client draws the
+ * Removing the transform and the result of the {@link WindowState} layout are both tied to the
+ * {@link WindowState} next frame, such that they apply at the same time the client draws the
* window in the new orientation.
*/
- public void finish(WindowToken token, WindowState win) {
+ public void finish(WindowState win) {
mTransform.reset();
- token.getPendingTransaction().setMatrix(token.mSurfaceControl, mTransform, mFloat9);
+ final Transaction t = win.getPendingTransaction();
+ t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
+ t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
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());
- win.getPendingTransaction().deferTransactionUntil(
- win.mWinAnimator.mSurfaceController.mSurfaceControl,
- win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
- win.getFrameNumber());
+ t.deferTransactionUntil(win.mSurfaceControl,
+ win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
+ t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
+ win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index f9a71d3..acc9c03 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.util.MergedConfiguration;
@@ -56,10 +55,6 @@
import android.view.WindowManager;
import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.server.LocalServices;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -73,8 +68,6 @@
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
- final IInputMethodClient mClient;
- final InputMethodManagerInternal mInputMethodManagerInternal;
final int mUid;
final int mPid;
private final String mStringName;
@@ -95,17 +88,9 @@
private String mPackageName;
private String mRelayoutTag;
- public Session(WindowManagerService service, IWindowSessionCallback callback,
- IInputMethodClient client, IInputContext inputContext) {
+ public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
mCallback = callback;
- mClient = client;
- // Depending on the timing when Session object gets called and SystemServer#mFactoryTestMode
- // this could be null, right?
- final InputMethodManagerInternal immInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- mInputMethodManagerInternal =
- immInternal != null ? immInternal : InputMethodManagerInternal.NOP;
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
@@ -134,12 +119,11 @@
sb.append("}");
mStringName = sb.toString();
- mInputMethodManagerInternal.addClient(client, inputContext, mUid, mPid);
try {
- client.asBinder().linkToDeath(this, 0);
+ mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
// The caller has died, so we can just forget about this.
- mInputMethodManagerInternal.removeClient(client);
+ // Hmmm, should we call killSessionLocked()??
}
}
@@ -159,9 +143,8 @@
@Override
public void binderDied() {
- mInputMethodManagerInternal.removeClient(mClient);
synchronized(mService.mWindowMap) {
- mClient.asBinder().unlinkToDeath(this, 0);
+ mCallback.asBinder().unlinkToDeath(this, 0);
mClientDead = true;
killSessionLocked();
}
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index c99329a..30d84fb 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -12,20 +12,6 @@
]
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.wm."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "android.support.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "WmTests",
"options": [
{
@@ -35,7 +21,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
@@ -45,14 +31,6 @@
"name": "CtsWindowManagerDeviceTestCases"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.wm."
- }
- ]
- },
- {
"name": "WmTests",
"options": [
{
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index eb419c9..cc23ab6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,6 +29,8 @@
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.ID;
+import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
+import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -297,6 +299,12 @@
return boundsChange;
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ updateSurfaceSize(dc);
+ super.onDisplayChanged(dc);
+ }
+
/**
* Sets the bounds used to calculate the insets. See
* {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
@@ -703,6 +711,8 @@
getBounds().writeToProto(proto, BOUNDS);
mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
proto.write(DEFER_REMOVAL, mDeferRemoval);
+ proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
+ proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 8effc6b..d2696c0 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -19,17 +19,17 @@
import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
import android.annotation.IntDef;
-import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.graphics.Point;
import android.graphics.Rect;
@@ -51,7 +51,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
-import com.android.server.wm.WindowManagerService.H;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -209,7 +208,6 @@
// 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) {
@@ -237,7 +235,7 @@
}
/**
- * @param display The Display that the window being dragged is on.
+ * @param displayContent The Display that the window being dragged is on.
*/
void register(DisplayContent displayContent) {
final Display display = displayContent.getDisplay();
@@ -303,6 +301,9 @@
}
mDisplayContent.pauseRotationLocked();
+ // Notify InputMonitor to take mDragWindowHandle.
+ mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
+
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
@@ -334,6 +335,9 @@
mDragApplicationHandle = null;
mDragEnded = true;
+ // Notify InputMonitor to remove mDragWindowHandle.
+ mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
+
// Resume rotations after a drag.
if (DEBUG_ORIENTATION) {
Slog.d(TAG, "Resuming rotation after re-position");
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 9cdc6b7..33416f6 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -20,14 +20,13 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.Nullable;
-import android.app.IActivityManager;
import android.app.IActivityTaskManager;
-import android.os.RemoteException;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.util.Slog;
-import android.view.Display;
import android.view.IWindow;
+
import com.android.internal.annotations.GuardedBy;
import com.android.server.input.InputManagerService;
import com.android.server.input.InputWindowHandle;
@@ -124,10 +123,8 @@
return false;
}
- Display display = displayContent.getDisplay();
mTaskPositioner = TaskPositioner.create(mService);
mTaskPositioner.register(displayContent);
- 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
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8e704a8..e860939 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -20,13 +20,14 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.SurfaceControl.Transaction;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.CallSuper;
import android.annotation.IntDef;
@@ -503,6 +504,24 @@
}
}
+ /**
+ * Update the surface size when display changed in order to avoid children being bound by the
+ * old display size.
+ *
+ * Note that we don't want to apply this to all layers, but only limiting this to layers that
+ * don't set their own size ({@link Task}, {@link WindowState} and {@link WindowToken}).
+ */
+ void updateSurfaceSize(DisplayContent dc) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ final int newSurfaceSize = dc.getSurfaceSize();
+ if (mSurfaceControl.getWidth() != newSurfaceSize) {
+ getPendingTransaction().setSize(mSurfaceControl, newSurfaceSize, newSurfaceSize);
+ }
+ }
+
void setWaitingForDrawnIfResizingChanged() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 9381fc6..20a874b 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -36,7 +36,6 @@
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
-import android.view.WindowManager;
import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -191,6 +190,10 @@
private final Rect mTmpRect = new Rect();
+ private boolean mHasOutsets;
+
+ private boolean mContentChanged;
+
public WindowFrames() {
}
@@ -237,11 +240,9 @@
* Calculates the outsets for this windowFrame. The outsets are calculated by the area between
* the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then
* {@link #mOutsets} is set to empty.
- *
- * @param hasOutsets Whether this frame has outsets.
*/
- void calculateOutsets(boolean hasOutsets) {
- if (hasOutsets) {
+ void calculateOutsets() {
+ if (mHasOutsets) {
InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
} else {
mOutsets.setEmpty();
@@ -249,7 +250,8 @@
}
/**
- * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
+ * Calculate the insets for the type
+ * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
*
* @param cutoutInsets The insets for the cutout.
*/
@@ -286,8 +288,9 @@
boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
&& mFrame.bottom > windowBounds.bottom;
- mTmpRect.set(mFrame.left, mFrame.top, overrideRightInset ? mTmpRect.right : mFrame.right,
- overrideBottomInset ? mTmpRect.bottom : mFrame.bottom);
+ mTmpRect.set(mFrame.left, mFrame.top,
+ overrideRightInset ? windowBounds.right : mFrame.right,
+ overrideBottomInset ? windowBounds.bottom : mFrame.bottom);
InsetUtils.insetsBetweenFrames(mTmpRect, mContentFrame, mContentInsets);
InsetUtils.insetsBetweenFrames(mTmpRect, mVisibleFrame, mVisibleInsets);
@@ -366,6 +369,28 @@
mLastContentInsets.set(-1, -1, -1, -1);
}
+ /**
+ * Sets whether the frame has outsets.
+ */
+ public void setHasOutsets(boolean hasOutsets) {
+ mHasOutsets = hasOutsets;
+ }
+
+ /**
+ * Sets whether the content has changed. This means that either the size or parent frame has
+ * changed.
+ */
+ public void setContentChanged(boolean contentChanged) {
+ mContentChanged = contentChanged;
+ }
+
+ /**
+ * @see #setContentChanged(boolean)
+ */
+ boolean hasContentChanged() {
+ return mContentChanged;
+ }
+
public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
mParentFrame.writeToProto(proto, PARENT_FRAME);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 793ce60..57cae39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,7 +30,6 @@
import android.view.MagnificationSpec;
import android.view.WindowInfo;
-import com.android.internal.view.IInputMethodClient;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
@@ -444,9 +443,14 @@
public abstract boolean isUidFocused(int uid);
/**
- * Returns {@code true} if a process that is identified by {@code client} has IME focus.
+ * Checks whether the specified process has IME focus or not.
+ *
+ * @param uid UID of the process to be queried
+ * @param pid PID of the process to be queried
+ * @return {@code true} if a process that is identified by {@code uid} and {@code pid} has IME
+ * focus
*/
- public abstract boolean inputMethodClientHasFocus(IInputMethodClient client);
+ public abstract boolean isInputMethodClientFocus(int uid, int pid);
/**
* Return the display Id for given window.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2ed09ae..71ce1d9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -232,8 +232,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
@@ -5037,12 +5035,8 @@
// -------------------------------------------------------------
@Override
- public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
- IInputContext inputContext) {
- if (client == null) throw new IllegalArgumentException("null client");
- if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- Session session = new Session(this, callback, client, inputContext);
- return session;
+ public IWindowSession openSession(IWindowSessionCallback callback) {
+ return new Session(this, callback);
}
@Override
@@ -5075,9 +5069,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5110,9 +5102,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5192,9 +5182,7 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
+
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
@@ -5241,9 +5229,6 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser",
@@ -5272,9 +5257,6 @@
throw new SecurityException("Must hold permission " +
android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- if (displayId != DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can only set the default display");
- }
final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser",
@@ -7411,32 +7393,32 @@
}
}
- public boolean inputMethodClientHasFocus(IInputMethodClient client) {
- boolean hasFocus;
- synchronized (mWindowMap) {
- // Check all displays if any input method window has focus.
- for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mRoot.mChildren.get(i);
- if (displayContent.inputMethodClientHasFocus(client)) {
+ @Override
+ public boolean isInputMethodClientFocus(int uid, int pid) {
+ synchronized (mWindowMap) {
+ // Check all displays if any input method window has focus.
+ for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mRoot.mChildren.get(i);
+ if (displayContent.isInputMethodClientFocus(uid, pid)) {
+ return true;
+ }
+ }
+
+ // Okay, how about this... what is the current focus?
+ // It seems in some cases we may not have moved the IM
+ // target window, such as when it was in a pop-up window,
+ // so let's also look at the current focus. (An example:
+ // go to Gmail, start searching so the keyboard goes up,
+ // press home. Sometimes the IME won't go down.)
+ // Would be nice to fix this more correctly, but it's
+ // way at the end of a release, and this should be good enough.
+ if (mCurrentFocus != null && mCurrentFocus.mSession.mUid == uid
+ && mCurrentFocus.mSession.mPid == pid) {
return true;
}
}
-
- // Okay, how about this... what is the current focus?
- // It seems in some cases we may not have moved the IM
- // target window, such as when it was in a pop-up window,
- // so let's also look at the current focus. (An example:
- // go to Gmail, start searching so the keyboard goes up,
- // press home. Sometimes the IME won't go down.)
- // Would be nice to fix this more correctly, but it's
- // way at the end of a release, and this should be good enough.
- if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
- && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
- return true;
- }
+ return false;
}
- return false;
- }
@Override
public int getDisplayIdForWindow(IBinder windowToken) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 6e0ccfd..831418b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -20,7 +20,6 @@
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -28,10 +27,6 @@
import android.view.Display;
import android.view.IWindowManager;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -87,21 +82,48 @@
return -1;
}
+ private int getDisplayId(String opt) {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String option = "-d".equals(opt) ? opt : getNextOption();
+ if (option != null && "-d".equals(option)) {
+ try {
+ displayId = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e);
+ }
+ }
+ return displayId;
+ }
+
+ private void printInitialDisplaySize(PrintWriter pw , int displayId) {
+ final Point initialSize = new Point();
+ final Point baseSize = new Point();
+
+ try {
+ mInterface.getInitialDisplaySize(displayId, initialSize);
+ mInterface.getBaseDisplaySize(displayId, baseSize);
+ pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+ if (!initialSize.equals(baseSize)) {
+ pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
+ }
+ } catch (RemoteException e) {
+ // Can't call getInitialDisplaySize() on IWindowManager or
+ // Can't call getBaseDisplaySize() on IWindowManager
+ pw.println("Remote exception: " + e);
+ }
+ }
+
private int runDisplaySize(PrintWriter pw) throws RemoteException {
String size = getNextArg();
int w, h;
+ final int displayId = getDisplayId(size);
if (size == null) {
- Point initialSize = new Point();
- Point baseSize = new Point();
- try {
- mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
- mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
- pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
- if (!initialSize.equals(baseSize)) {
- pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
- }
- } catch (RemoteException e) {
- }
+ printInitialDisplaySize(pw, displayId);
+ return 0;
+ } else if ("-d".equals(size)) {
+ printInitialDisplaySize(pw, displayId);
return 0;
} else if ("reset".equals(size)) {
w = h = -1;
@@ -114,8 +136,8 @@
String wstr = size.substring(0, div);
String hstr = size.substring(div+1);
try {
- w = parseDimension(wstr);
- h = parseDimension(hstr);
+ w = parseDimension(wstr, displayId);
+ h = parseDimension(hstr, displayId);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: bad number " + e);
return -1;
@@ -123,27 +145,38 @@
}
if (w >= 0 && h >= 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+ mInterface.setForcedDisplaySize(displayId, w, h);
} else {
- mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+ mInterface.clearForcedDisplaySize(displayId);
}
return 0;
}
+ private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
+ try {
+ final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
+ final int baseDensity = mInterface.getBaseDisplayDensity(displayId);
+ pw.println("Physical density: " + initialDensity);
+ if (initialDensity != baseDensity) {
+ pw.println("Override density: " + baseDensity);
+ }
+ } catch (RemoteException e) {
+ // Can't call getInitialDisplayDensity() on IWindowManager or
+ // Can't call getBaseDisplayDensity() on IWindowManager
+ pw.println("Remote exception: " + e);
+ }
+ }
+
private int runDisplayDensity(PrintWriter pw) throws RemoteException {
String densityStr = getNextArg();
int density;
+ final int displayId = getDisplayId(densityStr);
+
if (densityStr == null) {
- try {
- int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
- int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
- pw.println("Physical density: " + initialDensity);
- if (initialDensity != baseDensity) {
- pw.println("Override density: " + baseDensity);
- }
- } catch (RemoteException e) {
- }
+ printInitialDisplayDensity(pw, displayId);
+ return 0;
+ } else if ("-d".equals(densityStr)) {
+ printInitialDisplayDensity(pw, displayId);
return 0;
} else if ("reset".equals(densityStr)) {
density = -1;
@@ -161,11 +194,10 @@
}
if (density > 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+ mInterface.setForcedDisplayDensityForUser(displayId, density,
UserHandle.USER_CURRENT);
} else {
- mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+ mInterface.clearForcedDisplayDensityForUser(displayId,
UserHandle.USER_CURRENT);
}
return 0;
@@ -174,6 +206,7 @@
private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
String overscanStr = getNextArgRequired();
Rect rect = new Rect();
+ final int displayId = getDisplayId(overscanStr);
if ("reset".equals(overscanStr)) {
rect.set(0, 0, 0, 0);
} else {
@@ -190,17 +223,16 @@
rect.bottom = Integer.parseInt(matcher.group(4));
}
- mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right,
- rect.bottom);
+ mInterface.setOverscan(displayId, rect.left, rect.top, rect.right, rect.bottom);
return 0;
}
private int runDisplayScaling(PrintWriter pw) throws RemoteException {
String scalingStr = getNextArgRequired();
if ("auto".equals(scalingStr)) {
- mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+ mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 0);
} else if ("off".equals(scalingStr)) {
- mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+ mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 1);
} else {
getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
return -1;
@@ -213,14 +245,14 @@
return 0;
}
- private int parseDimension(String s) throws NumberFormatException {
+ private int parseDimension(String s, int displayId) throws NumberFormatException {
if (s.endsWith("px")) {
return Integer.parseInt(s.substring(0, s.length() - 2));
}
if (s.endsWith("dp")) {
int density;
try {
- density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+ density = mInterface.getBaseDisplayDensity(displayId);
} catch (RemoteException e) {
density = DisplayMetrics.DENSITY_DEFAULT;
}
@@ -236,14 +268,14 @@
pw.println("Window manager (window) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" size [reset|WxH|WdpxHdp]");
+ pw.println(" size [reset|WxH|WdpxHdp] [-d DISPLAY_ID]");
pw.println(" Return or override display size.");
pw.println(" width and height in pixels unless suffixed with 'dp'.");
- pw.println(" density [reset|DENSITY]");
+ pw.println(" density [reset|DENSITY] [-d DISPLAY_ID]");
pw.println(" Return or override display density.");
- pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM]");
+ pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]");
pw.println(" Set overscan area for display.");
- pw.println(" scaling [off|auto]");
+ pw.println(" scaling [off|auto] [-d DISPLAY_ID]");
pw.println(" Set display scaling mode.");
pw.println(" dismiss-keyguard");
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5272b66..69ff815 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -359,8 +359,6 @@
*/
private final Rect mInsetFrame = new Rect();
- boolean mContentChanged;
-
// If a window showing a wallpaper: the requested offset for the
// wallpaper; if a wallpaper window: the currently applied offset.
float mWallpaperX = -1;
@@ -593,14 +591,14 @@
if (mForceSeamlesslyRotate || requested) {
mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
- mPendingSeamlessRotate.unrotate(transaction, this.mToken);
+ mPendingSeamlessRotate.unrotate(transaction, this);
mService.markForSeamlessRotation(this, true);
}
}
void finishSeamlessRotation() {
if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.finish(this.mToken, this);
+ mPendingSeamlessRotate.finish(this);
mFinishSeamlessRotateFrameNumber = getFrameNumber();
mPendingSeamlessRotate = null;
mService.markForSeamlessRotation(this, false);
@@ -787,7 +785,7 @@
}
@Override
- public void computeFrameLw(WindowFrames windowFrames) {
+ public void computeFrameLw() {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -796,8 +794,6 @@
return;
}
mHaveFrame = true;
- mWindowFrames.setParentFrameWasClippedByDisplayCutout(
- windowFrames.parentFrameWasClippedByDisplayCutout());
final Task task = getTask();
final boolean inFullscreenContainer = inFullscreenContainer();
@@ -827,10 +823,9 @@
final int layoutYDiff;
if (inFullscreenContainer || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
- mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame);
- mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame);
- layoutDisplayFrame = windowFrames.mDisplayFrame;
- layoutContainingFrame = windowFrames.mParentFrame;
+ mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
+ layoutDisplayFrame = mWindowFrames.mDisplayFrame;
+ layoutContainingFrame = mWindowFrames.mParentFrame;
layoutXDiff = 0;
layoutYDiff = 0;
} else {
@@ -849,17 +844,17 @@
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
- > windowFrames.mContentFrame.bottom) {
+ > mWindowFrames.mContentFrame.bottom) {
// In freeform we want to move the top up directly.
// TODO: Investigate why this is contentFrame not parentFrame.
mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
- - windowFrames.mContentFrame.bottom;
+ - mWindowFrames.mContentFrame.bottom;
} else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
- > windowFrames.mParentFrame.bottom) {
+ > mWindowFrames.mParentFrame.bottom) {
// But in docked we want to behave like fullscreen and behave as if the task
// were given smaller bounds for the purposes of layout. Skip adjustments for
// the pinned stack, they are handled separately in the PinnedStackController.
- mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
+ mWindowFrames.mContainingFrame.bottom = mWindowFrames.mParentFrame.bottom;
}
}
@@ -868,7 +863,7 @@
// if it wasn't set already. No need to intersect it with the (visible)
// "content frame" since it is allowed to be outside the visible desktop.
if (mWindowFrames.mContainingFrame.isEmpty()) {
- mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame);
+ mWindowFrames.mContainingFrame.set(mWindowFrames.mContentFrame);
}
}
@@ -878,10 +873,11 @@
// PIP edge case: When going from pinned to fullscreen, we apply a
// tempInsetFrame for the full task - but we're still at the start of the animation.
// To prevent a jump if there's a letterbox, restrict to the parent frame.
- mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
- mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
+ mInsetFrame.intersectUnchecked(mWindowFrames.mParentFrame);
+ mWindowFrames.mContainingFrame.intersectUnchecked(mWindowFrames.mParentFrame);
}
+ layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
layoutXDiff =
!mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
@@ -892,41 +888,24 @@
layoutContainingFrame =
!mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
- subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame,
- windowFrames.mDisplayFrame, mTmpRect);
+ subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
+ mTmpRect);
if (!layoutInParentFrame()) {
subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
- windowFrames.mParentFrame, mTmpRect);
- subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
+ mWindowFrames.mParentFrame, mTmpRect);
+ subtractInsets(mInsetFrame, layoutContainingFrame, mWindowFrames.mParentFrame,
mTmpRect);
}
- layoutDisplayFrame = windowFrames.mDisplayFrame;
layoutDisplayFrame.intersect(layoutContainingFrame);
}
final int pw = mWindowFrames.mContainingFrame.width();
final int ph = mWindowFrames.mContainingFrame.height();
- if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) {
- //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
- // + " to " + parentFrame);
- mWindowFrames.mParentFrame.set(windowFrames.mParentFrame);
- mContentChanged = true;
- }
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
mLastRequestedWidth = mRequestedWidth;
mLastRequestedHeight = mRequestedHeight;
- mContentChanged = true;
- }
-
- mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame);
- mWindowFrames.mContentFrame.set(windowFrames.mContentFrame);
- mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame);
- mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame);
- mWindowFrames.mStableFrame.set(windowFrames.mStableFrame);
- final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty();
- if (hasOutsets) {
- mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame);
+ mWindowFrames.setContentChanged(true);
}
final int fw = mWindowFrames.mFrame.width();
@@ -935,7 +914,7 @@
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
// Calculate the outsets before the content frame gets shrinked to the window frame.
- mWindowFrames.calculateOutsets(hasOutsets);
+ mWindowFrames.calculateOutsets();
// Make sure the content and visible frames are inside of the
// final window frame.
@@ -997,7 +976,7 @@
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
+ final WmDisplayCutout c = mWindowFrames.mDisplayCutout.calculateRelativeTo(
mWindowFrames.mDisplayFrame);
mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
} else {
@@ -1006,7 +985,7 @@
}
mWindowFrames.setDisplayCutout(
- windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
+ mWindowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
// Offset the actual frame by the amount layout frame is off.
mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
@@ -1280,6 +1259,7 @@
@Override
void onDisplayChanged(DisplayContent dc) {
+ updateSurfaceSize(dc);
super.onDisplayChanged(dc);
// Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
if (dc != null) {
@@ -1733,7 +1713,7 @@
* sense to call from performLayoutAndPlaceSurfacesLockedInner().)
*/
private boolean hasMoved() {
- return mHasSurface && (mContentChanged || mMovedByResize)
+ return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize)
&& !mAnimatingExit
&& (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
|| mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
@@ -4762,6 +4742,15 @@
return mWindowFrames.mVisibleInsets;
}
+ @Override
+ public WindowFrames getWindowFrames() {
+ return mWindowFrames;
+ }
+
+ void resetContentChanged() {
+ mWindowFrames.setContentChanged(false);
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fec8039..979149a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1176,15 +1176,17 @@
if (mIsWallpaper) {
w.dispatchWallpaperVisibility(true);
}
- // This draw means the difference between unique content and mirroring.
- // Run another pass through performLayout to set mHasContent in the
- // LogicalDisplay.
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- FINISH_LAYOUT_REDO_ANIM);
- if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(
- "showSurfaceRobustlyLocked " + w,
- mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+ if (!w.getDisplayContent().getLastHasContent()) {
+ // This draw means the difference between unique content and mirroring.
+ // Run another pass through performLayout to set mHasContent in the
+ // LogicalDisplay.
+ mAnimator.setPendingLayoutChanges(w.getDisplayId(),
+ FINISH_LAYOUT_REDO_ANIM);
+ if (DEBUG_LAYOUT_REPEATS) {
+ mService.mWindowPlacerLocked.debugLayoutRepeats(
+ "showSurfaceRobustlyLocked " + w,
+ mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+ }
}
} else {
w.setOrientationChanging(false);
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e411c0a..8972c38 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -275,6 +276,7 @@
// to another display before the window behind
// it is ready.
+ updateSurfaceSize(dc);
super.onDisplayChanged(dc);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0ca0835..e76afa3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2588,12 +2588,32 @@
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
throws SecurityException {
+ return getActiveAdminOrCheckPermissionForCallerLocked(who,
+ reqPolicy, /* permission= */ null);
+ }
+
+ /**
+ * Finds an active admin for the caller then checks {@code permission} if admin check failed.
+ *
+ * @return an active admin or {@code null} if there is no active admin but
+ * {@code permission} is granted
+ * @throws SecurityException if caller neither has an active admin nor {@code permission}
+ */
+ @Nullable
+ ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
+ ComponentName who,
+ int reqPolicy,
+ @Nullable String permission) throws SecurityException {
ensureLocked();
final int callingUid = mInjector.binderGetCallingUid();
ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
if (result != null) {
return result;
+ } else if (permission != null
+ && (mContext.checkCallingPermission(permission)
+ == PackageManager.PERMISSION_GRANTED)) {
+ return null;
}
if (who != null) {
@@ -2605,7 +2625,7 @@
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
- + " does not own the device");
+ + " does not own the device");
}
if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
@@ -2621,20 +2641,39 @@
+ admin.info.getTagForPolicy(reqPolicy));
} else {
throw new SecurityException("No active admin owned by uid "
- + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy);
+ + callingUid + " for policy #" + reqPolicy);
}
}
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy, boolean parent)
throws SecurityException {
+ return getActiveAdminOrCheckPermissionForCallerLocked(
+ who, reqPolicy, parent, /* permission= */ null);
+ }
+
+ /**
+ * Finds an active admin for the caller then checks {@code permission} if admin check failed.
+ *
+ * @return an active admin or {@code null} if there is no active admin but
+ * {@code permission} is granted
+ * @throws SecurityException if caller neither has an active admin nor {@code permission}
+ */
+ @Nullable
+ ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
+ ComponentName who,
+ int reqPolicy,
+ boolean parent,
+ @Nullable String permission) throws SecurityException {
ensureLocked();
if (parent) {
enforceManagedProfile(mInjector.userHandleGetCallingUserId(),
"call APIs on the parent profile");
}
- ActiveAdmin admin = getActiveAdminForCallerLocked(who, reqPolicy);
+ ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ who, reqPolicy, permission);
return parent ? admin.getParentActiveAdmin() : admin;
}
+
/**
* Find the admin for the component and userId bit of the uid, then check
* the admin's uid matches the uid.
@@ -4744,10 +4783,15 @@
preN = getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M;
} else {
- // Otherwise, make sure the caller has any active admin with the right policy.
- admin = getActiveAdminForCallerLocked(null,
- DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
- preN = getTargetSdk(admin.info.getPackageName(),
+ // Otherwise, make sure the caller has any active admin with the right policy or
+ // the required permission.
+ admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ null,
+ DeviceAdminInfo.USES_POLICY_RESET_PASSWORD,
+ android.Manifest.permission.RESET_PASSWORD);
+ // Cannot be preN if admin is null because an exception would have been
+ // thrown before getting here
+ preN = admin == null ? false : getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M;
// As of N, password resetting to empty/null is not allowed anymore.
@@ -4763,9 +4807,9 @@
// As of N, password cannot be changed by the admin if it is already set.
if (isLockScreenSecureUnchecked(userHandle)) {
if (!preN) {
- throw new SecurityException("Admin cannot change current password");
+ throw new SecurityException("Cannot change current password");
} else {
- Slog.e(LOG_TAG, "Admin cannot change current password");
+ Slog.e(LOG_TAG, "Cannot change current password");
return false;
}
}
@@ -5136,31 +5180,37 @@
final int callingUserId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- final ActiveAdmin admin = getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
-
+ // Make sure the caller has any active admin with the right policy or
+ // the required permission.
+ final ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ null,
+ DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
+ parent,
+ android.Manifest.permission.LOCK_DEVICE);
final long ident = mInjector.binderClearCallingIdentity();
try {
- final ComponentName adminComponent = admin.info.getComponent();
- // Evict key
- if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
- enforceManagedProfile(
- callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
- if (!isProfileOwner(adminComponent, callingUserId)) {
- throw new SecurityException("Only profile owner admins can set "
- + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ final ComponentName adminComponent = admin == null ?
+ null : admin.info.getComponent();
+ if (adminComponent != null) {
+ // For Profile Owners only, callers with only permission not allowed.
+ if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
+ // Evict key
+ enforceManagedProfile(
+ callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ if (!isProfileOwner(adminComponent, callingUserId)) {
+ throw new SecurityException("Only profile owner admins can set "
+ + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ }
+ if (parent) {
+ throw new IllegalArgumentException(
+ "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
+ }
+ if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
+ throw new UnsupportedOperationException(
+ "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
+ }
+ mUserManager.evictCredentialEncryptionKey(callingUserId);
}
- if (parent) {
- throw new IllegalArgumentException(
- "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
- }
- if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
- throw new UnsupportedOperationException(
- "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
- }
- mUserManager.evictCredentialEncryptionKey(callingUserId);
}
// Lock all users unless this is a managed profile with a separate challenge
@@ -5179,7 +5229,7 @@
mInjector.getTrustManager().setDeviceLockedForUser(userToLock, true);
}
- if (SecurityLog.isLoggingEnabled()) {
+ if (SecurityLog.isLoggingEnabled() && adminComponent != null) {
final int affectedUserId =
parent ? getProfileParentId(callingUserId) : callingUserId;
SecurityLog.writeEvent(SecurityLog.TAG_REMOTE_LOCK,
@@ -5969,7 +6019,7 @@
success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
if (!success) {
Slog.w(LOG_TAG, "Couldn't remove user " + userId);
- } else if (isManagedProfile(userId)) {
+ } else if (isManagedProfile(userId) && !TextUtils.isEmpty(wipeReasonForUser)) {
sendWipeProfileNotification(wipeReasonForUser);
}
} catch (RemoteException re) {
@@ -5984,7 +6034,6 @@
if (!mHasFeature) {
return;
}
- Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
final ActiveAdmin admin;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bb08345..9d63305 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.storage.DeviceStorageMonitorService;
import com.android.server.telecom.TelecomLoaderService;
import com.android.server.textclassifier.TextClassificationManagerService;
+import com.android.server.textservices.TextServicesManagerService;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.tv.TvRemoteService;
@@ -797,6 +798,9 @@
boolean isWatch = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WATCH);
+ boolean enableVrService = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
+
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) {
throw new RuntimeException();
@@ -930,7 +934,7 @@
traceLog.traceEnd();
}, START_HIDL_SERVICES);
- if (!isWatch) {
+ if (!isWatch && enableVrService) {
traceBeginAndSlog("StartVrManagerService");
mSystemServiceManager.startService(VrManagerService.class);
traceEnd();
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 33010a1..823c0a1 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -430,6 +430,8 @@
params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
+ if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName());
+
for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -549,6 +551,20 @@
}
}
+ private byte getHopLimit(String upstreamIface) {
+ try {
+ int upstreamHopLimit = Integer.parseUnsignedInt(
+ mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
+ // Add one hop to account for this forwarding device
+ upstreamHopLimit++;
+ // Cap the hop limit to 255.
+ return (byte) Integer.min(upstreamHopLimit, 255);
+ } catch (Exception e) {
+ mLog.e("Failed to find upstream interface hop limit", e);
+ }
+ return RaParams.DEFAULT_HOPLIMIT;
+ }
+
private void setRaParams(RaParams newParams) {
if (mRaDaemon != null) {
final RaParams deprecatedParams =
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
index 1c025cf..aad7230 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
@@ -18,23 +18,22 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
-import android.os.Looper;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
-import java.util.ArrayList;
+
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
+import java.util.ArrayList;
/**
* Tests for AccessibilityGestureDetector
@@ -50,14 +49,6 @@
private AccessibilityGestureDetector mDetector;
private AccessibilityGestureDetector.Listener mResultListener;
-
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setUp() {
// Construct a mock Context.
@@ -67,7 +58,6 @@
Resources mockResources = mock(Resources.class);
when(mockResources.getDisplayMetrics()).thenReturn(displayMetricsMock);
Context contextMock = mock(Context.class);
- when(contextMock.getMainLooper()).thenReturn(Looper.myLooper());
when(contextMock.getResources()).thenReturn(mockResources);
// Construct a testable AccessibilityGestureDetector.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
index 4c0f38a..b9b6d55 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
@@ -29,7 +29,6 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
-import android.os.Looper;
import android.os.UserHandle;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -41,8 +40,8 @@
import com.android.internal.util.IntPair;
+import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -63,13 +62,6 @@
private MessageCapturingHandler mHandler;
private Instrumentation mInstrumentation;
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -77,6 +69,12 @@
mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
+ @After
+ public void tearDown() {
+ mHandler.removeAllMessages();
+ }
+
+
private AccessibilityManager createManager(boolean enabled) throws Exception {
long serviceReturnValue = IntPair.of(
(enabled) ? AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED : 0,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 412e844..66d9345 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -33,16 +33,14 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -74,13 +72,6 @@
MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -98,6 +89,12 @@
mMockGlobalActionPerformer);
}
+ @After
+ public void tearDown() {
+ mHandler.removeAllMessages();
+ }
+
+
@Test
public void bind_requestsContextToBindService() {
mConnection.bindLocked();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index a3decb9..44a514f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -16,19 +16,8 @@
package com.android.server.accessibility;
-import android.accessibilityservice.FingerprintGestureController;
-import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
-import android.accessibilityservice.IAccessibilityServiceConnection;
-import android.os.Looper;
-import android.support.test.filters.FlakyTest;
-
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import static android.accessibilityservice.FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN;
+import static android.accessibilityservice.FingerprintGestureController
+ .FINGERPRINT_GESTURE_SWIPE_DOWN;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -38,6 +27,15 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.accessibilityservice.FingerprintGestureController;
+import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
/**
* Tests for FingerprintGestureController.
* TODO: These tests aren't really for server code, so this isn't their ideal home.
@@ -47,13 +45,6 @@
@Mock FingerprintGestureCallback mMockFingerprintGestureCallback;
FingerprintGestureController mFingerprintGestureController;
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -97,7 +88,6 @@
}
@Test
- @FlakyTest
public void testDetectionActiveCallback_withHandler_shouldPostRunnableToHandler() {
MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> {
message.getCallback().run();
@@ -127,6 +117,8 @@
mFingerprintGestureController.onGestureDetectionActiveChanged(false);
assertFalse(messageCapturingHandler.hasMessages());
verifyZeroInteractions(mMockFingerprintGestureCallback);
+
+ messageCapturingHandler.removeAllMessages();
}
@Test
@@ -145,7 +137,6 @@
}
@Test
- @FlakyTest
public void testGestureCallback_withHandler_shouldPostRunnableToHandler() {
MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> {
message.getCallback().run();
@@ -167,5 +158,7 @@
mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
assertFalse(messageCapturingHandler.hasMessages());
verifyZeroInteractions(mMockFingerprintGestureCallback);
+
+ messageCapturingHandler.removeAllMessages();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
index 6ce7bbe..de7bc44 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -16,25 +16,6 @@
package com.android.server.accessibility;
-import android.accessibilityservice.FingerprintGestureController;
-import android.content.res.Resources;
-import android.hardware.fingerprint.IFingerprintService;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.view.KeyEvent;
-
-import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient;
-
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Arrays;
-import java.util.Collections;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
@@ -44,6 +25,22 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.accessibilityservice.FingerprintGestureController;
+import android.content.res.Resources;
+import android.hardware.fingerprint.IFingerprintService;
+import android.view.KeyEvent;
+
+import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+
/**
* Tests for FingerprintGestureDispatcher
*/
@@ -57,13 +54,6 @@
private MessageCapturingHandler mMessageCapturingHandler;
private FingerprintGestureDispatcher mFingerprintGestureDispatcher;
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -77,6 +67,12 @@
when(mGestureCapturingClient.isCapturingFingerprintGestures()).thenReturn(true);
}
+ @After
+ public void tearDown() {
+ mMessageCapturingHandler.removeAllMessages();
+ }
+
+
@Test
public void testNoServices_doesNotCrashOrConsumeGestures() {
mFingerprintGestureDispatcher.onClientActiveChanged(true);
@@ -171,7 +167,7 @@
}
@Test
- public void ifGestureDectionNotSupported_neverSaysAvailable() throws Exception {
+ public void ifGestureDetectionNotSupported_neverSaysAvailable() throws Exception {
when(mMockResources.getBoolean(anyInt())).thenReturn(false);
// Need to create a new dispatcher, since it picks up the resource value in its
// constructor. This is fine since hardware config values don't change dynamically.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 236b458..23ce483 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -40,6 +40,7 @@
import android.os.RemoteException;
import android.view.KeyEvent;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.KeyEventDispatcher.KeyEventFilter;
@@ -47,16 +48,14 @@
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
/**
* Tests for KeyEventDispatcher
@@ -68,7 +67,7 @@
private final KeyEvent mKeyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, 0x40);
private final KeyEvent mOtherKeyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, 0x50);
private final Object mLock = new Object();
- private MessageCapturingHandler mInputEventsHander;
+ private MessageCapturingHandler mInputEventsHandler;
private KeyEventDispatcher mKeyEventDispatcher;
private KeyEventFilter mKeyEventFilter1;
private KeyEventFilter mKeyEventFilter2;
@@ -77,23 +76,17 @@
private ArgumentCaptor<Integer> mFilter1SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
private ArgumentCaptor<Integer> mFilter2SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setUp() {
- mInputEventsHander = new MessageCapturingHandler();
+ Looper looper = InstrumentationRegistry.getContext().getMainLooper();
+ mInputEventsHandler = new MessageCapturingHandler(looper, null);
mMockPowerManagerService = mock(IPowerManager.class);
// TODO: It would be better to mock PowerManager rather than its binder, but the class is
// final.
PowerManager powerManager =
- new PowerManager(mock(Context.class), mMockPowerManagerService, new Handler());
- mMessageCapturingHandler = new MessageCapturingHandler();
- mKeyEventDispatcher = new KeyEventDispatcher(mInputEventsHander, SEND_FRAMEWORK_KEY_EVENT,
+ new PowerManager(mock(Context.class), mMockPowerManagerService, new Handler(looper));
+ mMessageCapturingHandler = new MessageCapturingHandler(looper, null);
+ mKeyEventDispatcher = new KeyEventDispatcher(mInputEventsHandler, SEND_FRAMEWORK_KEY_EVENT,
mLock, powerManager, mMessageCapturingHandler);
mKeyEventFilter1 = mock(KeyEventFilter.class);
@@ -107,10 +100,17 @@
.thenReturn(true);
}
+ @After
+ public void tearDown() {
+ mInputEventsHandler.removeAllMessages();
+ mMessageCapturingHandler.removeAllMessages();
+ }
+
+
@Test
public void testNotifyKeyEvent_withNoBoundServices_shouldReturnFalse() {
assertFalse(mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Collections.EMPTY_LIST));
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@Test
@@ -119,7 +119,7 @@
when(keyEventFilter.onKeyEvent((KeyEvent) anyObject(), anyInt())).thenReturn(false);
assertFalse(mKeyEventDispatcher
.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(keyEventFilter)));
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@Test
@@ -152,9 +152,9 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, false,
mFilter1SequenceCaptor.getValue());
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verifyZeroInteractions(mMockPowerManagerService);
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@Test
@@ -166,10 +166,10 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, true,
mFilter1SequenceCaptor.getValue());
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@Test
@@ -182,9 +182,9 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verifyZeroInteractions(mMockPowerManagerService);
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@Test
@@ -198,10 +198,10 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, true,
mFilter2SequenceCaptor.getValue());
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@Test
@@ -215,10 +215,10 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@Test
@@ -232,10 +232,10 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, true,
mFilter2SequenceCaptor.getValue());
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
// Each event should have its result set only once, but if it's set twice, we should ignore
@@ -249,14 +249,14 @@
mFilter1SequenceCaptor.getValue());
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, false,
mFilter1SequenceCaptor.getValue());
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
// Verify event is sent properly when other service responds
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verifyZeroInteractions(mMockPowerManagerService);
- assertFalse(mMessageCapturingHandler.isTimeoutPending());
+ assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -269,9 +269,9 @@
mKeyEvent, 0, Arrays.asList(mKeyEventFilter1, mKeyEventFilter2));
assertEquals(1, mMessageCapturingHandler.timedMessages.size());
- mKeyEventDispatcher.handleMessage(mMessageCapturingHandler.timedMessages.get(0));
+ mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verifyZeroInteractions(mMockPowerManagerService);
}
@@ -284,9 +284,9 @@
mFilter1SequenceCaptor.getValue());
assertEquals(1, mMessageCapturingHandler.timedMessages.size());
- mKeyEventDispatcher.handleMessage(mMessageCapturingHandler.timedMessages.get(0));
+ mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verifyZeroInteractions(mMockPowerManagerService);
}
@@ -300,9 +300,9 @@
mFilter1SequenceCaptor.getValue());
assertEquals(1, mMessageCapturingHandler.timedMessages.size());
- mKeyEventDispatcher.handleMessage(mMessageCapturingHandler.timedMessages.get(0));
+ mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
}
@@ -310,34 +310,34 @@
@Test
public void testEventTimesOut_thenServiceReturnsFalse_shouldPassToFrameworkOnce() {
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
assertEquals(1, mMessageCapturingHandler.timedMessages.size());
- mKeyEventDispatcher.handleMessage(mMessageCapturingHandler.timedMessages.get(0));
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- mInputEventsHander.removeMessages(SEND_FRAMEWORK_KEY_EVENT);
+ mInputEventsHandler.removeMessages(SEND_FRAMEWORK_KEY_EVENT);
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, false,
mFilter1SequenceCaptor.getValue());
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verifyZeroInteractions(mMockPowerManagerService);
}
@Test
public void testEventTimesOut_afterServiceReturnsFalse_shouldPassToFrameworkOnce() {
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, false,
mFilter1SequenceCaptor.getValue());
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- mInputEventsHander.removeMessages(SEND_FRAMEWORK_KEY_EVENT);
+ mInputEventsHandler.removeMessages(SEND_FRAMEWORK_KEY_EVENT);
assertEquals(1, mMessageCapturingHandler.timedMessages.size());
- mKeyEventDispatcher.handleMessage(mMessageCapturingHandler.timedMessages.get(0));
+ mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verifyZeroInteractions(mMockPowerManagerService);
}
@@ -349,9 +349,9 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, true,
mFilter1SequenceCaptor.getValue());
assertEquals(1, mMessageCapturingHandler.timedMessages.size());
- mKeyEventDispatcher.handleMessage(mMessageCapturingHandler.timedMessages.get(0));
+ mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
}
@@ -362,11 +362,11 @@
@Test
public void testFlushService_withPendingEvent_shouldPassToFramework() {
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.flush(mKeyEventFilter1);
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
}
@Test
@@ -377,7 +377,7 @@
mKeyEventDispatcher.flush(mKeyEventFilter1);
mKeyEventDispatcher.flush(mKeyEventFilter2);
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
}
@Test
@@ -389,7 +389,7 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, true,
mFilter2SequenceCaptor.getValue());
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
}
@Test
@@ -401,7 +401,7 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
- assertTrue(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
}
@Test
@@ -417,11 +417,11 @@
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
mKeyEventDispatcher
.notifyKeyEventLocked(mOtherKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, false,
mFilter1SequenceCaptor.getAllValues().get(0));
- mInputEventsHander.removeMessages(SEND_FRAMEWORK_KEY_EVENT);
+ mInputEventsHandler.removeMessages(SEND_FRAMEWORK_KEY_EVENT);
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, false,
mFilter1SequenceCaptor.getAllValues().get(1));
@@ -433,14 +433,14 @@
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
mKeyEventDispatcher
.notifyKeyEventLocked(mOtherKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, true,
mFilter1SequenceCaptor.getAllValues().get(0));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, true,
mFilter1SequenceCaptor.getAllValues().get(1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
}
@Test
@@ -448,7 +448,7 @@
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
mKeyEventDispatcher
.notifyKeyEventLocked(mOtherKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, true,
mFilter1SequenceCaptor.getAllValues().get(0));
@@ -463,7 +463,7 @@
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
mKeyEventDispatcher
.notifyKeyEventLocked(mOtherKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, false,
mFilter1SequenceCaptor.getAllValues().get(0));
@@ -478,9 +478,9 @@
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
mKeyEventDispatcher
.notifyKeyEventLocked(mOtherKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- mKeyEventDispatcher.handleMessage(mMessageCapturingHandler.timedMessages.get(0));
+ mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, true,
mFilter1SequenceCaptor.getAllValues().get(0));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter1, true,
@@ -494,7 +494,7 @@
mKeyEventDispatcher.notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(mKeyEventFilter1));
mKeyEventDispatcher.notifyKeyEventLocked(
mOtherKeyEvent, 0, Arrays.asList(mKeyEventFilter1, mKeyEventFilter2));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
@@ -513,7 +513,7 @@
mKeyEventDispatcher.flush(mKeyEventFilter1);
mKeyEventDispatcher.notifyKeyEventLocked(
mOtherKeyEvent, 0, Arrays.asList(mKeyEventFilter2));
- assertFalse(mInputEventsHander.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
+ assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getAllValues().get(0));
@@ -543,24 +543,34 @@
}
private void assertOneKeyEventSentToFramework(KeyEvent event) {
- assertEquals(1, mInputEventsHander.timedMessages.size());
- assertEquals(SEND_FRAMEWORK_KEY_EVENT, mInputEventsHander.timedMessages.get(0).what);
- assertEquals(WindowManagerPolicy.FLAG_PASS_TO_USER,
- mInputEventsHander.timedMessages.get(0).arg1);
- assertTrue(new KeyEventMatcher(event).matches(mInputEventsHander.timedMessages.get(0).obj));
+ assertEquals(1, mInputEventsHandler.timedMessages.size());
+
+ Message m = getTimedMessage(mInputEventsHandler, 0);
+ assertEquals(SEND_FRAMEWORK_KEY_EVENT, m.what);
+ assertEquals(WindowManagerPolicy.FLAG_PASS_TO_USER, m.arg1);
+ assertTrue(new KeyEventMatcher(event).matches(m.obj));
}
private void assertTwoKeyEventsSentToFrameworkInOrder(KeyEvent first, KeyEvent second) {
- assertEquals(2, mInputEventsHander.timedMessages.size());
- assertEquals(SEND_FRAMEWORK_KEY_EVENT, mInputEventsHander.timedMessages.get(0).what);
- assertEquals(WindowManagerPolicy.FLAG_PASS_TO_USER,
- mInputEventsHander.timedMessages.get(0).arg1);
- assertTrue(new KeyEventMatcher(first).matches(mInputEventsHander.timedMessages.get(0).obj));
- assertEquals(SEND_FRAMEWORK_KEY_EVENT, mInputEventsHander.timedMessages.get(1).what);
- assertEquals(WindowManagerPolicy.FLAG_PASS_TO_USER,
- mInputEventsHander.timedMessages.get(1).arg1);
- assertTrue(new KeyEventMatcher(second)
- .matches(mInputEventsHander.timedMessages.get(1).obj));
+ assertEquals(2, mInputEventsHandler.timedMessages.size());
+
+ Message m0 = getTimedMessage(mInputEventsHandler, 0);
+ assertEquals(SEND_FRAMEWORK_KEY_EVENT, m0.what);
+ assertEquals(WindowManagerPolicy.FLAG_PASS_TO_USER, m0.arg1);
+ assertTrue(new KeyEventMatcher(first).matches(m0.obj));
+
+ Message m1 = getTimedMessage(mInputEventsHandler, 1);
+ assertEquals(SEND_FRAMEWORK_KEY_EVENT, m1.what);
+ assertEquals(WindowManagerPolicy.FLAG_PASS_TO_USER, m1.arg1);
+ assertTrue(new KeyEventMatcher(second).matches(m1.obj));
+ }
+
+ private static Message getTimedMessage(MessageCapturingHandler handler, int i) {
+ return handler.timedMessages.get(i).first;
+ }
+
+ private static boolean isTimeoutPending(MessageCapturingHandler handler) {
+ return handler.hasMessages(KeyEventDispatcher.MSG_ON_KEY_EVENT_TIMEOUT);
}
private class KeyEventMatcher extends TypeSafeMatcher<KeyEvent> {
@@ -581,18 +591,4 @@
description.appendText("Key event matcher");
}
}
-
- private class MessageCapturingHandler extends Handler {
- List<Message> timedMessages = new ArrayList<>();
-
- @Override
- public boolean sendMessageAtTime(Message message, long uptimeMillis) {
- timedMessages.add(Message.obtain(message));
- return super.sendMessageAtTime(message, uptimeMillis);
- }
-
- public boolean isTimeoutPending() {
- return hasMessages(KeyEventDispatcher.MSG_ON_KEY_EVENT_TIMEOUT);
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 851e221..9926a09 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
-import android.os.Looper;
import android.view.KeyEvent;
import androidx.test.runner.AndroidJUnit4;
@@ -38,8 +37,8 @@
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -58,19 +57,18 @@
@Mock AccessibilityManagerService mMockAms;
@Mock WindowManagerPolicy mMockPolicy;
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mInterceptor = new KeyboardInterceptor(mMockAms, mMockPolicy, mHandler);
}
+ @After
+ public void tearDown() {
+ mHandler.removeAllMessages();
+ }
+
+
@Test
public void whenNonspecialKeyArrives_withNothingInQueue_eventGoesToAms() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index d6d21c6..c88b873 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -43,9 +43,9 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.support.test.filters.FlakyTest;
import android.view.MagnificationSpec;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
@@ -55,8 +55,8 @@
import org.hamcrest.CoreMatchers;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -67,7 +67,6 @@
import java.util.Locale;
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class MagnificationControllerTest {
static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 100, 200);
static final PointF INITIAL_MAGNIFICATION_BOUNDS_CENTER = new PointF(
@@ -75,8 +74,10 @@
static final PointF INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER = new PointF(25, 50);
static final PointF INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER = new PointF(75, 150);
static final Rect OTHER_MAGNIFICATION_BOUNDS = new Rect(100, 200, 500, 600);
+ static final Rect OTHER_MAGNIFICATION_BOUNDS_COMPAT = new Rect(50, 100, 450, 500);
static final PointF OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER = new PointF(400, 500);
static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
+ static final Region OTHER_REGION_COMPAT = new Region(OTHER_MAGNIFICATION_BOUNDS_COMPAT);
static final Region OTHER_REGION = new Region(OTHER_MAGNIFICATION_BOUNDS);
static final int SERVICE_ID_1 = 1;
static final int SERVICE_ID_2 = 2;
@@ -91,25 +92,17 @@
return mMagnificationController.handleMessage(msg);
}
});
- final ArgumentCaptor<MagnificationSpec> mMagnificationSpecCaptor =
- ArgumentCaptor.forClass(MagnificationSpec.class);
final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
MagnificationController.SettingsBridge mMockSettingsBridge;
-
MagnificationController mMagnificationController;
ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setUp() {
- when(mMockContext.getMainLooper()).thenReturn(Looper.myLooper());
+ Looper looper = InstrumentationRegistry.getContext().getMainLooper();
+ // Pretending ID of the Thread associated with looper as main thread ID in controller
+ when(mMockContext.getMainLooper()).thenReturn(looper);
Resources mockResources = mock(Resources.class);
when(mMockContext.getResources()).thenReturn(mockResources);
when(mockResources.getInteger(R.integer.config_longAnimTime))
@@ -136,6 +129,12 @@
Mockito.reset(mMockValueAnimator); // Ignore other initialization
}
+ @After
+ public void tearDown() {
+ mMessageCapturingHandler.removeAllMessages();
+ }
+
+
@Test
public void testRegister_WindowManagerAndContextRegisterListeners() {
mMagnificationController.register();
@@ -202,6 +201,7 @@
final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale);
assertTrue(mMagnificationController
.setScale(scale, center.x, center.y, false, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets);
verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
@@ -219,6 +219,7 @@
PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
assertTrue(mMagnificationController
.setScale(scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
// New center should be halfway between original center and pivot
PointF newCenter = new PointF((pivotPoint.x + INITIAL_MAGNIFICATION_BOUNDS.centerX()) / 2,
@@ -264,6 +265,7 @@
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
assertTrue(mMagnificationController
.setCenter(newCenter.x, newCenter.y, false, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec expectedSpec = getMagnificationSpec(scale, expectedOffsets);
@@ -284,6 +286,7 @@
assertTrue(mMagnificationController.setScaleAndCenter(scale, newCenter.x, newCenter.y,
true, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
@@ -325,6 +328,7 @@
assertTrue(mMagnificationController.setScaleAndCenter(
MagnificationController.MAX_SCALE + 1.0f,
newCenter.x, newCenter.y, false, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
@@ -335,6 +339,7 @@
assertTrue(mMagnificationController.setScaleAndCenter(0.5f,
INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
false, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x,
mMagnificationController.getCenterX(), 0.5);
@@ -351,6 +356,7 @@
// Off the edge to the top and left
assertTrue(mMagnificationController.setScaleAndCenter(
scale, -100f, -200f, false, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
@@ -364,6 +370,7 @@
assertTrue(mMagnificationController.setScaleAndCenter(scale,
INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1,
false, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
@@ -391,12 +398,14 @@
// First zoom in
assertTrue(mMagnificationController
.setScaleAndCenter(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
Mockito.reset(mMockWindowManager);
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
mMagnificationController.offsetMagnifiedRegion(
startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y, SERVICE_ID_1);
+ mMessageCapturingHandler.sendAllMessages();
MagnificationSpec expectedSpec = getMagnificationSpec(scale, newOffsets);
verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
@@ -490,6 +499,7 @@
public void testResetIfNeeded_doesWhatItSays() {
mMagnificationController.register();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
reset(mMockAms);
assertTrue(mMagnificationController.resetIfNeeded(false));
verify(mMockAms).notifyMagnificationChanged(
@@ -507,6 +517,7 @@
broadcastReceiverCaptor.capture(), (IntentFilter) anyObject());
BroadcastReceiver br = broadcastReceiverCaptor.getValue();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
br.onReceive(mMockContext, null);
mMessageCapturingHandler.sendAllMessages();
assertFalse(mMagnificationController.isMagnifying());
@@ -517,6 +528,7 @@
mMagnificationController.register();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
callbacks.onUserContextChanged();
mMessageCapturingHandler.sendAllMessages();
assertFalse(mMagnificationController.isMagnifying());
@@ -539,10 +551,11 @@
// Going from a small region to a large one leads to no issues
mMagnificationController.register();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
MagnificationSpec startSpec = getCurrentMagnificationSpec();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
Mockito.reset(mMockWindowManager);
- callbacks.onMagnificationRegionChanged(OTHER_REGION);
+ callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT);
mMessageCapturingHandler.sendAllMessages();
assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
@@ -553,11 +566,12 @@
mMagnificationController.register();
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
+ // setting animate parameter to true is differ from zoomIn2xToMiddle()
mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1);
MagnificationSpec startSpec = getCurrentMagnificationSpec();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
Mockito.reset(mMockWindowManager);
- callbacks.onMagnificationRegionChanged(OTHER_REGION);
+ callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT);
mMessageCapturingHandler.sendAllMessages();
assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
@@ -573,6 +587,7 @@
PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
float scale = 2.0f;
mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+ mMessageCapturingHandler.sendAllMessages();
MagnificationSpec startSpec = getCurrentMagnificationSpec();
verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec)));
Mockito.reset(mMockWindowManager);
@@ -597,8 +612,9 @@
PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
float scale = 2.0f;
mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1);
+ mMessageCapturingHandler.sendAllMessages();
MagnificationSpec startSpec = getCurrentMagnificationSpec();
- when (mMockValueAnimator.isRunning()).thenReturn(true);
+ when(mMockValueAnimator.isRunning()).thenReturn(true);
callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION);
mMessageCapturingHandler.sendAllMessages();
@@ -616,6 +632,7 @@
public void testRequestRectOnScreen_rectAlreadyOnScreen_doesNothing() {
mMagnificationController.register();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
MagnificationSpec startSpec = getCurrentMagnificationSpec();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
Mockito.reset(mMockWindowManager);
@@ -631,6 +648,7 @@
public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() {
mMagnificationController.register();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
Mockito.reset(mMockWindowManager);
callbacks.onRectangleOnScreenRequested(0, 0, 1, 1);
@@ -644,6 +662,7 @@
public void testRequestRectOnScreen_garbageInput_doesNothing() {
mMagnificationController.register();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
MagnificationSpec startSpec = getCurrentMagnificationSpec();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
Mockito.reset(mMockWindowManager);
@@ -653,12 +672,12 @@
verifyNoMoreInteractions(mMockWindowManager);
}
-
@Test
public void testRequestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale() {
Locale.setDefault(new Locale("en", "us"));
mMagnificationController.register();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
MagnificationSpec startSpec = getCurrentMagnificationSpec();
Mockito.reset(mMockWindowManager);
@@ -685,6 +704,7 @@
public void testRequestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen() {
mMagnificationController.register();
zoomIn2xToMiddle();
+ mMessageCapturingHandler.sendAllMessages();
MagnificationCallbacks callbacks = getMagnificationCallbacks();
MagnificationSpec startSpec = getCurrentMagnificationSpec();
Mockito.reset(mMockWindowManager);
@@ -708,6 +728,7 @@
assertTrue(mMagnificationController.setScaleAndCenter(scale, firstCenter.x, firstCenter.y,
true, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
assertEquals(firstCenter.x, mMagnificationController.getCenterX(), 0.5);
assertEquals(firstCenter.y, mMagnificationController.getCenterY(), 0.5);
@@ -736,6 +757,7 @@
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale));
assertTrue(mMagnificationController.setCenter(
newCenter.x, newCenter.y, true, SERVICE_ID_1));
+ mMessageCapturingHandler.sendAllMessages();
// Animation should have been restarted
verify(mMockValueAnimator, times(2)).start();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java b/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
index e3ee47f..e2b517f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
@@ -16,11 +16,13 @@
package com.android.server.accessibility;
-import android.app.Notification;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.util.Pair;
+import androidx.test.InstrumentationRegistry;
+
import java.util.ArrayList;
import java.util.List;
@@ -34,13 +36,22 @@
Handler.Callback mCallback;
public MessageCapturingHandler(Handler.Callback callback) {
+ this(InstrumentationRegistry.getContext().getMainLooper(), callback);
+ }
+
+ public MessageCapturingHandler(Looper looper, Callback callback) {
+ super(looper);
mCallback = callback;
}
+ /**
+ * Holding messages in queue, but never dispatching.
+ * @see #removeAllMessages()
+ */
@Override
public boolean sendMessageAtTime(Message message, long uptimeMillis) {
timedMessages.add(new Pair<>(Message.obtain(message), uptimeMillis));
- return super.sendMessageAtTime(message, uptimeMillis);
+ return super.sendMessageAtTime(message, Long.MAX_VALUE);
}
public void setCallback(Handler.Callback callback) {
@@ -67,6 +78,22 @@
removeStaleMessages();
}
+ /**
+ * Clear messages sent from this handler in queue.
+ * <p>
+ * If main looper is used, this method should be called in tear down function
+ * to ensure messages isolation between test cases.
+ * </p>
+ */
+ public void removeAllMessages() {
+ if (hasMessages()) {
+ for (int i = 0; i < timedMessages.size(); i++) {
+ Message message = timedMessages.get(i).first;
+ removeMessages(message.what, message.obj);
+ }
+ }
+ }
+
public boolean hasMessages() {
removeStaleMessages();
return !timedMessages.isEmpty();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 5f0fa87..2cba9d0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -43,7 +43,6 @@
import android.accessibilityservice.IAccessibilityServiceClient;
import android.graphics.Point;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
@@ -57,8 +56,8 @@
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
+import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -127,13 +126,6 @@
Matcher<MotionEvent> mIsClickDown;
Matcher<MotionEvent> mIsClickUp;
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setUp() {
mMessageCapturingHandler = new MessageCapturingHandler(new Handler.Callback() {
@@ -172,6 +164,12 @@
hasTimeFromDown(CLICK_DURATION));
}
+ @After
+ public void tearDown() {
+ mMessageCapturingHandler.removeAllMessages();
+ }
+
+
@Test
public void testInjectEvents_shouldEmergeInOrderWithCorrectTiming() throws RemoteException {
EventStreamTransformation next = attachMockNext(mMotionEventInjector);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/TouchExplorerTest.java
index c47885f..2645461 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/TouchExplorerTest.java
@@ -16,12 +16,14 @@
package com.android.server.accessibility;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
import android.content.Context;
import android.graphics.PointF;
import android.os.SystemClock;
+import android.testing.DexmakerShareClassLoaderRule;
import android.util.DebugUtils;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -30,6 +32,7 @@
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -66,6 +69,11 @@
private TouchExplorer mTouchExplorer;
private long mLastDownTime = Integer.MIN_VALUE;
+ // mock package-private AccessibilityGestureDetector class
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+
/**
* {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object
* is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation
@@ -93,8 +101,9 @@
public void setUp() {
Context context = InstrumentationRegistry.getContext();
AccessibilityManagerService ams = new AccessibilityManagerService(context);
+ AccessibilityGestureDetector detector = mock(AccessibilityGestureDetector.class);
mCaptor = new EventCaptor();
- mTouchExplorer = new TouchExplorer(context, ams);
+ mTouchExplorer = new TouchExplorer(context, ams, detector);
mTouchExplorer.setNext(mCaptor);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 8853db2..8e09d60 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -32,13 +32,12 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
-import android.os.Looper;
import android.view.accessibility.AccessibilityEvent;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -66,13 +65,6 @@
@Mock IAccessibilityServiceClient mMockAccessibilityServiceClient;
@Mock IBinder mMockServiceAsBinder;
- @BeforeClass
- public static void oneTimeInitialization() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -88,6 +80,12 @@
mMessageCapturingHandler = new MessageCapturingHandler(null);
}
+ @After
+ public void tearDown() {
+ mMessageCapturingHandler.removeAllMessages();
+ }
+
+
@Test
public void isRunning_returnsTrueOnlyWhenRunning() {
assertFalse(mUiAutomationManager.isUiAutomationRunningLocked());
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
similarity index 97%
rename from core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
rename to services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 8a83518..910d433 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
+package com.android.server.inputmethod;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -23,15 +23,15 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
-import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
-import com.android.internal.inputmethod.InputMethodUtils;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
+import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
similarity index 90%
rename from core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
rename to services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index 3064afa..4d0278f 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
+package com.android.server.inputmethod;
import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR;
import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS;
@@ -37,13 +37,14 @@
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,15 +72,11 @@
private static final Locale LOCALE_FR = new Locale("fr");
private static final Locale LOCALE_FR_CA = new Locale("fr", "CA");
private static final Locale LOCALE_HI = new Locale("hi");
- private static final Locale LOCALE_JA = new Locale("ja");
private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN");
private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW");
private static final Locale LOCALE_IN = new Locale("in");
private static final Locale LOCALE_ID = new Locale("id");
- private static final Locale LOCALE_TH = new Locale("ht");
- private static final Locale LOCALE_TH_TH = new Locale("ht", "TH");
- private static final Locale LOCALE_TH_TH_TH = new Locale("ht", "TH", "TH");
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
private static final String SUBTYPE_MODE_HANDWRITING = "handwriting";
@@ -1087,122 +1084,6 @@
}
@Test
- public void testGetSuitableLocalesForSpellChecker() throws Exception {
- {
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_US);
- assertEquals(3, locales.size());
- assertEquals(LOCALE_EN_US, locales.get(0));
- assertEquals(LOCALE_EN_GB, locales.get(1));
- assertEquals(LOCALE_EN, locales.get(2));
- }
-
- {
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_GB);
- assertEquals(3, locales.size());
- assertEquals(LOCALE_EN_GB, locales.get(0));
- assertEquals(LOCALE_EN_US, locales.get(1));
- assertEquals(LOCALE_EN, locales.get(2));
- }
-
- {
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN);
- assertEquals(3, locales.size());
- assertEquals(LOCALE_EN, locales.get(0));
- assertEquals(LOCALE_EN_US, locales.get(1));
- assertEquals(LOCALE_EN_GB, locales.get(2));
- }
-
- {
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_IN);
- assertEquals(4, locales.size());
- assertEquals(LOCALE_EN_IN, locales.get(0));
- assertEquals(LOCALE_EN_US, locales.get(1));
- assertEquals(LOCALE_EN_GB, locales.get(2));
- assertEquals(LOCALE_EN, locales.get(3));
- }
-
- {
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_JA_JP);
- assertEquals(5, locales.size());
- assertEquals(LOCALE_JA_JP, locales.get(0));
- assertEquals(LOCALE_JA, locales.get(1));
- assertEquals(LOCALE_EN_US, locales.get(2));
- assertEquals(LOCALE_EN_GB, locales.get(3));
- assertEquals(Locale.ENGLISH, locales.get(4));
- }
-
- // Test 3-letter language code.
- {
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_FIL_PH);
- assertEquals(5, locales.size());
- assertEquals(LOCALE_FIL_PH, locales.get(0));
- assertEquals(LOCALE_FIL, locales.get(1));
- assertEquals(LOCALE_EN_US, locales.get(2));
- assertEquals(LOCALE_EN_GB, locales.get(3));
- assertEquals(Locale.ENGLISH, locales.get(4));
- }
-
- // Test variant.
- {
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_TH_TH_TH);
- assertEquals(6, locales.size());
- assertEquals(LOCALE_TH_TH_TH, locales.get(0));
- assertEquals(LOCALE_TH_TH, locales.get(1));
- assertEquals(LOCALE_TH, locales.get(2));
- assertEquals(LOCALE_EN_US, locales.get(3));
- assertEquals(LOCALE_EN_GB, locales.get(4));
- assertEquals(Locale.ENGLISH, locales.get(5));
- }
-
- // Test Locale extension.
- {
- final Locale localeWithoutVariant = LOCALE_JA_JP;
- final Locale localeWithVariant = new Locale.Builder()
- .setLocale(LOCALE_JA_JP)
- .setExtension('x', "android")
- .build();
- assertFalse(localeWithoutVariant.equals(localeWithVariant));
-
- final ArrayList<Locale> locales =
- InputMethodUtils.getSuitableLocalesForSpellChecker(localeWithVariant);
- assertEquals(5, locales.size());
- assertEquals(LOCALE_JA_JP, locales.get(0));
- assertEquals(LOCALE_JA, locales.get(1));
- assertEquals(LOCALE_EN_US, locales.get(2));
- assertEquals(LOCALE_EN_GB, locales.get(3));
- assertEquals(Locale.ENGLISH, locales.get(4));
- }
- }
-
- @Test
- public void testConstructLocaleFromString() throws Exception {
- assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en"));
- assertEquals(new Locale("en", "US"), InputMethodUtils.constructLocaleFromString("en_US"));
- assertEquals(new Locale("en", "US", "POSIX"),
- InputMethodUtils.constructLocaleFromString("en_US_POSIX"));
-
- // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not
- // support three letter language codes, and used "tl" (Tagalog) as the language string for
- // "fil" (Filipino).
- assertEquals(new Locale("fil"), InputMethodUtils.constructLocaleFromString("tl"));
- assertEquals(new Locale("fil", "PH"), InputMethodUtils.constructLocaleFromString("tl_PH"));
- assertEquals(new Locale("fil", "PH", "POSIX"),
- InputMethodUtils.constructLocaleFromString("tl_PH_POSIX"));
-
- // So far rejecting an invalid/unexpected locale string is out of the scope of this method.
- assertEquals(new Locale("a"), InputMethodUtils.constructLocaleFromString("a"));
- assertEquals(new Locale("a b c"), InputMethodUtils.constructLocaleFromString("a b c"));
- assertEquals(new Locale("en-US"), InputMethodUtils.constructLocaleFromString("en-US"));
- }
-
- @Test
public void testIsSoftInputModeStateVisibleAllowed() {
// On pre-P devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are always
// allowed, regardless of the focused view state.
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/LocaleUtilsTest.java
similarity index 98%
rename from core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
rename to services/tests/servicestests/src/com/android/server/inputmethod/LocaleUtilsTest.java
index 549511a..3fc0e4f 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/LocaleUtilsTest.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
+package com.android.server.inputmethod;
import static org.junit.Assert.assertEquals;
import android.os.LocaleList;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ee41c0b..c3c0788 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -716,9 +716,9 @@
assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
assertThat(origPkgSetting.name, is(testPkgSetting.name));
- // oldCodePaths is _not_ copied
- // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths);
- // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths)));
+ // mOldCodePaths is _not_ copied
+ // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
+ // assertThat(origPkgSetting.mOldCodePaths, is(not(testPkgSetting.mOldCodePaths)));
assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
assertSame(origPkgSetting.pkg, testPkgSetting.pkg);
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5039e42..8ac2930 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -21,18 +21,32 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.BatteryManagerInternal;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerSaveState;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.lights.LightsManager;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.PowerManagerService.Injector;
+import com.android.server.power.PowerManagerService.NativeWrapper;
+import com.android.server.power.batterysaver.BatterySavingStats;
import org.junit.Rule;
import org.mockito.Mock;
@@ -47,11 +61,19 @@
private static final boolean BATTERY_SAVER_ENABLED = true;
private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason";
- private @Mock BatterySaverPolicy mBatterySaverPolicy;
+ private @Mock BatterySaverPolicy mBatterySaverPolicyMock;
+ private @Mock LightsManager mLightsManagerMock;
+ private @Mock DisplayManagerInternal mDisplayManagerInternalMock;
+ private @Mock BatteryManagerInternal mBatteryManagerInternalMock;
+ private @Mock ActivityManagerInternal mActivityManagerInternalMock;
+ private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock;
+ private @Mock Notifier mNotifierMock;
private PowerManagerService mService;
private PowerSaveState mPowerSaveState;
private DisplayPowerRequest mDisplayPowerRequest;
+
+
@Rule
public void setUp() throws Exception {
super.setUp();
@@ -61,11 +83,51 @@
.setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
.setBrightnessFactor(BRIGHTNESS_FACTOR)
.build();
- when(mBatterySaverPolicy.getBatterySaverPolicy(
+ when(mBatterySaverPolicyMock.getBatterySaverPolicy(
eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS), anyBoolean()))
.thenReturn(mPowerSaveState);
+
mDisplayPowerRequest = new DisplayPowerRequest();
- mService = new PowerManagerService(getContext(), mBatterySaverPolicy);
+ addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+ addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+ addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
+ addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+
+ mService = new PowerManagerService(getContext(), new Injector() {
+ Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+ return mNotifierMock;
+ }
+
+ SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+ return mock(SuspendBlocker.class);
+ }
+
+ BatterySaverPolicy createBatterySaverPolicy(
+ Object lock, Context context, BatterySavingStats batterySavingStats) {
+ return mBatterySaverPolicyMock;
+ }
+
+ NativeWrapper createNativeWrapper() {
+ return mNativeWrapperMock;
+ }
+ });
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(LightsManager.class);
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
}
@SmallTest
@@ -110,6 +172,25 @@
mService.setVrModeEnabled(false);
assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
+ }
+ @SmallTest
+ public void testWakefulnessAwake_InitialValue() throws Exception {
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @SmallTest
+ public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception {
+ // Start with AWAKE state
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ mService.systemReady(null);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Take a nap with a flag.
+ mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java b/services/tests/servicestests/src/com/android/server/textservices/LazyIntToIntMapTest.java
similarity index 95%
rename from core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java
rename to services/tests/servicestests/src/com/android/server/textservices/LazyIntToIntMapTest.java
index 3518527..f80afb2 100644
--- a/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java
+++ b/services/tests/servicestests/src/com/android/server/textservices/LazyIntToIntMapTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.textservice;
+package com.android.server.textservices;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,8 +25,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java b/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
new file mode 100644
index 0000000..3766d24
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.textservices;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+public class LocaleUtilsTest {
+ private static final Locale LOCALE_EN = new Locale("en");
+ private static final Locale LOCALE_EN_US = new Locale("en", "US");
+ private static final Locale LOCALE_EN_GB = new Locale("en", "GB");
+ private static final Locale LOCALE_EN_IN = new Locale("en", "IN");
+ private static final Locale LOCALE_FIL = new Locale("fil");
+ private static final Locale LOCALE_FIL_PH = new Locale("fil", "PH");
+ private static final Locale LOCALE_JA = new Locale("ja");
+ private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
+ private static final Locale LOCALE_TH = new Locale("ht");
+ private static final Locale LOCALE_TH_TH = new Locale("ht", "TH");
+ private static final Locale LOCALE_TH_TH_TH = new Locale("ht", "TH", "TH");
+
+ @Test
+ public void testGetSuitableLocalesForSpellChecker() throws Exception {
+ {
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_US);
+ assertEquals(3, locales.size());
+ assertEquals(LOCALE_EN_US, locales.get(0));
+ assertEquals(LOCALE_EN_GB, locales.get(1));
+ assertEquals(LOCALE_EN, locales.get(2));
+ }
+
+ {
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_GB);
+ assertEquals(3, locales.size());
+ assertEquals(LOCALE_EN_GB, locales.get(0));
+ assertEquals(LOCALE_EN_US, locales.get(1));
+ assertEquals(LOCALE_EN, locales.get(2));
+ }
+
+ {
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN);
+ assertEquals(3, locales.size());
+ assertEquals(LOCALE_EN, locales.get(0));
+ assertEquals(LOCALE_EN_US, locales.get(1));
+ assertEquals(LOCALE_EN_GB, locales.get(2));
+ }
+
+ {
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_IN);
+ assertEquals(4, locales.size());
+ assertEquals(LOCALE_EN_IN, locales.get(0));
+ assertEquals(LOCALE_EN_US, locales.get(1));
+ assertEquals(LOCALE_EN_GB, locales.get(2));
+ assertEquals(LOCALE_EN, locales.get(3));
+ }
+
+ {
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_JA_JP);
+ assertEquals(5, locales.size());
+ assertEquals(LOCALE_JA_JP, locales.get(0));
+ assertEquals(LOCALE_JA, locales.get(1));
+ assertEquals(LOCALE_EN_US, locales.get(2));
+ assertEquals(LOCALE_EN_GB, locales.get(3));
+ assertEquals(Locale.ENGLISH, locales.get(4));
+ }
+
+ // Test 3-letter language code.
+ {
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_FIL_PH);
+ assertEquals(5, locales.size());
+ assertEquals(LOCALE_FIL_PH, locales.get(0));
+ assertEquals(LOCALE_FIL, locales.get(1));
+ assertEquals(LOCALE_EN_US, locales.get(2));
+ assertEquals(LOCALE_EN_GB, locales.get(3));
+ assertEquals(Locale.ENGLISH, locales.get(4));
+ }
+
+ // Test variant.
+ {
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(LOCALE_TH_TH_TH);
+ assertEquals(6, locales.size());
+ assertEquals(LOCALE_TH_TH_TH, locales.get(0));
+ assertEquals(LOCALE_TH_TH, locales.get(1));
+ assertEquals(LOCALE_TH, locales.get(2));
+ assertEquals(LOCALE_EN_US, locales.get(3));
+ assertEquals(LOCALE_EN_GB, locales.get(4));
+ assertEquals(Locale.ENGLISH, locales.get(5));
+ }
+
+ // Test Locale extension.
+ {
+ final Locale localeWithoutVariant = LOCALE_JA_JP;
+ final Locale localeWithVariant = new Locale.Builder()
+ .setLocale(LOCALE_JA_JP)
+ .setExtension('x', "android")
+ .build();
+ assertFalse(localeWithoutVariant.equals(localeWithVariant));
+
+ final ArrayList<Locale> locales =
+ LocaleUtils.getSuitableLocalesForSpellChecker(localeWithVariant);
+ assertEquals(5, locales.size());
+ assertEquals(LOCALE_JA_JP, locales.get(0));
+ assertEquals(LOCALE_JA, locales.get(1));
+ assertEquals(LOCALE_EN_US, locales.get(2));
+ assertEquals(LOCALE_EN_GB, locales.get(3));
+ assertEquals(Locale.ENGLISH, locales.get(4));
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 95d4a15..659c6e7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -34,6 +35,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -47,6 +49,9 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -68,7 +73,9 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Set;
public class ManagedServicesTest extends UiServiceTestCase {
@@ -113,7 +120,12 @@
when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
}
when(mUm.getUsers()).thenReturn(users);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ IntArray profileIds = new IntArray();
+ profileIds.add(0);
+ profileIds.add(11);
+ profileIds.add(10);
+ profileIds.add(12);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
mExpectedPrimaryPackages = new ArrayMap<>();
mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
@@ -165,12 +177,12 @@
@Test
public void testBackupAndRestore_migration_preO() throws Exception {
- ArrayMap backupPrimaryPackages = new ArrayMap<>();
+ ArrayMap<Integer, String> backupPrimaryPackages = new ArrayMap<>();
backupPrimaryPackages.put(0, "backup.0:backup:0a");
backupPrimaryPackages.put(10, "10.backup");
backupPrimaryPackages.put(11, "eleven");
backupPrimaryPackages.put(12, "");
- ArrayMap backupPrimaryComponentNames = new ArrayMap<>();
+ ArrayMap<Integer, String> backupPrimaryComponentNames = new ArrayMap<>();
backupPrimaryComponentNames.put(0, "backup.first/whatever:a/b");
backupPrimaryComponentNames.put(10, "again/M1");
backupPrimaryComponentNames.put(11, "orange/youglad:itisnot/banana");
@@ -179,11 +191,11 @@
backupPrimary.put(APPROVAL_BY_PACKAGE, backupPrimaryPackages);
backupPrimary.put(APPROVAL_BY_COMPONENT, backupPrimaryComponentNames);
- ArrayMap backupSecondaryComponentNames = new ArrayMap<>();
+ ArrayMap<Integer, String> backupSecondaryComponentNames = new ArrayMap<>();
backupSecondaryComponentNames.put(0, "secondary.1/component.Name");
backupSecondaryComponentNames.put(10,
"this.is.another.package.backup/with.Component:component.backup/2");
- ArrayMap backupSecondaryPackages = new ArrayMap<>();
+ ArrayMap<Integer, String> backupSecondaryPackages = new ArrayMap<>();
backupSecondaryPackages.put(0, "");
backupSecondaryPackages.put(10,
"this.is.another.package.backup:package.backup");
@@ -368,7 +380,7 @@
serializer.endDocument();
serializer.flush();
- for (int userId : mUserProfiles.getCurrentProfileIds()) {
+ for (int userId : mUserProfiles.getCurrentProfileIds().toArray()) {
List<String> expected =
stringToList(mExpectedPrimary.get(approvalLevel).get(userId));
List<String> actual = stringToList(Settings.Secure.getStringForUser(
@@ -633,7 +645,7 @@
}
@Test
- public void testGetAllowedComponents() throws Exception {
+ public void testGetAllowedComponentsByUser() throws Exception {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
APPROVAL_BY_COMPONENT);
loadXml(service);
@@ -708,6 +720,145 @@
assertTrue(services.isSameUser(service, 10));
}
+ @Test
+ public void testGetAllowedComponents() throws Exception {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ loadXml(service);
+
+ SparseArray<ArraySet<ComponentName>> expected = new SparseArray<>();
+
+ ArraySet<ComponentName> expected10 = new ArraySet<>();
+ expected10.add(ComponentName.unflattenFromString("this.is.another.package/M1"));
+ expected10.add(ComponentName.unflattenFromString("this.is.another.package/with.Component"));
+ expected10.add(ComponentName.unflattenFromString("component/2"));
+ expected10.add(ComponentName.unflattenFromString("package/component2"));
+ expected.put(10, expected10);
+ ArraySet<ComponentName> expected0 = new ArraySet<>();
+ expected0.add(ComponentName.unflattenFromString("secondary/component.Name"));
+ expected0.add(ComponentName.unflattenFromString("this.is.a.package.name/Ba"));
+ expected0.add(ComponentName.unflattenFromString("another.package/B1"));
+ expected.put(0, expected0);
+ ArraySet<ComponentName> expected12 = new ArraySet<>();
+ expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!"));
+ expected.put(12, expected12);
+ expected.put(11, new ArraySet<>());
+
+ SparseArray<ArraySet<ComponentName>> actual =
+ service.getAllowedComponents(mUserProfiles.getCurrentProfileIds());
+
+ assertContentsInAnyOrder(expected, actual);
+ }
+
+ @Test
+ public void testPopulateComponentsToUnbind_forceRebind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+ mock(ServiceConnection.class), 26);
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(service0);
+ removableBoundServices.add(service10);
+
+ SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+ Set<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ allowedComponentsToBind.put(0, allowed0);
+ Set<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowedComponentsToBind.put(10, allowed10);
+
+ SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ service.populateComponentsToUnbind(true, removableBoundServices, allowedComponentsToBind,
+ componentsToUnbind);
+
+ assertEquals(2, componentsToUnbind.size());
+ assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+ assertTrue(componentsToUnbind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+ }
+
+ @Test
+ public void testPopulateComponentsToUnbind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service0a = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("c/c"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+ mock(ServiceConnection.class), 26);
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(service0);
+ removableBoundServices.add(service0a);
+ removableBoundServices.add(service10);
+
+ SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+ Set<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ allowedComponentsToBind.put(0, allowed0);
+ Set<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowedComponentsToBind.put(10, allowed10);
+
+ SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ service.populateComponentsToUnbind(false, removableBoundServices, allowedComponentsToBind,
+ componentsToUnbind);
+
+ assertEquals(1, componentsToUnbind.size());
+ assertEquals(1, componentsToUnbind.get(0).size());
+ assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
+ }
+
+ @Test
+ public void populateComponentsToBind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+ ArraySet<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ approvedComponentsByUser.put(0, allowed0);
+ ArraySet<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowed10.add(ComponentName.unflattenFromString("c/c"));
+ approvedComponentsByUser.put(10, allowed10);
+ ArraySet<ComponentName> allowed15 = new ArraySet<>();
+ allowed15.add(ComponentName.unflattenFromString("d/d"));
+ approvedComponentsByUser.put(15, allowed15);
+
+ IntArray users = new IntArray();
+ users.add(10);
+ users.add(0);
+
+ SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+
+ assertEquals(2, componentsToBind.size());
+ assertEquals(1, componentsToBind.get(0).size());
+ assertTrue(componentsToBind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+ assertEquals(2, componentsToBind.get(10).size());
+ assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+ assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
+ }
+
private void loadXml(ManagedServices service) throws Exception {
final StringBuffer xml = new StringBuffer();
xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -775,7 +926,8 @@
ManagedServices.ENABLED_SERVICES_SEPARATOR)));
}
- private void assertContentsInAnyOrder(List<?> expected, List<?> actual) {
+ private void assertContentsInAnyOrder(Collection<?> expected, Collection<?> actual) {
+ assertNotNull(actual);
assertEquals(expected.size(), actual.size());
for (Object o : expected) {
@@ -787,6 +939,21 @@
}
}
+ private void assertContentsInAnyOrder(SparseArray<ArraySet<ComponentName>> expected,
+ SparseArray<ArraySet<ComponentName>> actual) throws Exception {
+ assertEquals(expected.size(), actual.size());
+
+ for (int i = 0; i < expected.size(); i++) {
+ int key = expected.keyAt(i);
+ assertTrue(actual.indexOfKey(key) >= 0);
+ try {
+ assertContentsInAnyOrder(expected.valueAt(i), actual.get(key));
+ } catch (Throwable t) {
+ throw new Exception("Error validating " + key, t);
+ }
+ }
+ }
+
private void verifyExpectedBoundEntries(ManagedServices service, boolean primary)
throws Exception {
ArrayMap<Integer, String> verifyMap = primary ? mExpectedPrimary.get(service.mApprovalLevel)
@@ -920,7 +1087,7 @@
@Override
protected boolean checkType(IInterface service) {
- return false;
+ return true;
}
@Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index f9a4f78..1de1e4e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -33,6 +33,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.os.UserManager;
+import android.util.IntArray;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -103,7 +104,12 @@
}
when(mUm.getUsers()).thenReturn(users);
when(mUm.getUsers(anyBoolean())).thenReturn(users);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ IntArray profileIds = new IntArray();
+ profileIds.add(0);
+ profileIds.add(11);
+ profileIds.add(10);
+ profileIds.add(12);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
}
@Test
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 a1b3b98..4344285 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -519,8 +519,9 @@
NotificationChannel channel = new NotificationChannel("id", "name",
IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
- assertTrue(mService.isBlocked(r, mUsageStats));
- verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r));
+
+ // isBlocked is only used for user blocking, not app suspension
+ assertFalse(mService.isBlocked(r, mUsageStats));
}
@Test
@@ -3453,4 +3454,46 @@
// yay
}
}
+
+ @Test
+ public void testRemoveForegroundServiceFlagFromNotification_enqueued() {
+ Notification n = new Notification.Builder(mContext, "").build();
+ n.flags |= FLAG_FOREGROUND_SERVICE;
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 9, null, mUid, 0,
+ n, new UserHandle(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addEnqueuedNotification(r);
+
+ mInternalService.removeForegroundServiceFlagFromNotification(
+ PKG, r.sbn.getId(), r.sbn.getUserId());
+
+ waitForIdle();
+
+ verify(mListeners, timeout(200).times(0)).notifyPostedLocked(any(), any());
+ }
+
+ @Test
+ public void testRemoveForegroundServiceFlagFromNotification_posted() {
+ Notification n = new Notification.Builder(mContext, "").build();
+ n.flags |= FLAG_FOREGROUND_SERVICE;
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 9, null, mUid, 0,
+ n, new UserHandle(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addNotification(r);
+
+ mInternalService.removeForegroundServiceFlagFromNotification(
+ PKG, r.sbn.getId(), r.sbn.getUserId());
+
+ waitForIdle();
+
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any());
+
+ assertEquals(0, captor.getValue().getNotification().flags);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 88c6fcf..b955e56 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -32,6 +32,7 @@
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.IntArray;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -235,18 +236,22 @@
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.snooze(r3, 1000);
mSnoozeHelper.snooze(r4, 1000);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
assertEquals(3, mSnoozeHelper.getSnoozed().size());
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_CURRENT});
+ profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_CURRENT);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
assertEquals(1, mSnoozeHelper.getSnoozed().size());
}
@Test
public void testGetSnoozedByUser_managedProfiles() throws Exception {
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM, UserHandle.USER_CURRENT});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_CURRENT);
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
@@ -273,8 +278,9 @@
@Test
public void repostGroupSummary_repostsSummary() throws Exception {
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
NotificationRecord r = getNotificationRecord(
"pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
NotificationRecord r2 = getNotificationRecord(
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 91f4bc8..702161e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -45,6 +45,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -60,6 +61,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Xml;
import com.android.internal.R;
@@ -74,12 +76,16 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -88,7 +94,7 @@
ConditionProviders mConditionProviders;
@Mock NotificationManager mNotificationManager;
- @Mock private Resources mResources;
+ private Resources mResources;
private TestableLooper mTestableLooper;
private ZenModeHelper mZenModeHelperSpy;
private Context mContext;
@@ -101,11 +107,17 @@
mTestableLooper = TestableLooper.get(this);
mContext = spy(getContext());
mContentResolver = mContext.getContentResolver();
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getString(R.string.zen_mode_default_every_night_name)).thenReturn("night");
- when(mResources.getString(R.string.zen_mode_default_events_name)).thenReturn("events");
- when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
+ mResources = spy(mContext.getResources());
+ try {
+ when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn(
+ getDefaultConfigParser());
+ } catch (Exception e) {
+ Log.d("ZenModeHelperTest", "Couldn't mock default zen mode config xml file err=" +
+ e.toString());
+ }
+
+ when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
mConditionProviders = new ConditionProviders(mContext, new UserProfiles(),
AppGlobals.getPackageManager());
mConditionProviders.addSystemProvider(new CountdownConditionProvider());
@@ -113,6 +125,30 @@
mConditionProviders));
}
+ private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException {
+ String xml = "<zen version=\"8\" user=\"0\">\n"
+ + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
+ + "visualScreenOff=\"true\" alarms=\"true\" "
+ + "media=\"true\" system=\"false\" />\n"
+ + "<automatic ruleId=\"EVENTS_DEFAULT_RULE\" enabled=\"false\" snoozing=\"false\""
+ + " name=\"Event\" zen=\"1\""
+ + " component=\"android/com.android.server.notification.EventConditionProvider\""
+ + " conditionId=\"condition://android/event?userId=-10000&calendar=&"
+ + "reply=1\"/>\n"
+ + "<automatic ruleId=\"EVERY_NIGHT_DEFAULT_RULE\" enabled=\"false\""
+ + " snoozing=\"false\" name=\"Sleeping\" zen=\"1\""
+ + " component=\"android/com.android.server.notification.ScheduleConditionProvider\""
+ + " conditionId=\"condition://android/schedule?days=1.2.3.4.5.6.7 &start=22.0"
+ + "&end=7.0&exitAtAlarm=true\"/>"
+ + "<disallow visualEffects=\"511\" />"
+ + "</zen>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null);
+ parser.nextTag();
+ return new XmlResourceParserImpl(parser);
+ }
+
private ByteArrayOutputStream writeXmlAndPurge(boolean forBackup, Integer version)
throws Exception {
XmlSerializer serializer = new FastXmlSerializer();
@@ -649,8 +685,8 @@
customRule.id = "customRule";
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- customRule.component = new ComponentName("test", "test");
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
automaticRules.put("customRule", customRule);
mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
@@ -662,8 +698,8 @@
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
mZenModeHelperSpy.readXml(parser, true);
- assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode());
assertEquals(original, mZenModeHelperSpy.mConfig);
+ assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode());
}
@Test
@@ -678,6 +714,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
enabledAutoRule.put("customRule", customRule);
mZenModeHelperSpy.mConfig.automaticRules = enabledAutoRule;
@@ -842,6 +879,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
disabledAutoRule.put("customRule", customRule);
mZenModeHelperSpy.mConfig.automaticRules = disabledAutoRule;
@@ -877,6 +915,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
automaticRules.put("customRule", customRule);
ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -886,6 +925,7 @@
defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
defaultScheduleRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule);
@@ -922,6 +962,7 @@
customRule.name = "Custom Rule";
customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+ customRule.component = new ComponentName("android", "ScheduleConditionProvider");
automaticRules.put("customRule", customRule);
ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
@@ -1015,4 +1056,294 @@
assertTrue(mZenModeHelperSpy.mConfig.allowRepeatCallers);
assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelperSpy.mConfig.suppressedVisualEffects);
}
+
+ /**
+ * Wrapper to use XmlPullParser as XmlResourceParser for Resources.getXml()
+ */
+ final class XmlResourceParserImpl implements XmlResourceParser {
+ private XmlPullParser parser;
+
+ public XmlResourceParserImpl(XmlPullParser parser) {
+ this.parser = parser;
+ }
+
+ public int getEventType() throws XmlPullParserException {
+ return parser.getEventType();
+ }
+
+ @Override
+ public void setFeature(String name, boolean state) throws XmlPullParserException {
+ parser.setFeature(name, state);
+ }
+
+ @Override
+ public boolean getFeature(String name) {
+ return false;
+ }
+
+ @Override
+ public void setProperty(String name, Object value) throws XmlPullParserException {
+ parser.setProperty(name, value);
+ }
+
+ @Override
+ public Object getProperty(String name) {
+ return parser.getProperty(name);
+ }
+
+ @Override
+ public void setInput(Reader in) throws XmlPullParserException {
+ parser.setInput(in);
+ }
+
+ @Override
+ public void setInput(InputStream inputStream, String inputEncoding)
+ throws XmlPullParserException {
+ parser.setInput(inputStream, inputEncoding);
+ }
+
+ @Override
+ public String getInputEncoding() {
+ return parser.getInputEncoding();
+ }
+
+ @Override
+ public void defineEntityReplacementText(String entityName, String replacementText)
+ throws XmlPullParserException {
+ parser.defineEntityReplacementText(entityName, replacementText);
+ }
+
+ @Override
+ public int getNamespaceCount(int depth) throws XmlPullParserException {
+ return parser.getNamespaceCount(depth);
+ }
+
+ @Override
+ public String getNamespacePrefix(int pos) throws XmlPullParserException {
+ return parser.getNamespacePrefix(pos);
+ }
+
+ @Override
+ public String getNamespaceUri(int pos) throws XmlPullParserException {
+ return parser.getNamespaceUri(pos);
+ }
+
+ @Override
+ public String getNamespace(String prefix) {
+ return parser.getNamespace(prefix);
+ }
+
+ @Override
+ public int getDepth() {
+ return parser.getDepth();
+ }
+
+ @Override
+ public String getPositionDescription() {
+ return parser.getPositionDescription();
+ }
+
+ @Override
+ public int getLineNumber() {
+ return parser.getLineNumber();
+ }
+
+ @Override
+ public int getColumnNumber() {
+ return parser.getColumnNumber();
+ }
+
+ @Override
+ public boolean isWhitespace() throws XmlPullParserException {
+ return parser.isWhitespace();
+ }
+
+ @Override
+ public String getText() {
+ return parser.getText();
+ }
+
+ @Override
+ public char[] getTextCharacters(int[] holderForStartAndLength) {
+ return parser.getTextCharacters(holderForStartAndLength);
+ }
+
+ @Override
+ public String getNamespace() {
+ return parser.getNamespace();
+ }
+
+ @Override
+ public String getName() {
+ return parser.getName();
+ }
+
+ @Override
+ public String getPrefix() {
+ return parser.getPrefix();
+ }
+
+ @Override
+ public boolean isEmptyElementTag() throws XmlPullParserException {
+ return false;
+ }
+
+ @Override
+ public int getAttributeCount() {
+ return parser.getAttributeCount();
+ }
+
+ public int next() throws IOException, XmlPullParserException {
+ return parser.next();
+ }
+
+ @Override
+ public int nextToken() throws XmlPullParserException, IOException {
+ return parser.next();
+ }
+
+ @Override
+ public void require(int type, String namespace, String name)
+ throws XmlPullParserException, IOException {
+ parser.require(type, namespace, name);
+ }
+
+ @Override
+ public String nextText() throws XmlPullParserException, IOException {
+ return parser.nextText();
+ }
+
+ @Override
+ public String getAttributeNamespace(int index) {
+ return "";
+ }
+
+ @Override
+ public String getAttributeName(int index) {
+ return parser.getAttributeName(index);
+ }
+
+ @Override
+ public String getAttributePrefix(int index) {
+ return parser.getAttributePrefix(index);
+ }
+
+ @Override
+ public String getAttributeType(int index) {
+ return parser.getAttributeType(index);
+ }
+
+ @Override
+ public boolean isAttributeDefault(int index) {
+ return parser.isAttributeDefault(index);
+ }
+
+ @Override
+ public String getAttributeValue(int index) {
+ return parser.getAttributeValue(index);
+ }
+
+ @Override
+ public String getAttributeValue(String namespace, String name) {
+ return parser.getAttributeValue(namespace, name);
+ }
+
+ @Override
+ public int getAttributeNameResource(int index) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeListValue(String namespace, String attribute, String[] options,
+ int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public boolean getAttributeBooleanValue(String namespace, String attribute,
+ boolean defaultValue) {
+ return false;
+ }
+
+ @Override
+ public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeUnsignedIntValue(String namespace, String attribute,
+ int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public float getAttributeFloatValue(String namespace, String attribute,
+ float defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeListValue(int index, String[] options, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
+ return false;
+ }
+
+ @Override
+ public int getAttributeResourceValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeIntValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getAttributeUnsignedIntValue(int index, int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public float getAttributeFloatValue(int index, float defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public String getIdAttribute() {
+ return null;
+ }
+
+ @Override
+ public String getClassAttribute() {
+ return null;
+ }
+
+ @Override
+ public int getIdAttributeResourceValue(int defaultValue) {
+ return 0;
+ }
+
+ @Override
+ public int getStyleAttribute() {
+ return 0;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public int nextTag() throws IOException, XmlPullParserException {
+ return parser.nextTag();
+ }
+ }
}
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
index 0f8b18a..9a6c225 100644
--- a/services/tests/wmtests/Android.mk
+++ b/services/tests/wmtests/Android.mk
@@ -14,9 +14,14 @@
$(call all-java-files-under, ../servicestests/utils)
LOCAL_STATIC_JAVA_LIBRARIES := \
+ frameworks-base-testutils \
+ services.core \
androidx-test \
mockito-target-minus-junit4 \
platform-test-annotations \
+ truth-prebuilt \
+ testables \
+ ub-uiautomator \
LOCAL_JAVA_LIBRARIES := \
android.test.mock \
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 1fb9473..a03573d 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -22,7 +22,27 @@
android:minSdkVersion="1"
android:targetSdkVersion="28" />
- <application android:testOnly="true" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+ <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
+ <uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+ <application android:testOnly="true">
+ <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityA" />
+ <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityB" />
+ <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
+ <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
+ <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
+ <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" />
+ </application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index 2717ef90..16e16dc 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -24,9 +24,9 @@
</target_preparer>
<option name="test-tag" value="WmTests" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.wmtests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
- <option name="hidden-api-checks" value="false" />
+ <option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
index 9de64f2..fb09f0d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
@@ -44,7 +44,7 @@
* Tests for exercising resizing bounds due to activity options.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:ActivityLaunchParamsModifierTests
+ * atest WmTests:ActivityLaunchParamsModifierTests
*/
@MediumTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityManagerInternalTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityManagerInternalTest.java
index 9a7488e..751ac52 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -34,17 +34,8 @@
/**
* Test class for {@link ActivityManagerInternal}.
*
- * To run the tests, use
- *
- * runtest -c com.android.server.am.ActivityManagerInternalTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerInternalTest -w \
- * com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ * atest WmTests:ActivityManagerInternalTest
*/
@RunWith(AndroidJUnit4.class)
public class ActivityManagerInternalTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityManagerServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityManagerServiceTest.java
index 47ce879..5d5f930 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -89,17 +89,8 @@
/**
* Test class for {@link ActivityManagerService}.
*
- * To run the tests, use
- *
- * runtest -c com.android.server.am.ActivityManagerServiceTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerServiceTest -w \
- * com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ * atest WmTests:ActivityManagerServiceTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityManagerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityManagerTest.java
index ba25b16..a2f942e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityManagerTest.java
@@ -18,13 +18,18 @@
import android.app.ActivityManager;
import android.app.IActivityManager;
-import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
import java.util.List;
+/**
+ * Tests for {@link ActivityManager}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ActivityManagerTest
+ */
public class ActivityManagerTest extends AndroidTestCase {
IActivityManager service;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityOptionsTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityOptionsTest.java
index d15bff4..45c509a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityOptionsTest.java
@@ -34,7 +34,10 @@
import org.junit.runner.RunWith;
/**
- * atest FrameworksServicesTests:ActivityOptionsTest
+ * Tests for {@link ActivityOptions}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ActivityOptionsTest
*/
@MediumTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityRecordTests.java
similarity index 83%
rename from services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityRecordTests.java
index 2338744..171203a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityRecordTests.java
@@ -63,13 +63,12 @@
* Tests for the {@link ActivityRecord} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.am.ActivityRecordTests
+ * atest WmTests:com.android.server.am.ActivityRecordTests
*/
@MediumTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
private TestActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@@ -79,10 +78,10 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
- mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ setupActivityTaskManagerService();
+ mStack = mSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+ mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
mActivity = new ActivityBuilder(mService).setTask(mTask).build();
}
@@ -189,17 +188,13 @@
@Test
public void testCanBeLaunchedOnDisplay() throws Exception {
- testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
- true /*activityResizeable*/, true /*expected*/);
+ mService.mSupportsMultiWindow = true;
+ final ActivityRecord activity = new ActivityBuilder(mService).build();
- testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
- false /*activityResizeable*/, false /*expected*/);
-
- testSupportsLaunchingResizeable(true /*taskPresent*/, false /*taskResizeable*/,
- true /*activityResizeable*/, false /*expected*/);
-
- testSupportsLaunchingResizeable(true /*taskPresent*/, true /*taskResizeable*/,
- false /*activityResizeable*/, true /*expected*/);
+ // An activity can be launched on default display.
+ assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
+ // An activity cannot be launched on a non-existent display.
+ assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
}
@Test
@@ -230,26 +225,4 @@
assertNull(mActivity.pendingOptions);
assertNotNull(activity2.pendingOptions);
}
-
- private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
- boolean activityResizeable, boolean expected) {
- mService.mSupportsMultiWindow = true;
-
- final TaskRecord task = taskPresent
- ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null;
-
- if (task != null) {
- task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
- }
-
- final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build();
- record.info.resizeMode = activityResizeable
- ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
-
- record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
-
-
- verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
- anyInt(), anyInt(), eq(record.info));
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStackSupervisorTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 1aa80c8..2e49245 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -58,14 +58,12 @@
* Tests for the {@link ActivityStackSupervisor} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
+ * atest WmTests:ActivityStackSupervisorTests
*/
@MediumTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
- private ActivityStackSupervisor mSupervisor;
private ActivityStack mFullscreenStack;
@Before
@@ -73,8 +71,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
- mSupervisor = mService.mStackSupervisor;
+ setupActivityTaskManagerService();
mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStackTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStackTests.java
index 95f8fd1..971ae99 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStackTests.java
@@ -54,14 +54,12 @@
* Tests for the {@link ActivityStack} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.am.ActivityStackTests
+ * atest WmTests:ActivityStackTests
*/
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
- private ActivityStackSupervisor mSupervisor;
private ActivityDisplay mDefaultDisplay;
private ActivityStack mStack;
private TaskRecord mTask;
@@ -71,9 +69,8 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
- mSupervisor = mService.mStackSupervisor;
- mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
+ setupActivityTaskManagerService();
+ mDefaultDisplay = mSupervisor.getDefaultDisplay();
mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStartControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStartControllerTests.java
index e1ebbcf..6d110a0 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -46,7 +46,7 @@
* Tests for the {@link ActivityStartController} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:ActivityStartControllerTests
+ * atest WmTests:ActivityStartControllerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityStartInterceptorTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStartInterceptorTest.java
index 86541b9..fb28b9b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -59,7 +59,7 @@
* Unit tests for {@link ActivityStartInterceptorTest}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
+ * atest WmTests:ActivityStartInterceptorTest
*/
@Presubmit
@SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStarterTests.java
similarity index 81%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStarterTests.java
index d032eb5..2aa6bda 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStarterTests.java
@@ -32,26 +32,32 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.WindowLayout;
@@ -65,28 +71,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.view.Gravity;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
-
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -100,13 +84,12 @@
* Tests for the {@link ActivityStarter} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:ActivityStarterTests
+ * atest WmTests:ActivityStarterTests
*/
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStarterTests extends ActivityTestsBase {
- private ActivityTaskManagerService mService;
private ActivityStarter mStarter;
private ActivityStartController mController;
private ActivityMetricsLogger mActivityMetricsLogger;
@@ -130,7 +113,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
+ setupActivityTaskManagerService();
mController = mock(ActivityStartController.class);
mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
clearInvocations(mActivityMetricsLogger);
@@ -323,7 +306,22 @@
}
}
- private ActivityStarter prepareStarter(int launchFlags) {
+ private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
+ return prepareStarter(launchFlags, true /* mockGetLaunchStack */);
+ }
+
+ /**
+ * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
+ *
+ * @param launchFlags The intent flags to launch activity.
+ * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+ * always launching to the testing stack. Set to false when allowing
+ * the activity can be launched to any stack that is decided by real
+ * implementation.
+ * @return A {@link ActivityStarter} with default setup.
+ */
+ private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
+ boolean mockGetLaunchStack) {
// always allow test to start activity.
doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
@@ -343,11 +341,13 @@
// return task when created.
doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
- // direct starter to use spy stack.
- doReturn(stack).when(mService.mStackSupervisor)
- .getLaunchStack(any(), any(), any(), anyBoolean());
- doReturn(stack).when(mService.mStackSupervisor)
- .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
+ if (mockGetLaunchStack) {
+ // Direct starter to use spy stack.
+ doReturn(stack).when(mService.mStackSupervisor)
+ .getLaunchStack(any(), any(), any(), anyBoolean());
+ 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,
@@ -546,4 +546,84 @@
eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
any(), eq(false));
}
+
+ /**
+ * This test ensures that when starting an existing single task activity on secondary display
+ * which is not the top focused display, it should deliver new intent to the activity and not
+ * create a new stack.
+ */
+ @Test
+ public void testDeliverIntentToTopActivityOfNonTopDisplay() {
+ final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+ false /* mockGetLaunchStack */);
+
+ // Create a secondary display at bottom.
+ final TestActivityDisplay secondaryDisplay = spy(addNewActivityDisplayAt(POSITION_BOTTOM));
+ final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // Create an activity record on the top of secondary display.
+ final ComponentName componentName = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME,
+ DEFAULT_COMPONENT_PACKAGE_NAME + ".ReusableActivity");
+ final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
+ .setComponent(componentName)
+ .setStack(stack)
+ .build();
+ final ActivityRecord topActivityOnSecondaryDisplay = new ActivityBuilder(mService)
+ .setComponent(componentName)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setTask(taskRecord)
+ .build();
+
+ // Put an activity on default display as the top focused activity.
+ new ActivityBuilder(mService).setCreateTask(true).build();
+
+ // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
+ // on secondary display.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
+ .setIntent(topActivityOnSecondaryDisplay.intent)
+ .setActivityOptions(options.toBundle())
+ .execute();
+
+ // Ensure result is delivering intent to top.
+ assertEquals(START_DELIVERED_TO_TOP, result);
+
+ // Ensure secondary display only creates one stack.
+ verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
+ }
+
+ /**
+ * This test ensures that a reused top activity in the top focused stack is able to be
+ * reparented to another display.
+ */
+ @Test
+ public void testReparentTopFocusedActivityToSecondaryDisplay() {
+ final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+ false /* mockGetLaunchStack */);
+
+ // Create a secondary display at bottom.
+ final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM);
+ secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+
+ // Put an activity on default display as the top focused activity.
+ final ActivityRecord topActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .build();
+
+ // Start activity with the same intent as {@code topActivity} on secondary display.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
+ .setIntent(topActivity.intent)
+ .setActivityOptions(options.toBundle())
+ .execute();
+
+ // Ensure the activity is moved to secondary display.
+ assertEquals(secondaryDisplay, topActivity.getDisplay());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/am/ActivityTestsBase.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityTestsBase.java
index bb8e5c5..2902ee2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityTestsBase.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -38,14 +39,6 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
-import android.content.pm.PackageManagerInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.DisplayWindowController;
-
-import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
@@ -53,20 +46,27 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.InstrumentationRegistry;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.AttributeCache;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.AppWindowContainerController;
+import com.android.server.wm.DisplayWindowController;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.RootWindowContainerController;
import com.android.server.wm.StackWindowController;
@@ -76,7 +76,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
import java.util.List;
@@ -86,6 +88,8 @@
public class ActivityTestsBase {
private static boolean sOneTimeSetupDone = false;
+ private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
+
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
@@ -93,6 +97,9 @@
private final Context mContext = InstrumentationRegistry.getContext();
private HandlerThread mHandlerThread;
+ ActivityTaskManagerService mService;
+ ActivityStackSupervisor mSupervisor;
+
// Default package name
static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
@@ -122,6 +129,11 @@
return atm;
}
+ void setupActivityTaskManagerService() {
+ mService = createActivityTaskManagerService();
+ mSupervisor = mService.mStackSupervisor;
+ }
+
ActivityManagerService createActivityManagerService() {
final TestActivityTaskManagerService atm =
spy(new TestActivityTaskManagerService(mContext));
@@ -134,6 +146,18 @@
return am;
}
+ /** Creates a {@link TestActivityDisplay}. */
+ TestActivityDisplay createNewActivityDisplay() {
+ return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+ }
+
+ /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+ TestActivityDisplay addNewActivityDisplayAt(int position) {
+ final TestActivityDisplay display = createNewActivityDisplay();
+ mSupervisor.addChild(display, position);
+ return display;
+ }
+
void setupActivityManagerService(
TestActivityManagerService am, TestActivityTaskManagerService atm) {
atm.setActivityManagerService(am);
@@ -173,6 +197,7 @@
private boolean mCreateTask;
private ActivityStack mStack;
private int mActivityFlags;
+ private int mLaunchMode;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -198,6 +223,11 @@
return this;
}
+ ActivityBuilder setLaunchMode(int launchMode) {
+ mLaunchMode = launchMode;
+ return this;
+ }
+
ActivityBuilder setStack(ActivityStack stack) {
mStack = stack;
return this;
@@ -233,6 +263,7 @@
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
aInfo.flags |= mActivityFlags;
+ aInfo.launchMode = mLaunchMode;
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
0 /* launchedFromPid */, 0, null, intent, null,
@@ -406,6 +437,10 @@
}
@Override
+ void updateUsageStats(ActivityRecord component, boolean resumed) {
+ }
+
+ @Override
final protected ActivityStackSupervisor createStackSupervisor() {
final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
final KeyguardController keyguardController = mock(KeyguardController.class);
@@ -464,10 +499,6 @@
}
@Override
- void updateUsageStats(ActivityRecord component, boolean resumed) {
- }
-
- @Override
Configuration getGlobalConfiguration() {
return mContext.getResources().getConfiguration();
}
@@ -500,7 +531,7 @@
@Override
public void initialize() {
super.initialize();
- mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY));
+ mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
@@ -516,10 +547,20 @@
}
protected static class TestActivityDisplay extends ActivityDisplay {
-
private final ActivityStackSupervisor mSupervisor;
- TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
- super(supervisor, displayId);
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ return new TestActivityDisplay(supervisor,
+ supervisor.mDisplayManager.getDisplay(displayId));
+ }
+ final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+ new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+ return new TestActivityDisplay(supervisor, display);
+ }
+
+ TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+ super(supervisor, display);
// Normally this comes from display-properties as exposed by WM. Without that, just
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/wmtests/src/com/android/server/am/AppErrorDialogTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
rename to services/tests/wmtests/src/com/android/server/am/AppErrorDialogTest.java
index 87d367f..01351bb 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/AppErrorDialogTest.java
@@ -33,7 +33,10 @@
import java.io.File;
/**
- * runtest -c com.android.server.am.AppErrorDialogTest frameworks-services
+ * Tests for {@link AppErrorDialog}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:AppErrorDialogTest
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/am/AssistDataRequesterTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
rename to services/tests/wmtests/src/com/android/server/am/AssistDataRequesterTest.java
index a030210..20ddd8b 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -64,7 +64,8 @@
* Note: Currently, we only support fetching the screenshot for the current application, so the
* screenshot checks are hardcoded accordingly.
*
- * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+ * Build/Install/Run:
+ * atest WmTests:AssistDataRequesterTest
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/wmtests/src/com/android/server/am/BroadcastRecordTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
rename to services/tests/wmtests/src/com/android/server/am/BroadcastRecordTest.java
index 62c5734..f8c88b2 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/BroadcastRecordTest.java
@@ -40,7 +40,7 @@
* Test class for {@link BroadcastRecord}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.am.BroadcastRecordTest
+ * atest WmTests:BroadcastRecordTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/am/ClientLifecycleManagerTests.java
similarity index 65%
rename from services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
rename to services/tests/wmtests/src/com/android/server/am/ClientLifecycleManagerTests.java
index b4ad183..457c12e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ClientLifecycleManagerTests.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
package com.android.server.am;
import static org.mockito.Mockito.mock;
@@ -16,6 +32,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Test class for {@link ClientLifecycleManager}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ClientLifecycleManagerTests
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/wmtests/src/com/android/server/am/CoreSettingsObserverTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
rename to services/tests/wmtests/src/com/android/server/am/CoreSettingsObserverTest.java
index fe8256e..60389ae 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -48,17 +48,8 @@
/**
* Test class for {@link CoreSettingsObserver}.
*
- * To run the tests, use
- *
- * runtest -c com.android.server.am.CoreSettingsObserverTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.CoreSettingsObserverTest -w \
- * com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ * atest WmTests:CoreSettingsObserverTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
deleted file mode 100644
index 023e4ab..0000000
--- a/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-
-import androidx.test.filters.FlakyTest;
-
-/**
- * Dummy test for com.android.server.am.
- * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
- */
-public class DummyAmTests {
-
- @Presubmit
- @Test
- public void preSubmitTest() {}
-
- @FlakyTest
- @Presubmit
- @Test
- public void flakyPreSubmitTest() {}
-
- @Test
- public void postSubmitTest() {}
-
- @FlakyTest
- @Test
- public void flakyPostSubmitTest() {}
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java b/services/tests/wmtests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
rename to services/tests/wmtests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
index 765aaad..0395db9 100644
--- a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
@@ -37,6 +37,9 @@
/**
* Tests for {@link GlobalSettingsToPropertiesMapper}
+ *
+ * Build/Install/Run:
+ * atest WmTests:GlobalSettingsToPropertiesMapperTest
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/am/LaunchParamsControllerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
rename to services/tests/wmtests/src/com/android/server/am/LaunchParamsControllerTests.java
index d4bab2e..73b819e 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/LaunchParamsControllerTests.java
@@ -52,7 +52,7 @@
* Tests for exercising {@link LaunchParamsController}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:LaunchParamsControllerTests
+ * atest WmTests:LaunchParamsControllerTests
*/
@MediumTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/am/LockTaskControllerTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
rename to services/tests/wmtests/src/com/android/server/am/LockTaskControllerTest.java
index 863a0d8..1ce5c42 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/LockTaskControllerTest.java
@@ -35,9 +35,20 @@
import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
-import static org.junit.Assert.*;
-import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
@@ -77,7 +88,7 @@
* Unit tests for {@link LockTaskController}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest
+ * atest WmTests:LockTaskControllerTest
*/
@Presubmit
@SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/wmtests/src/com/android/server/am/MemoryStatUtilTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
rename to services/tests/wmtests/src/com/android/server/am/MemoryStatUtilTest.java
index 06c7437..912b023 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -29,6 +29,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link MemoryStatUtil}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:MemoryStatUtilTest
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MemoryStatUtilTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
rename to services/tests/wmtests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
index 1c4e0f6..e69d207 100644
--- a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
@@ -38,7 +38,10 @@
import org.mockito.MockitoAnnotations;
/**
- * atest PendingRemoteAnimationRegistryTest
+ * Tests for {@link PendingRemoteAnimationRegistry}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PendingRemoteAnimationRegistryTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/wmtests/src/com/android/server/am/PersistentConnectionTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
rename to services/tests/wmtests/src/com/android/server/am/PersistentConnectionTest.java
index 54f93a8..5c42081 100644
--- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/PersistentConnectionTest.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.server.am;
import static org.mockito.ArgumentMatchers.any;
@@ -41,6 +42,12 @@
import java.util.Arrays;
import java.util.Collections;
+/**
+ * Tests for {@link PersistentConnection}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PersistentConnectionTest
+ */
@SmallTest
public class PersistentConnectionTest extends AndroidTestCase {
private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/am/RecentTasksTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
rename to services/tests/wmtests/src/com/android/server/am/RecentTasksTest.java
index 5195214..93685d8 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/RecentTasksTest.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -38,6 +37,8 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static java.lang.Integer.MAX_VALUE;
+
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -55,7 +56,6 @@
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.MutableLong;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
import androidx.test.InstrumentationRegistry;
@@ -68,8 +68,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static java.lang.Integer.MAX_VALUE;
-
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -79,7 +77,10 @@
import java.util.Set;
/**
- * atest FrameworksServicesTests:RecentTasksTest
+ * Tests for {@link RecentTasks}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:RecentTasksTest
*/
@MediumTest
@Presubmit
@@ -870,8 +871,8 @@
@Override
public void initialize() {
super.initialize();
- mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
- mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+ mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+ mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/am/RecentsAnimationTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
rename to services/tests/wmtests/src/com/android/server/am/RecentsAnimationTest.java
index f15b5f7..4958782 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/RecentsAnimationTest.java
@@ -45,7 +45,10 @@
import org.junit.runner.RunWith;
/**
- * atest FrameworksServicesTests:RecentsAnimationTest
+ * Tests for {@link RecentsAnimation}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:RecentsAnimationTest
*/
@MediumTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/am/RunningTasksTest.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
rename to services/tests/wmtests/src/com/android/server/am/RunningTasksTest.java
index d56c6a6..ddb3e64 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/RunningTasksTest.java
@@ -30,7 +30,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -43,7 +42,10 @@
import java.util.ArrayList;
/**
- * atest FrameworksServicesTests:RunningTasksTest
+ * Tests for {@link RunningTasks}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:RunningTasksTest
*/
@MediumTest
@Presubmit
@@ -51,7 +53,6 @@
public class RunningTasksTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityTaskManagerService mService;
private RunningTasks mRunningTasks;
@@ -60,21 +61,20 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityTaskManagerService();
+ setupActivityTaskManagerService();
mRunningTasks = new RunningTasks();
}
@Test
public void testCollectTasksByLastActiveTime() throws Exception {
// Create a number of stacks with tasks (of incrementing active time)
- final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
final ArrayList<ActivityDisplay> displays = new ArrayList<>();
- final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY);
+ final ActivityDisplay display = TestActivityDisplay.create(mSupervisor, DEFAULT_DISPLAY);
displays.add(display);
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
- final ActivityStack stack = new TestActivityStack(display, stackIndex, supervisor,
+ final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
display.addChild(stack, POSITION_BOTTOM);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/am/SafeActivityOptionsTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
rename to services/tests/wmtests/src/com/android/server/am/SafeActivityOptionsTest.java
index 8e4e7e6..99f5345 100644
--- a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/SafeActivityOptionsTest.java
@@ -28,6 +28,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link SafeActivityOptions}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:SafeActivityOptionsTest
+ */
@MediumTest
@Presubmit
@FlakyTest
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
rename to services/tests/wmtests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index f5b8f78..5050744 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -22,7 +22,6 @@
import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
import android.content.pm.ActivityInfo.WindowLayout;
import android.graphics.Rect;
@@ -40,7 +39,7 @@
* Tests for exercising resizing task bounds.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:TaskLaunchParamsModifierTests
+ * atest WmTests:TaskLaunchParamsModifierTests
*/
@MediumTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/am/TaskPersisterTest.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
rename to services/tests/wmtests/src/com/android/server/am/TaskPersisterTest.java
index 8d54bc2..41b105b 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskPersisterTest.java
@@ -17,20 +17,16 @@
package com.android.server.am;
import android.content.pm.UserInfo;
-import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
-import android.util.Log;
import android.util.SparseBooleanArray;
-import com.android.server.am.TaskPersister;
-
-import java.io.File;
-import java.util.Random;
-
/**
- * atest FrameworksServicesTests:TaskPersisterTest
+ * Tests for {@link TaskPersister}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:TaskPersisterTest
*/
public class TaskPersisterTest extends AndroidTestCase {
private static final String TEST_USER_NAME = "AM-Test-User";
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/am/TaskRecordTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
rename to services/tests/wmtests/src/com/android/server/am/TaskRecordTests.java
index fa8a09c..23420aa 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskRecordTests.java
@@ -60,7 +60,7 @@
* Tests for exercising {@link TaskRecord}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.am.TaskRecordTests
+ * atest WmTests:TaskRecordTests
*/
@MediumTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/am/TaskStackChangedListenerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
rename to services/tests/wmtests/src/com/android/server/am/TaskStackChangedListenerTest.java
index 3f7c714..c124895 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskStackChangedListenerTest.java
@@ -53,6 +53,12 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+/**
+ * Tests for {@link TaskStackListener}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:TaskStackChangedListenerTest
+ */
@MediumTest
@RunWith(AndroidJUnit4.class)
public class TaskStackChangedListenerTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/wmtests/src/com/android/server/am/UserControllerTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
rename to services/tests/wmtests/src/com/android/server/am/UserControllerTest.java
index cc4f519..fb5ded5 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,30 @@
package com.android.server.am;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+
+import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -48,30 +72,11 @@
import java.util.List;
import java.util.Set;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
-import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
-
/**
- * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest
+ * Tests for {@link UserController}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:UserControllerTest
*/
@Presubmit
public class UserControllerTest extends AndroidTestCase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
similarity index 90%
rename from services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
rename to services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
index aecb278..03fb123 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm;
+package com.android.server.policy;
import android.platform.test.annotations.Presubmit;
@@ -23,10 +23,10 @@
import androidx.test.filters.FlakyTest;
/**
- * Dummy test for com.android.server.wm
+ * Dummy test for com.android.server.policy.
* TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
*/
-public class DummyWmTests {
+public class DummyPolicyTests {
@Presubmit
@Test
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
similarity index 90%
rename from services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
rename to services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
index b238e43..d7eb105 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
@@ -16,21 +16,17 @@
package com.android.server.policy;
-import android.annotation.Nullable;
-import android.graphics.Point;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.WindowManager;
import com.android.server.wm.WindowFrames;
-import com.android.server.wm.utils.WmDisplayCutout;
public class FakeWindowState implements WindowManagerPolicy.WindowState {
- private WindowFrames windowFrames;
+ private WindowFrames mWindowFrames = new WindowFrames();
public WindowManager.LayoutParams attrs;
public int displayId;
@@ -53,41 +49,40 @@
}
@Override
- public void computeFrameLw(WindowFrames windowFrames) {
- this.windowFrames = windowFrames;
+ public void computeFrameLw() {
}
@Override
public Rect getFrameLw() {
- return windowFrames.mParentFrame;
+ return mWindowFrames.mParentFrame;
}
@Override
public Rect getDisplayFrameLw() {
- return windowFrames.mDisplayFrame;
+ return mWindowFrames.mDisplayFrame;
}
@Override
public Rect getOverscanFrameLw() {
- return windowFrames.mOverscanFrame;
+ return mWindowFrames.mOverscanFrame;
}
@Override
public Rect getContentFrameLw() {
- return windowFrames.mContentFrame;
+ return mWindowFrames.mContentFrame;
}
@Override
public Rect getVisibleFrameLw() {
- return windowFrames.mVisibleFrame;
+ return mWindowFrames.mVisibleFrame;
}
public Rect getStableFrame() {
- return windowFrames.mStableFrame;
+ return mWindowFrames.mStableFrame;
}
public Rect getDecorFrame() {
- return windowFrames.mDecorFrame;
+ return mWindowFrames.mDecorFrame;
}
@Override
@@ -252,6 +247,11 @@
}
@Override
+ public WindowFrames getWindowFrames() {
+ return mWindowFrames;
+ }
+
+ @Override
public boolean isInputMethodTarget() {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
index cce6ba7..be044e9 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
@@ -37,6 +37,12 @@
import org.junit.rules.ErrorCollector;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link PhoneWindowManager} insets.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PhoneWindowManagerInsetsTest
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index fee761d..4dbbeb1 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -49,6 +49,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link PhoneWindowManager} layout.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PhoneWindowManagerLayoutTest
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
index d92d7e0..e20f5d0 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
@@ -52,6 +52,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link PhoneWindowManager}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PhoneWindowManagerTest
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index acd065e..e31b47b 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -17,14 +17,14 @@
package com.android.server.policy;
import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+import static com.android.server.wm.utils.CoordinateTransforms
+ .transformPhysicalToLogicalCoordinates;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index b5fe8b1..2291967 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -41,7 +41,7 @@
* Tests for the {@link TaskStack} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.AnimatingAppWindowTokenRegistryTest
+ * atest WmTests:AnimatingAppWindowTokenRegistryTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 3053c41..a762993 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -38,7 +38,8 @@
/**
* Test class for {@link AppTransition}.
*
- * atest AppTransitionTests
+ * Build/Install/Run:
+ * atest WmTests:AppTransitionTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowContainerControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index fcd8a39e4..c3d89e5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -41,7 +41,8 @@
/**
* Test class for {@link AppWindowContainerController}.
*
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
+ * Build/Install/Run:
+ * atest WmTests:AppWindowContainerControllerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index e3ab5cf..b1029a3 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -26,11 +26,11 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
+import android.view.SurfaceControl;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import android.view.SurfaceControl;
-
import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
import org.junit.Test;
@@ -43,7 +43,7 @@
* Animation related tests for the {@link AppWindowToken} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenAnimationTests
+ * atest WmTests:AppWindowTokenAnimationTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 6d31dfb..a0b2443 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -52,7 +52,7 @@
* Tests for the {@link AppWindowToken} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenTests
+ * atest WmTests:AppWindowTokenTests
*/
@SmallTest
// TODO: b/68267650
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 12be0b3..888ce4c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -26,7 +26,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -57,7 +56,7 @@
* appropriately.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
+ * atest WmTests:BoundsAnimationControllerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
index 2b8214d..34ff206 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -48,7 +47,7 @@
* Test class for {@link ConfigurationContainer}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.ConfigurationContainerTests
+ * atest WmTests:ConfigurationContainerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 21555e3..5efbf30 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -39,8 +39,10 @@
import org.junit.runner.RunWith;
/**
+ * Tests for {@link Dimmer}.
+ *
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ * atest WmTests:DimmerTests;
*/
@Presubmit
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index b330304..4e755ab 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -49,7 +49,6 @@
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
-import android.util.SparseIntArray;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -74,7 +73,7 @@
* Tests for the {@link DisplayContent} class.
*
* Build/Install/Run:
- * atest com.android.server.wm.DisplayContentTests
+ * atest WmTests:DisplayContentTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplaySettingsTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplaySettingsTests.java
index 07eafa5..a6cd649 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplaySettingsTests.java
@@ -35,6 +35,12 @@
import java.io.File;
+/**
+ * Tests for {@link DisplaySettings}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplaySettingsTests
+ */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 7125246..b7cd764 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -57,7 +57,8 @@
/**
* Tests for the {@link DragDropController} class.
*
- * atest FrameworksServicesTests:com.android.server.wm.DragDropControllerTests
+ * Build/Install/Run:
+ * atest WmTests:DragDropControllerTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 7222a99..981c01f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -24,6 +24,12 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+/**
+ * Tests for {@link PinnedStackController}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PinnedStackControllerTest
+ */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index e7c45d5..5672ff1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -48,7 +48,10 @@
import org.mockito.MockitoAnnotations;
/**
- * atest FrameworksServicesTests:com.android.server.wm.RecentsAnimationControllerTest
+ * Tests for {@link RecentsAnimationController}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:RecentsAnimationControllerTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 7d19baa..085b86a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -53,7 +53,10 @@
import org.mockito.MockitoAnnotations;
/**
- * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
+ * Tests for {@link RemoteAnimationController}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:RemoteAnimationControllerTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index c1db6a6..fdd08fa1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -16,7 +16,7 @@
* Tests for the {@link RootWindowContainer} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ * atest WmTests:RootWindowContainerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 60025f0..e27dc06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -72,7 +72,7 @@
* Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
+ * atest WmTests:ScreenDecorWindowTests
*/
// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
// TODO: Test non-Activity windows.
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
index 9f2645c..83e2b7b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -34,7 +34,7 @@
* Test class for {@link StackWindowController}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:StackWindowControllerTests
+ * atest WmTests:StackWindowControllerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 4551c36..69aac2d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -27,6 +27,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
import android.animation.ValueAnimator;
import android.graphics.Matrix;
@@ -54,14 +56,13 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
import java.util.concurrent.CountDownLatch;
/**
* Test class for {@link SurfaceAnimationRunner}.
*
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
+ * Build/Install/Run:
+ * atest WmTests:SurfaceAnimationRunnerTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 7b5e8de..b786cd5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -50,7 +50,8 @@
/**
* Test class for {@link SurfaceAnimatorTest}.
*
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimatorTest
+ * Build/Install/Run:
+ * atest WmTests:SurfaceAnimatorTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 17b7fc1..fb10a65 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -45,7 +45,8 @@
/**
* Tests for the {@link TaskPositioner} class.
*
- * runtest frameworks-services -c com.android.server.wm.TaskPositionerTests
+ * Build/Install/Run:
+ * atest WmTests:TaskPositionerTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index ced0847..e7ef31d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -40,7 +40,8 @@
/**
* Tests for the {@link TaskPositioningController} class.
*
- * atest com.android.server.wm.TaskPositioningControllerTests
+ * Build/Install/Run:
+ * atest WmTests:TaskPositioningControllerTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index c9d8004..50878ce 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -33,7 +33,8 @@
/**
* Test class for {@link TaskSnapshotCache}.
*
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest
+ * Build/Install/Run:
+ * atest WmTests:TaskSnapshotCacheTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index efce063..c78d200 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -20,7 +20,8 @@
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.TRANSIT_UNSET;
-import static com.android.server.wm.TaskSnapshotController.*;
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME;
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
import static junit.framework.Assert.assertEquals;
@@ -38,7 +39,8 @@
/**
* Test class for {@link TaskSnapshotController}.
*
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotControllerTest
+ * Build/Install/Run:
+ * atest WmTests:TaskSnapshotControllerTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 600b2e5..31cf47b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -46,7 +46,8 @@
/**
* Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader}
*
- * atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest
+ * Build/Install/Run:
+ * atest WmTests:TaskSnapshotPersisterLoaderTest
*/
@MediumTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 91074b9..6df179e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -49,7 +49,8 @@
/**
* Test class for {@link TaskSnapshotSurface}.
*
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotSurfaceTest
+ * Build/Install/Run:
+ * atest WmTests:TaskSnapshotSurfaceTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
similarity index 83%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index 9fa5ba4..cc6755e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -37,7 +37,7 @@
* Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.TaskStackContainersTests
+ * atest WmTests:TaskStackContainersTests
*/
@SmallTest
@Presubmit
@@ -109,4 +109,22 @@
assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
}
+
+ @Test
+ public void testDisplayPositionWithPinnedStack() {
+ // The display contains pinned stack that was added in {@link #setUp}.
+ final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+
+ // Add another display at top.
+ sWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+ false /* includingParents */);
+
+ // Move the task of {@code mDisplayContent} to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+ final int indexOfDisplayWithPinnedStack = sWm.mRoot.mChildren.indexOf(mDisplayContent);
+
+ assertEquals("The testing DisplayContent should be moved to top with task",
+ sWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 53a1185..d60ed8e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -34,7 +34,7 @@
* Tests for the {@link TaskStack} class.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.TaskStackTests
+ * atest WmTests:TaskStackTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index edd7664..48abce2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -33,7 +33,7 @@
* Test class for {@link TaskWindowContainerController}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.TaskWindowContainerControllerTests
+ * atest WmTests:TaskWindowContainerControllerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
rename to services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 353aa7d..032f416 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import com.android.internal.os.IResultReceiver;
-
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -27,6 +25,8 @@
import android.view.DragEvent;
import android.view.IWindow;
+import com.android.internal.os.IResultReceiver;
+
public class TestIWindow extends IWindow.Stub {
@Override
public void executeCommand(String command, String parameters,
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
rename to services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 3ac97027..616276a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -30,7 +30,8 @@
/**
* Test class for {@link AppTransition}.
*
- * runtest frameworks-services -c com.android.server.wm.UnknownAppVisibilityControllerTest
+ * Build/Install/Run:
+ * atest WmTests:UnknownAppVisibilityControllerTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
index 827d938..de49e75 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -41,7 +41,8 @@
/**
* Tests for the {@link WindowAnimationSpec} class.
*
- * atest FrameworksServicesTests:com.android.server.wm.WindowAnimationSpecTest
+ * Build/Install/Run:
+ * atest WmTests:WindowAnimationSpecTest
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index e7cc285..17a0f91b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -44,7 +44,7 @@
* Test class to for {@link android.app.WindowConfiguration}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowConfigurationTests
+ * atest WmTests:WindowConfigurationTests
*/
@SmallTest
@FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
index 6b1feb8..b97a79b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -36,7 +36,7 @@
* Test class for {@link WindowContainerController}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowContainerControllerTests
+ * atest WmTests:WindowContainerControllerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index dc763b9..aa9fdb8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -57,7 +57,7 @@
* Test class for {@link WindowContainer}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.WindowContainerTests
+ * atest WmTests:WindowContainerTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index ffc8622..7fbd882 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -37,6 +37,9 @@
/**
* Tests for {@link WindowContainer#forAllWindows} and various implementations.
+ *
+ * Build/Install/Run:
+ * atest WmTests:WindowContainerTraversalTests
*/
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index e648230..f93a4ec 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -21,7 +21,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import android.app.ActivityManager.TaskDescription;
import android.content.res.Configuration;
@@ -44,7 +43,8 @@
/**
* Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
*
- * Build/Install/Run: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
+ * Build/Install/Run:
+ * atest WmTests:WindowFrameTests
*/
@SmallTest
@Presubmit
@@ -202,8 +202,8 @@
// When mFrame extends past cf, the content insets are
// the difference between mFrame and ContentFrame. Visible
// and stable frames work the same way.
- final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -217,7 +217,7 @@
w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
w.mRequestedWidth = 100;
w.mRequestedHeight = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 100, 100, 200, 200);
assertContentInset(w, 0, 0, 0, 0);
// In this case the frames are shrunk to the window frame.
@@ -238,8 +238,8 @@
// Here the window has FILL_PARENT, FILL_PARENT
// so we expect it to fill the entire available frame.
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
- w.computeFrameLw(windowFrames);
+ w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// It can select various widths and heights within the bounds.
@@ -247,14 +247,14 @@
// and we use mRequestedWidth/mRequestedHeight
w.mAttrs.width = 300;
w.mAttrs.height = 300;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// Explicit width and height without requested width/height
// gets us nothing.
assertFrame(w, 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
assertFrame(w, 0, 0, 300, 300);
@@ -267,14 +267,14 @@
w.mRequestedWidth = -1;
w.mAttrs.width = 100;
w.mAttrs.height = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
@@ -282,7 +282,7 @@
w.mAttrs.y = 300;
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
@@ -292,16 +292,16 @@
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, 600, 600, 900, 900);
}
@@ -322,8 +322,9 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -336,7 +337,7 @@
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
int contentInsetRight = taskRight - cfRight;
int contentInsetBottom = taskBottom - cfBottom;
@@ -354,7 +355,7 @@
final int insetBottom = insetTop + (taskBottom - taskTop);
task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
@@ -384,13 +385,14 @@
// We use a decor content frame with insets to produce cropping.
Rect dcf = new Rect(cf);
- final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw();
assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
// Now we set up a window which doesn't fill the entire decor frame.
@@ -404,7 +406,7 @@
w.mAttrs.height = logicalHeight / 2;
w.mRequestedWidth = logicalWidth / 2;
w.mRequestedHeight = logicalHeight / 2;
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
// Normally the crop is shrunk from the decor frame
// to the computed window frame.
@@ -437,8 +439,9 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -455,7 +458,7 @@
pf.set(0, 0, logicalWidth, logicalHeight);
task.mFullscreenForTest = true;
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
assertContentFrame(w, cf);
assertContentInset(w, 0, 0, 0, 0);
@@ -473,9 +476,10 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
@@ -497,9 +501,10 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ final WindowFrames windowFrames = w.getWindowFrames();
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw(windowFrames);
+ w.computeFrameLw();
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index b43d9a6..b832391 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -57,6 +57,10 @@
import android.view.SurfaceControl;
import android.view.WindowManager;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Test;
@@ -65,14 +69,11 @@
import java.util.Arrays;
import java.util.LinkedList;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
/**
* Tests for the {@link WindowState} class.
*
- * atest FrameworksServicesTests:com.android.server.wm.WindowStateTests
+ * Build/Install/Run:
+ * atest WmTests:WindowStateTests
*/
@SmallTest
@FlakyTest(bugId = 74078662)
@@ -391,14 +392,14 @@
@Test
public void testDisplayCutoutIsCalculatedRelativeToFrame() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- WindowFrames wf = new WindowFrames();
+ WindowFrames wf = app.getWindowFrames();
wf.mParentFrame.set(7, 10, 185, 380);
wf.mDisplayFrame.set(wf.mParentFrame);
final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22),
Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400)));
wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
- app.computeFrameLw(wf);
+ app.computeFrameLw();
assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20)));
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowSurfacePlacerTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowSurfacePlacerTest.java
index 057f047..f7ac344 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowSurfacePlacerTest.java
@@ -33,6 +33,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link WindowSurfacePlacer}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:WindowSurfacePlacerTest
+ */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index a1b1640..d9bf815 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -16,6 +16,20 @@
package com.android.server.wm;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -26,23 +40,9 @@
import android.view.IApplicationToken;
import android.view.IWindow;
import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
-import static android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
import org.mockito.invocation.InvocationOnMock;
/**
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 3732486..18b148e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -39,7 +39,7 @@
* Tests for the {@link WindowToken} class.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowTokenTests
+ * atest WmTests:WindowTokenTests
*/
@SmallTest
@FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index bbc6550..9f9e9f9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -37,7 +37,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.Preconditions;
-import com.android.server.wm.WindowManagerTraceProto;
import org.junit.After;
import org.junit.Before;
@@ -55,7 +54,7 @@
* Test class for {@link WindowTracing}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowTracingTest
+ * atest WmTests:WindowTracingTest
*/
@SmallTest
@FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 8f9fb1b..07476af 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -51,7 +51,7 @@
* Tests for the {@link WindowLayersController} class.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ * atest WmTests:ZOrderingTests
*/
@SmallTest
@FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index f82b012..6ad1677 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -21,14 +21,16 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static com.android.server.wm.utils.CoordinateTransforms.transformLogicalToPhysicalCoordinates;
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
-
-
+import static com.android.server.wm.utils.CoordinateTransforms
+ .transformLogicalToPhysicalCoordinates;
+import static com.android.server.wm.utils.CoordinateTransforms
+ .transformPhysicalToLogicalCoordinates;
import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -40,6 +42,12 @@
import org.junit.Test;
import org.junit.rules.ErrorCollector;
+/**
+ * Tests for {@link CoordinateTransforms}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:CoordinateTransformsTest
+ */
public class CoordinateTransformsTest {
private static final int W = 200;
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
index 3364aef..e2d06cd 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
@@ -32,6 +32,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link InsetUtils}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:InsetUtilsTest
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
index 5d08920..09d50bf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
@@ -34,6 +34,12 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for {@link RotationCache}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:RotationCacheTest
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
@FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index 9ce3dca..74a6323 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -16,7 +16,6 @@
package com.android.server.wm.utils;
-
import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.DisplayCutout.fromBoundingRect;
@@ -39,7 +38,8 @@
/**
* Tests for {@link WmDisplayCutout}
*
- * Run with: atest WmDisplayCutoutTest
+ * Build/Install/Run:
+ * atest WmTests:WmDisplayCutoutTest
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 589bcdc..cf47ad3 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -40,6 +40,7 @@
import com.android.server.usb.descriptors.UsbDescriptor;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import com.android.server.usb.descriptors.UsbDeviceDescriptor;
+import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
import com.android.server.usb.descriptors.report.TextReportCanvas;
import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
@@ -352,8 +353,6 @@
}
return false;
}
- UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
- logUsbDevice(parser);
if (isBlackListed(deviceClass, deviceSubclass)) {
if (DEBUG) {
@@ -362,6 +361,15 @@
return false;
}
+ UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
+ if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE
+ && !checkUsbInterfacesBlackListed(parser)) {
+ return false;
+ }
+
+ // Potentially can block as it may read data from the USB device.
+ logUsbDevice(parser);
+
synchronized (mLock) {
if (mDevices.get(deviceAddress) != null) {
Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
@@ -513,6 +521,29 @@
}
}
+ private boolean checkUsbInterfacesBlackListed(UsbDescriptorParser parser) {
+ // Device class needs to be obtained through the device interface. Ignore device only
+ // if ALL interfaces are black-listed.
+ boolean shouldIgnoreDevice = false;
+ for (UsbDescriptor descriptor: parser.getDescriptors()) {
+ if (!(descriptor instanceof UsbInterfaceDescriptor)) {
+ continue;
+ }
+ UsbInterfaceDescriptor iface = (UsbInterfaceDescriptor) descriptor;
+ shouldIgnoreDevice = isBlackListed(iface.getUsbClass(), iface.getUsbSubclass());
+ if (!shouldIgnoreDevice) {
+ break;
+ }
+ }
+ if (shouldIgnoreDevice) {
+ if (DEBUG) {
+ Slog.d(TAG, "usb interface class is black listed");
+ }
+ return false;
+ }
+ return true;
+ }
+
private native void monitorUsbHostBus();
private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index c5d6dc7..99ad1f4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,6 +19,8 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
@@ -1128,6 +1130,27 @@
}
}
+ @Override
+ public void getActiveServiceSupportedActions(List<String> voiceActions,
+ IVoiceActionCheckCallback callback) {
+ enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+ synchronized (this) {
+ if (mImpl == null) {
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.getActiveServiceSupportedActions(voiceActions, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
public void onSessionShown() {
synchronized (this) {
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 57e9f66..61d7d6c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -25,6 +25,8 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
@@ -57,6 +59,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
final static String TAG = "VoiceInteractionServiceManager";
@@ -177,6 +180,23 @@
activityTokens);
}
+ public void getActiveServiceSupportedActions(List<String> commands,
+ IVoiceActionCheckCallback callback) {
+ if (mService == null) {
+ Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
+ try {
+ callback.onComplete(null);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+ try {
+ mService.getActiveServiceSupportedActions(commands, callback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException while calling getActiveServiceSupportedActions", e);
+ }
+ }
+
public boolean hideSessionLocked() {
if (mActiveSession != null) {
return mActiveSession.hideLocked();
diff --git a/startop/scripts/app_startup/force_compiler_filter b/startop/scripts/app_startup/force_compiler_filter
new file mode 100755
index 0000000..08f983d
--- /dev/null
+++ b/startop/scripts/app_startup/force_compiler_filter
@@ -0,0 +1,143 @@
+#!/bin/bash
+#
+# 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.
+
+#
+# Forces an application APK to be compiled (by ART's dex2oat)
+# with a specific compiler filter.
+#
+# Example usage:
+# $> ./force_compiler_filter -p com.google.android.apps.maps -c speed-profile
+#
+# (The application may be started/stopped as a side effect)
+#
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "$DIR/lib/common"
+
+usage() {
+ cat <<EOF
+Usage: $(basename $0) [OPTION]...
+
+ Required:
+ -p, --package package of the app to recompile
+ -c, --compiler-filter override the compiler filter if set (default none)
+ valid options are listed by: adb shell cmd package, under compile -m
+
+ Optional:
+ -a, --activity activity of the app to recompile
+ -h, --help usage information (this)
+ -v, --verbose enable extra verbose printing
+ -w, --wait_time how long to wait for app startup (default 10) in seconds
+EOF
+}
+
+wait_time="10" # seconds
+
+parse_arguments() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -a|--activity)
+ activity="$2"
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -p|--package)
+ package="$2"
+ shift
+ ;;
+ -w|--wait_time)
+ wait_time="$2"
+ shift
+ ;;
+ -c|--compiler-filter)
+ compiler_filter="$2"
+ shift
+ ;;
+ -v|--verbose)
+ verbose="y"
+ ;;
+ esac
+ shift
+ done
+
+ if [[ -z "$compiler_filter" ]]; then
+ echo "Missing required --compiler-filter" >&2
+ echo ""
+ usage
+ exit 1
+ fi
+ if [[ -z "$package" ]]; then
+ echo "Missing required --package" >&2
+ echo ""
+ usage
+ exit 1
+ fi
+
+ if [[ "$activity" == "" ]]; then
+ activity="$(get_activity_name "$package")"
+ if [[ "$activity" == "" ]]; then
+ echo "Activity name could not be found, invalid package name?" 1>&2
+ exit 1
+ else
+ verbose_print "Activity name inferred: " "$activity"
+ fi
+ fi
+}
+
+force_package_compilation() {
+ local arg_compiler_filter="$1"
+ local arg_package="$2"
+
+ if [[ $arg_compiler_filter == speed-profile ]]; then
+ # Force the running app to dump its profiles to disk.
+ remote_pkill "$arg_package" -SIGUSR1
+ sleep 1 # give some time for above to complete.
+ fi
+
+ adb shell cmd package compile -m "$arg_compiler_filter" -f "$arg_package"
+}
+
+main() {
+ parse_arguments "$@"
+
+ if [[ $compiler_filter == speed-profile ]]; then
+ # screen needs to be unlocked in order to run an app
+ "$DIR"/unlock_screen
+
+ local output=$("$DIR"/launch_application "$package" "$activity")
+ if [[ $? -ne 0 ]]; then
+ echo "launching application failed" >&2
+ exit 1
+ fi
+
+ verbose_print "$output"
+ # give some time for app startup to complete.
+ # this is supposed to be an upper bound for measuring startup time.
+ sleep "$wait_time"
+ fi
+
+ force_package_compilation "$compiler_filter" "$package"
+
+ # kill the application to ensure next time it's started,
+ # it picks up the correct compilation filter.
+ adb shell am force-stop "$package"
+ remote_pkill "$package"
+}
+
+main "$@"
diff --git a/startop/scripts/app_startup/launch_application b/startop/scripts/app_startup/launch_application
index bc4ec51..8a68e50 100755
--- a/startop/scripts/app_startup/launch_application
+++ b/startop/scripts/app_startup/launch_application
@@ -20,6 +20,12 @@
launch_application() {
local package="$1"
local activity="$2"
+
+ # if there's any $s inside of the activity name, it needs to be escaped to \$.
+ # example '.app.honeycomb.Shell$HomeActivity'
+ # if the $ is not escaped, adb shell will try to evaluate $HomeActivity to a variable.
+ activity=${activity//\$/\\$}
+
local am_output="$(adb shell am start -S -W "$package"/"$activity")"
verbose_print adb shell am start -S -W "$package"/"$activity"
if [[ $? -ne 0 ]]; then
diff --git a/startop/scripts/app_startup/lib/common b/startop/scripts/app_startup/lib/common
index 4d5a53e..043d855 100755
--- a/startop/scripts/app_startup/lib/common
+++ b/startop/scripts/app_startup/lib/common
@@ -12,3 +12,42 @@
echo "$@" >&2
fi
}
+
+remote_pidof() {
+ local procname="$1"
+ adb shell ps | grep "$procname" | awk '{print $2;}'
+}
+
+remote_pkill() {
+ local procname="$1"
+ shift
+
+ local the_pids=$(remote_pidof "$procname")
+ local pid
+
+ for pid in $the_pids; do
+ verbose_print adb shell kill "$@" "$pid"
+ adb shell kill "$@" "$pid"
+ done
+}
+
+get_activity_name() {
+ local package="$1"
+ local action_key="android.intent.action.MAIN:"
+
+ # Example query-activities output being parsed:
+ #
+ # Activity #14:
+ # priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+ # com.google.android.videos/com.google.android.youtube.videos.EntryPoint
+ # Activity #15:
+ # priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+ # com.google.android.youtube/.app.honeycomb.Shell$HomeActivity
+
+ # Given package 'com.google.android.youtube' return '.app.honeycomb.Shell$HomeActivity'
+
+ local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package/")"
+ IFS="/" read -a array <<< "$activity_line"
+ local activity_name="${array[1]}"
+ echo "$activity_name"
+}
diff --git a/startop/scripts/app_startup/query_compiler_filter.py b/startop/scripts/app_startup/query_compiler_filter.py
new file mode 100755
index 0000000..dc97c66
--- /dev/null
+++ b/startop/scripts/app_startup/query_compiler_filter.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+#
+#
+# Query the current compiler filter for an application by its package name.
+# (By parsing the results of the 'adb shell dumpsys package $package' command).
+# The output is a string "$compilation_filter $compilation_reason $isa".
+#
+# See --help for more details.
+#
+# -----------------------------------
+#
+# Sample usage:
+#
+# $> ./query_compiler_filter.py --package com.google.android.calculator
+# speed-profile unknown arm64
+#
+
+import argparse
+import sys
+import re
+
+# TODO: refactor this with a common library file with analyze_metrics.py
+import app_startup_runner
+from app_startup_runner import _debug_print
+from app_startup_runner import execute_arbitrary_command
+
+from typing import List, NamedTuple, Iterable
+
+_DEBUG_FORCE = None # Ignore -d/--debug if this is not none.
+
+def parse_options(argv: List[str] = None):
+ """Parse command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Query the compiler filter for a package.")
+ # argparse considers args starting with - and -- optional in --help, even though required=True.
+ # by using a named argument group --help will clearly say that it's required instead of optional.
+ required_named = parser.add_argument_group('required named arguments')
+ required_named.add_argument('-p', '--package', action='store', dest='package', help='package of the application', required=True)
+
+ # optional arguments
+ # use a group here to get the required arguments to appear 'above' the optional arguments in help.
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-i', '--isa', '--instruction-set', action='store', dest='instruction_set', help='which instruction set to select. defaults to the first one available if not specified.', choices=('arm64', 'arm', 'x86_64', 'x86'))
+ optional_named.add_argument('-s', '--simulate', dest='simulate', action='store_true', help='Print which commands will run, but don\'t run the apps')
+ optional_named.add_argument('-d', '--debug', dest='debug', action='store_true', help='Add extra debugging output')
+
+ return parser.parse_args(argv)
+
+def remote_dumpsys_package(package: str, simulate: bool) -> str:
+ # --simulate is used for interactive debugging/development, but also for the unit test.
+ if simulate:
+ return """
+Dexopt state:
+ [%s]
+ path: /data/app/%s-D7s8PLidqqEq7Jc7UH_a5A==/base.apk
+ arm64: [status=speed-profile] [reason=unknown]
+ path: /data/app/%s-D7s8PLidqqEq7Jc7UH_a5A==/base.apk
+ arm: [status=speed] [reason=first-boot]
+ path: /data/app/%s-D7s8PLidqqEq7Jc7UH_a5A==/base.apk
+ x86: [status=quicken] [reason=install]
+""" %(package, package, package, package)
+
+ code, res = execute_arbitrary_command(['adb', 'shell', 'dumpsys', 'package', package], simulate=False, timeout=5)
+ if code:
+ return res
+ else:
+ raise AssertionError("Failed to dumpsys package, errors = %s", res)
+
+ParseTree = NamedTuple('ParseTree', [('label', str), ('children', List['ParseTree'])])
+DexoptState = ParseTree # With the Dexopt state: label
+ParseResult = NamedTuple('ParseResult', [('remainder', List[str]), ('tree', ParseTree)])
+
+def find_parse_subtree(parse_tree: ParseTree, match_regex: str) -> ParseTree:
+ if re.match(match_regex, parse_tree.label):
+ return parse_tree
+
+ for node in parse_tree.children:
+ res = find_parse_subtree(node, match_regex)
+ if res:
+ return res
+
+ return None
+
+def find_parse_children(parse_tree: ParseTree, match_regex: str) -> Iterable[ParseTree]:
+ for node in parse_tree.children:
+ if re.match(match_regex, node.label):
+ yield node
+
+def parse_tab_subtree(label: str, str_lines: List[str], separator=' ', indent=-1) -> ParseResult:
+ children = []
+
+ get_indent_level = lambda line: len(line) - len(line.lstrip())
+
+ line_num = 0
+
+ keep_going = True
+ while keep_going:
+ keep_going = False
+
+ for line_num in range(len(str_lines)):
+ line = str_lines[line_num]
+ current_indent = get_indent_level(line)
+
+ _debug_print("INDENT=%d, LINE=%s" %(current_indent, line))
+
+ current_label = line.lstrip()
+
+ # skip empty lines
+ if line.lstrip() == "":
+ continue
+
+ if current_indent > indent:
+ parse_result = parse_tab_subtree(current_label, str_lines[line_num+1::], separator, current_indent)
+ str_lines = parse_result.remainder
+ children.append(parse_result.tree)
+ keep_going = True
+ else:
+ # current_indent <= indent
+ keep_going = False
+
+ break
+
+ new_remainder = str_lines[line_num::]
+ _debug_print("NEW REMAINDER: ", new_remainder)
+
+ parse_tree = ParseTree(label, children)
+ return ParseResult(new_remainder, parse_tree)
+
+def parse_tab_tree(str_tree: str, separator=' ', indentation_level=-1) -> ParseTree:
+
+ label = None
+ lst = []
+
+ line_num = 0
+ line_lst = str_tree.split("\n")
+
+ return parse_tab_subtree("", line_lst, separator, indentation_level).tree
+
+def parse_dexopt_state(dumpsys_tree: ParseTree) -> DexoptState:
+ res = find_parse_subtree(dumpsys_tree, "Dexopt(\s+)state[:]?")
+ if not res:
+ raise AssertionError("Could not find the Dexopt state")
+ return res
+
+def find_first_compiler_filter(dexopt_state: DexoptState, package: str, instruction_set: str) -> str:
+ lst = find_all_compiler_filters(dexopt_state, package)
+
+ _debug_print("all compiler filters: ", lst)
+
+ for compiler_filter_info in lst:
+ if not instruction_set:
+ return compiler_filter_info
+
+ if compiler_filter_info.isa == instruction_set:
+ return compiler_filter_info
+
+ return None
+
+CompilerFilterInfo = NamedTuple('CompilerFilterInfo', [('isa', str), ('status', str), ('reason', str)])
+
+def find_all_compiler_filters(dexopt_state: DexoptState, package: str) -> List[CompilerFilterInfo]:
+
+ lst = []
+ package_tree = find_parse_subtree(dexopt_state, re.escape("[%s]" %package))
+
+ if not package_tree:
+ raise AssertionError("Could not find any package subtree for package %s" %(package))
+
+ _debug_print("package tree: ", package_tree)
+
+ for path_tree in find_parse_children(package_tree, "path: "):
+ _debug_print("path tree: ", path_tree)
+
+ matchre = re.compile("([^:]+):\s+\[status=([^\]]+)\]\s+\[reason=([^\]]+)\]")
+
+ for isa_node in find_parse_children(path_tree, matchre):
+
+ matches = re.match(matchre, isa_node.label).groups()
+
+ info = CompilerFilterInfo(*matches)
+ lst.append(info)
+
+ return lst
+
+def main() -> int:
+ opts = parse_options()
+ app_startup_runner._debug = opts.debug
+ if _DEBUG_FORCE is not None:
+ app_startup_runner._debug = _DEBUG_FORCE
+ _debug_print("parsed options: ", opts)
+
+ # Note: This can often 'fail' if the package isn't actually installed.
+ package_dumpsys = remote_dumpsys_package(opts.package, opts.simulate)
+ _debug_print("package dumpsys: ", package_dumpsys)
+ dumpsys_parse_tree = parse_tab_tree(package_dumpsys, package_dumpsys)
+ _debug_print("parse tree: ", dumpsys_parse_tree)
+ dexopt_state = parse_dexopt_state(dumpsys_parse_tree)
+
+ filter = find_first_compiler_filter(dexopt_state, opts.package, opts.instruction_set)
+
+ if filter:
+ print(filter.status, end=' ')
+ print(filter.reason, end=' ')
+ print(filter.isa)
+ else:
+ print("ERROR: Could not find any compiler-filter for package %s, isa %s" %(opts.package, opts.instruction_set), file=sys.stderr)
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/query_compiler_filter_test.py b/startop/scripts/app_startup/query_compiler_filter_test.py
new file mode 100755
index 0000000..a751a43
--- /dev/null
+++ b/startop/scripts/app_startup/query_compiler_filter_test.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+
+"""
+Unit tests for the query_compiler_filter.py script.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./query_compiler_filter.py
+ $> pytest query_compiler_filter.py
+ $> python -m pytest query_compiler_filter.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+# global imports
+from contextlib import contextmanager
+import io
+import shlex
+import sys
+import typing
+
+# pip imports
+import pytest
+
+# local imports
+import query_compiler_filter as qcf
+
+@contextmanager
+def redirect_stdout_stderr():
+ """Redirect stdout/stderr to a new StringIO for duration of context."""
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ new_stdout = io.StringIO()
+ sys.stdout = new_stdout
+ new_stderr = io.StringIO()
+ sys.stderr = new_stderr
+ try:
+ yield (new_stdout, new_stderr)
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ # Seek back to the beginning so we can read whatever was written into it.
+ new_stdout.seek(0)
+ new_stderr.seek(0)
+
+@contextmanager
+def replace_argv(argv):
+ """ Temporarily replace argv for duration of this context."""
+ old_argv = sys.argv
+ sys.argv = [sys.argv[0]] + argv
+ try:
+ yield
+ finally:
+ sys.argv = old_argv
+
+def exec_main(argv):
+ """Run the query_compiler_filter main function with the provided arguments.
+
+ Returns the stdout result when successful, assertion failure otherwise.
+ """
+ try:
+ with redirect_stdout_stderr() as (the_stdout, the_stderr):
+ with replace_argv(argv):
+ code = qcf.main()
+ assert 0 == code, the_stderr.readlines()
+
+ all_lines = the_stdout.readlines()
+ return "".join(all_lines)
+ finally:
+ the_stdout.close()
+ the_stderr.close()
+
+def test_query_compiler_filter():
+ # no --instruction-set specified: provide whatever was the 'first' filter.
+ assert exec_main(['--simulate',
+ '--package', 'com.google.android.apps.maps']) == \
+ "speed-profile unknown arm64\n"
+
+ # specifying an instruction set finds the exact compiler filter match.
+ assert exec_main(['--simulate',
+ '--package', 'com.google.android.apps.maps',
+ '--instruction-set', 'arm64']) == \
+ "speed-profile unknown arm64\n"
+
+ assert exec_main(['--simulate',
+ '--package', 'com.google.android.apps.maps',
+ '--instruction-set', 'arm']) == \
+ "speed first-boot arm\n"
+
+ assert exec_main(['--simulate',
+ '--debug',
+ '--package', 'com.google.android.apps.maps',
+ '--instruction-set', 'x86']) == \
+ "quicken install x86\n"
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
index 1ff5fc6..56bffa8 100755
--- a/startop/scripts/app_startup/run_app_with_prefetch
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -92,8 +92,7 @@
shift
;;
--compiler-filter)
- # ignore any '--compiler-filter xyz' settings.
- # FIXME: app_startup_runner.py should not be passing this flag.
+ compiler_filter="$2"
shift
;;
*)
@@ -112,18 +111,6 @@
echo "$@"
}
-get_activity_name() {
- local package="$1"
- local action_key="android.intent.action.MAIN:"
-
- local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")"
- #echo $activity_line
- IFS="/" read -a array <<< "$activity_line"
- local activity_name="${array[1]}"
- echo "$activity_name"
- #adb shell am start "$package/$activity_name"
-}
-
find_package_path() {
local pkg="$1"
@@ -134,11 +121,6 @@
echo "$res"
}
-remote_pkill() {
- local what="$1"
- adb shell "for i in $(pid $what); do kill \$i; done"
-}
-
# Main entry point
if [[ $# -eq 0 ]]; then
usage
@@ -194,9 +176,6 @@
fi
verbose_print "Package was in path '$package_path'"
-
-
-
keep_application_trace_file=n
application_trace_file_path="$package_path/TraceFile.pb"
trace_file_directory="$package_path"
@@ -286,6 +265,47 @@
fi
}
+configure_compiler_filter() {
+ local the_compiler_filter="$1"
+ local the_package="$2"
+ local the_activity="$3"
+
+ if [[ -z $the_compiler_filter ]]; then
+ verbose_print "No --compiler-filter specified, don't need to force it."
+ return 0
+ fi
+
+ local current_compiler_filter_info="$("$DIR"/query_compiler_filter.py --package "$the_package")"
+ local res=$?
+ if [[ $res -ne 0 ]]; then
+ return $res
+ fi
+
+ local current_compiler_filter
+ local current_reason
+ local current_isa
+ read current_compiler_filter current_reason current_isa <<< "$current_compiler_filter_info"
+
+ verbose_print "Compiler Filter="$current_compiler_filter "Reason="$current_reason "Isa="$current_isa
+
+ # Don't trust reasons that aren't 'unknown' because that means we didn't manually force the compilation filter.
+ # (e.g. if any automatic system-triggered compilations are not unknown).
+ if [[ $current_reason != "unknown" ]] || [[ $current_compiler_filter != $the_compiler_filter ]]; then
+ verbose_print "$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity"
+ "$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity"
+ res=$?
+ else
+ verbose_print "Queried compiler-filter matched requested compiler-filter, skip forcing."
+ res=0
+ fi
+
+ return $res
+}
+
+# Ensure the APK is currently compiled with whatever we passed in via --compiler-filter.
+# No-op if this option was not passed in.
+configure_compiler_filter "$compiler_filter" "$package" "$activity" || exit 1
+
# TODO: This loop logic could probably be moved into app_startup_runner.py
for ((i=0;i<count;++i)) do
verbose_print "=========================================="
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 8b0211e..3ad0f0c 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -18,6 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -118,7 +119,7 @@
}
/** The unique ID of the call. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public String getId() {
return mId;
}
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 279804e..097e352 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -43,7 +44,7 @@
public final class PhoneAccountHandle implements Parcelable {
@UnsupportedAppUsage
private final ComponentName mComponentName;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mId;
private final UserHandle mUserHandle;
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 2fa0d90..4846092 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -30,6 +30,7 @@
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
+import android.os.Build;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SmsMessage;
@@ -374,7 +375,7 @@
* Return cursor for table query.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static Cursor query(ContentResolver cr, String[] projection,
String where, String orderBy) {
return cr.query(CONTENT_URI, projection, where,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ffbe7d3..7506d00 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1070,6 +1070,16 @@
/**
* Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats.
+ *
+ * <p>Available options are:
+ * <ul>
+ * <li> 0: %s</li>
+ * <li> 1: %s Wi-Fi Calling</li>
+ * <li> 2: WLAN Call</li>
+ * <li> 3: %s WLAN Call</li>
+ * <li> 4: %s Wi-Fi</li>
+ * <li> 5: WiFi Calling | %s</li>
+ * <li> 6: %s VoWifi</li>
* @hide
*/
public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
@@ -1077,6 +1087,15 @@
public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
/**
+ * Use root locale when reading wfcSpnFormats.
+ *
+ * If true, then the root locale will always be used when reading wfcSpnFormats. This means the
+ * non localized version of wfcSpnFormats will be used.
+ * @hide
+ */
+ public static final String KEY_WFC_SPN_USE_ROOT_LOCALE = "wfc_spn_use_root_locale";
+
+ /**
* The Component Name of the activity that can setup the emergency addrees for WiFi Calling
* as per carrier requirement.
* @hide
@@ -2263,6 +2282,7 @@
sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+ sDefaults.putBoolean(KEY_WFC_SPN_USE_ROOT_LOCALE, false);
sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, "");
sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
@@ -2272,7 +2292,7 @@
sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
- sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true);
// MMS defaults
sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 91e24a9..e40bae1 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -603,7 +603,7 @@
/**
* Returns the message body as a String, if it exists and is text based.
- * @return message body is there is one, otherwise null
+ * @return message body if there is one, otherwise null
*/
public String getMessageBody() {
return mWrappedSmsMessage.getMessageBody();
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8ebfec4..777b850 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -41,6 +41,7 @@
import android.net.INetworkPolicyManager;
import android.net.NetworkCapabilities;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -59,6 +60,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -84,9 +86,8 @@
/** @hide */
public static final int INVALID_PHONE_INDEX = -1;
- /** An invalid slot identifier */
- /** @hide */
- public static final int INVALID_SIM_SLOT_INDEX = -1;
+ /** Indicates invalid sim slot. This can be returned by {@link #getSlotIndex(int)}. */
+ public static final int INVALID_SIM_SLOT_INDEX = -2;
/** Indicates the caller wants the default sub id. */
/** @hide */
@@ -137,9 +138,8 @@
/** @hide */
public static final String SIM_SLOT_INDEX = "sim_id";
- /** SIM is not inserted */
- /** @hide */
- public static final int SIM_NOT_INSERTED = -1;
+ /** Indicates SIM is not inserted. This can be returned by {@link #getSlotIndex(int)}. */
+ public static final int SIM_NOT_INSERTED = -3;
/**
* TelephonyProvider column name for user displayed name.
@@ -191,7 +191,7 @@
* The name_source is from the user
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final int NAME_SOURCE_USER_INPUT = 2;
/**
@@ -1262,16 +1262,22 @@
/**
* Get slotIndex associated with the subscription.
- * @return slotIndex as a positive integer or a negative value if an error either
- * SIM_NOT_INSERTED or < 0 if an invalid slot index
- * @hide
+ *
+ * @param subscriptionId the unique SubscriptionInfo index in database
+ * @return slotIndex as a positive integer or a negative value,
+ * <ol>
+ * <li>{@link #INVALID_SUBSCRIPTION_ID} if the supplied subscriptionId is invalid </li>
+ * <li>{@link #SIM_NOT_INSERTED} if sim is not inserted </li>
+ * <li>{@link #INVALID_SIM_SLOT_INDEX} if the supplied subscriptionId doesn't have an
+ * associated slot index </li>
+ * </ol>
*/
- @UnsupportedAppUsage
- public static int getSlotIndex(int subId) {
- if (!isValidSubscriptionId(subId)) {
+ public static int getSlotIndex(int subscriptionId) {
+ if (!isValidSubscriptionId(subscriptionId)) {
if (DBG) {
- logd("[getSlotIndex]- fail");
+ logd("[getSlotIndex]- supplied subscriptionId is invalid. ");
}
+ return INVALID_SUBSCRIPTION_ID;
}
int result = INVALID_SIM_SLOT_INDEX;
@@ -1279,7 +1285,7 @@
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getSlotIndex(subId);
+ result = iSub.getSlotIndex(subscriptionId);
}
} catch (RemoteException ex) {
// ignore it
@@ -1610,7 +1616,7 @@
}
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static boolean isValidSlotIndex(int slotIndex) {
return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSimCount();
}
@@ -1810,6 +1816,19 @@
*/
@UnsupportedAppUsage
public static Resources getResourcesForSubId(Context context, int subId) {
+ return getResourcesForSubId(context, subId, false);
+ }
+
+ /**
+ * Returns the resources associated with Subscription.
+ * @param context Context object
+ * @param subId Subscription Id of Subscription who's resources are required
+ * @param useRootLocale if root locale should be used. Localized locale is used if false.
+ * @return Resources associated with Subscription.
+ * @hide
+ */
+ public static Resources getResourcesForSubId(Context context, int subId,
+ boolean useRootLocale) {
final SubscriptionInfo subInfo =
SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
@@ -1821,6 +1840,11 @@
newConfig.mnc = subInfo.getMnc();
if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
}
+
+ if (useRootLocale) {
+ newConfig.setLocale(Locale.ROOT);
+ }
+
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
DisplayMetrics newMetrics = new DisplayMetrics();
newMetrics.setTo(metrics);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2ac0afe..a8bcbe3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -39,6 +39,7 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.BatteryStats;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
@@ -68,6 +69,7 @@
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyProperties;
@@ -702,7 +704,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
"android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
@@ -1499,23 +1501,24 @@
Rlog.d(TAG, "getCellLocation returning null because telephony is null");
return null;
}
+
Bundle bundle = telephony.getCellLocation(mContext.getOpPackageName());
- if (bundle.isEmpty()) {
- Rlog.d(TAG, "getCellLocation returning null because bundle is empty");
+ if (bundle == null || bundle.isEmpty()) {
+ Rlog.d(TAG, "getCellLocation returning null because CellLocation is unavailable");
return null;
}
+
CellLocation cl = CellLocation.newFromBundle(bundle);
- if (cl.isEmpty()) {
- Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty");
+ if (cl == null || cl.isEmpty()) {
+ Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or"
+ + " phone type doesn't match CellLocation type");
return null;
}
+
return cl;
} catch (RemoteException ex) {
Rlog.d(TAG, "getCellLocation returning null due to RemoteException " + ex);
return null;
- } catch (NullPointerException ex) {
- Rlog.d(TAG, "getCellLocation returning null due to NullPointerException " + ex);
- return null;
}
}
@@ -1690,7 +1693,7 @@
}
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int getPhoneTypeFromProperty(int phoneId) {
String type = getTelephonyProperty(phoneId,
TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
@@ -5787,21 +5790,46 @@
* @param persistSelection whether the selection will persist until reboot. If true, only allows
* attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
* normal network selection next time.
- * @return true on success; false on any failure.
+ * @return {@code true} on success; {@code false} on any failure.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
+ return setNetworkSelectionModeManual(
+ new OperatorInfo(
+ "" /* operatorAlphaLong */, "" /* operatorAlphaShort */, operatorNumeric),
+ persistSelection);
+ }
+
+ /**
+ * Ask the radio to connect to the input network and change selection mode to manual.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param operatorInfo included the PLMN id, long name, short name of the operator to attach to.
+ * @param persistSelection whether the selection will persist until reboot. If true, only allows
+ * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+ * normal network selection next time.
+ * @return {@code true} on success; {@code true} on any failure.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setNetworkSelectionModeManual(
+ OperatorInfo operatorInfo, boolean persistSelection) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.setNetworkSelectionModeManual(
- getSubId(), operatorNumeric, persistSelection);
+ getSubId(), operatorInfo, persistSelection);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
- } catch (NullPointerException ex) {
- Rlog.e(TAG, "setNetworkSelectionModeManual NPE", ex);
}
return false;
}
@@ -6215,36 +6243,25 @@
/**
* @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
public boolean endCall() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.endCall();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#endCall", e);
- }
return false;
}
/**
* @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
* @hide
+ * @removed
*/
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void answerRingingCall() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.answerRingingCall();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#answerRingingCall", e);
- }
+
}
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 1c33582..e1c770c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -865,14 +865,15 @@
* Ask the radio to connect to the input network and change selection mode to manual.
*
* @param subId the id of the subscription.
- * @param operatorNumeric the PLMN of the operator to attach to.
- * @param persistSelection Whether the selection will persist until reboot. If true, only allows
+ * @param operatorInfo the operator inforamtion, included the PLMN, long name and short name of
+ * the operator to attach to.
+ * @param persistSelection whether the selection will persist until reboot. If true, only allows
* attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
* normal network selection next time.
- * @return true if the request suceeded.
+ * @return {@code true} on success; {@code true} on any failure.
*/
- boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric,
- boolean persistSelection);
+ boolean setNetworkSelectionModeManual(
+ int subId, in OperatorInfo operatorInfo, boolean persisSelection);
/**
* Set the preferred network type.
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index d0245a0..a47e2b0 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -21,7 +21,7 @@
import android.os.Parcelable;
/**
- * {@hide}
+ * @hide
*/
public class OperatorInfo implements Parcelable {
public enum State {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 6bf22a0..8015b07 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -33,6 +33,7 @@
import com.android.internal.telephony.SmsConstants;
import java.io.UnsupportedEncodingException;
+import java.util.Locale;
/**
* Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
@@ -44,16 +45,34 @@
* Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
*/
private static final String[] LANGUAGE_CODES_GROUP_0 = {
- "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
- "pl", null
+ Locale.GERMAN.getLanguage(), // German
+ Locale.ENGLISH.getLanguage(), // English
+ Locale.ITALIAN.getLanguage(), // Italian
+ Locale.FRENCH.getLanguage(), // French
+ new Locale("es").getLanguage(), // Spanish
+ new Locale("nl").getLanguage(), // Dutch
+ new Locale("sv").getLanguage(), // Swedish
+ new Locale("da").getLanguage(), // Danish
+ new Locale("pt").getLanguage(), // Portuguese
+ new Locale("fi").getLanguage(), // Finnish
+ new Locale("nb").getLanguage(), // Norwegian
+ new Locale("el").getLanguage(), // Greek
+ new Locale("tr").getLanguage(), // Turkish
+ new Locale("hu").getLanguage(), // Hungarian
+ new Locale("pl").getLanguage(), // Polish
+ null
};
/**
* Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
*/
private static final String[] LANGUAGE_CODES_GROUP_2 = {
- "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
- null, null
+ new Locale("cs").getLanguage(), // Czech
+ new Locale("he").getLanguage(), // Hebrew
+ new Locale("ar").getLanguage(), // Arabic
+ new Locale("ru").getLanguage(), // Russian
+ new Locale("is").getLanguage(), // Icelandic
+ null, null, null, null, null, null, null, null, null, null, null
};
private static final char CARRIAGE_RETURN = 0x0d;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 4790b75..6b3df94 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -93,11 +93,23 @@
* converted. If the array size is more than needed, the rest of array remains unchanged.
*/
public static void bcdToBytes(String bcd, byte[] bytes) {
+ bcdToBytes(bcd, bytes, 0);
+ }
+
+ /**
+ * Converts BCD string to bytes and put it into the given byte array.
+ *
+ * @param bcd This should have an even length. If not, an "0" will be appended to the string.
+ * @param bytes If the array size is less than needed, the rest of the BCD string isn't be
+ * converted. If the array size is more than needed, the rest of array remains unchanged.
+ * @param offset the offset into the bytes[] to fill the data
+ */
+ public static void bcdToBytes(String bcd, byte[] bytes, int offset) {
if (bcd.length() % 2 != 0) {
bcd += "0";
}
- int size = Math.min(bytes.length * 2, bcd.length());
- for (int i = 0, j = 0; i + 1 < size; i += 2, j++) {
+ int size = Math.min((bytes.length - offset) * 2, bcd.length());
+ for (int i = 0, j = offset; i + 1 < size; i += 2, j++) {
bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i)));
}
}
@@ -125,6 +137,20 @@
}
/**
+ * Convert a 5 or 6 - digit PLMN string to a nibble-swizzled encoding as per 24.008 10.5.1.3
+ *
+ * @param plmn the PLMN to convert
+ * @param data a byte array for the output
+ * @param offset the offset into data to start writing
+ */
+ public static void stringToBcdPlmn(final String plmn, byte[] data, int offset) {
+ char digit6 = (plmn.length() > 5) ? plmn.charAt(5) : 'F';
+ data[offset] = (byte) (charToByte(plmn.charAt(1)) << 4 | charToByte(plmn.charAt(0)));
+ data[offset + 1] = (byte) (charToByte(digit6) << 4 | charToByte(plmn.charAt(2)));
+ data[offset + 2] = (byte) (charToByte(plmn.charAt(4)) << 4 | charToByte(plmn.charAt(3)));
+ }
+
+ /**
* Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
*/
public static String
@@ -844,7 +870,7 @@
}
/**
- * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
+ * Converts a character of [0-9a-fA-F] to its hex value in a byte. If the character is not a
* hex number, 0 will be returned.
*/
private static byte charToByte(char c) {
diff --git a/tests/ImfTest/Android.mk b/tests/ImfTest/Android.mk
deleted file mode 100644
index a8f5b08..0000000
--- a/tests/ImfTest/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := ImfTest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/ImfTest/AndroidManifest.xml b/tests/ImfTest/AndroidManifest.xml
deleted file mode 100644
index 82dbe75..0000000
--- a/tests/ImfTest/AndroidManifest.xml
+++ /dev/null
@@ -1,146 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.imftest">
-
- <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-
- <application>
-
- <activity android:name=".samples.InputTypeActivity" android:label="Input Type Activity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ButtonActivity" android:label="Button Activity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityNonScrollablePanScan" android:label="Big ET !Scroll Pan/Scan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ManyEditTextActivityNoScrollPanScan" android:label="ManyEditTextActivityNoScrollPanScan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityNonScrollableResize" android:label="Big ET !Scroll Resize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityScrollablePanScan" android:label="Big ET Scroll Pan/Scan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BigEditTextActivityScrollableResize" android:label="Big ET Scroll Resize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.EditTextActivityDialog" android:label="ET Dialog">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ManyEditTextActivityScrollPanScan" android:label="ManyEditTextActivityScrollPanScan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.ManyEditTextActivityScrollResize" android:label="ManyEditTextActivityScrollResize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BottomEditTextActivityPanScan" android:label="BottomEditTextActivityPanScan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.BottomEditTextActivityResize" android:label="BottomEditTextActivityResize">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.OneEditTextActivitySelected" android:label="OneEditTextActivitySelected">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.OneEditTextActivityNotSelected" android:label="OneEditTextActivityNotSelected">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.AutoCompleteTextViewActivityPortrait" android:label="AutoCompleteTextViewActivityPortrait" android:screenOrientation="portrait">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.AutoCompleteTextViewActivityLandscape" android:label="AutoCompleteTextViewActivityLandscape" android:screenOrientation="landscape">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name=".samples.DialogActivity" android:label="DialogActivity" android:screenOrientation="portrait">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.IMF_TEST" />
- </intent-filter>
- </activity>
-
- </application>
-
-</manifest>
diff --git a/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml b/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml
deleted file mode 100644
index 1a2b7eb..0000000
--- a/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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:padding="20dip"
- android:orientation="vertical">
-
- <View
- android:id="@+id/blank"
- android:layout_height="0dip"
- android:layout_width="match_parent"
- android:layout_weight="1"/>
-
- <EditText
- android:id="@+id/dialog_edit_text"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:scrollHorizontally="true"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
-</LinearLayout>
diff --git a/tests/ImfTest/res/layout/full_screen_edit_text.xml b/tests/ImfTest/res/layout/full_screen_edit_text.xml
deleted file mode 100644
index e760ac1..0000000
--- a/tests/ImfTest/res/layout/full_screen_edit_text.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/samples/SampleCode/res/layout/baseline_1.xml
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<EditText xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/data"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:minLines="15"
- android:gravity="top"/>
-
diff --git a/tests/ImfTest/res/layout/one_edit_text_activity.xml b/tests/ImfTest/res/layout/one_edit_text_activity.xml
deleted file mode 100644
index 0558228..0000000
--- a/tests/ImfTest/res/layout/one_edit_text_activity.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/samples/SampleCode/res/layout/baseline_1.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:baselineAligned="false">
-
- <View android:id="@+id/blank"
- android:layout_height="0dip"
- android:layout_width="match_parent"
- android:layout_weight="1"
- />
-
- <EditText android:id="@+id/dialog_edit_text"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:scrollHorizontally="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- />
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="@android:drawable/divider_horizontal_dark"
- />
-</LinearLayout>
diff --git a/tests/ImfTest/res/layout/sample_edit_text.xml b/tests/ImfTest/res/layout/sample_edit_text.xml
deleted file mode 100644
index 3ff6767..0000000
--- a/tests/ImfTest/res/layout/sample_edit_text.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/samples/SampleCode/res/layout/baseline_1.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
- android:baselineAligned="false"
- android:gravity="center_vertical"
- >
-
- <TextView android:id="@+id/label"
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:gravity="right|center_vertical"
- />
-
- <EditText android:id="@+id/data"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dip"
- />
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="@android:drawable/divider_horizontal_dark"
- />
-</LinearLayout>
diff --git a/tests/ImfTest/res/values/config.xml b/tests/ImfTest/res/values/config.xml
deleted file mode 100644
index 5ae40a3..0000000
--- a/tests/ImfTest/res/values/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources>
- <bool name="def_expect_ime_autopop">false</bool>
-</resources>
diff --git a/tests/ImfTest/res/values/strings.xml b/tests/ImfTest/res/values/strings.xml
deleted file mode 100644
index fc87480..0000000
--- a/tests/ImfTest/res/values/strings.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Strings for sample activities -->
- <string name="normal_edit_text_label">Normal</string>
- <string name="uri_edit_text_label">Uri</string>
- <string name="email_address_edit_text_label">Email Address</string>
- <string name="email_subject_edit_text_label">Email Subject</string>
- <string name="email_content_edit_text_label">Email Content</string>
- <string name="person_name_edit_text_label">Person Name</string>
- <string name="postal_address_edit_text_label">Postal Address</string>
- <string name="password_edit_text_label">Password</string>
- <string name="search_string_edit_text_label">Search String</string>
- <string name="web_edit_text_label">Web Edit Text</string>
- <string name="signed_number_edit_text_label">Signed Number</string>
- <string name="decimal_number_edit_text_label">Decimal Number</string>
- <string name="phone_number_edit_text_label">Phone Number</string>
- <string name="normal_datetime_edit_text_label">Datetime</string>
- <string name="date_edit_text_label">Date</string>
- <string name="time_edit_text_label">Time</string>
- <string name="cap_chars_edit_text_label">Cap Chars</string>
- <string name="cap_words_edit_text_label">Cap Words</string>
- <string name="multiline_edit_text_label">Multiline</string>
- <string name="search_edit_text_label">Search (flag)</string>
- <string name="cap_sentences_edit_text_label">Cap Sentences</string>
- <string name="auto_complete_edit_text_label">Auto Complete</string>
- <string name="auto_correct_edit_text_label">Auto Correct</string>
- <string name="test_dialog">Test Dialog</string>
- <string name="open_dialog_scrollable">open scrollable dialog</string>
- <string name="open_dialog_nonscrollable">open nonscrollable dialog</string>
-
-
-</resources>
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
deleted file mode 100644
index 6115fd5..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.MediaStore;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.AutoCompleteTextView;
-import android.widget.ArrayAdapter;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-
-import com.android.internal.R;
-
-/*
- * Activity with AutoCompleteTextView forced to landscape mode
- */
-public class AutoCompleteTextViewActivityLandscape extends Activity
-{
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.auto_complete_list);
-
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_dropdown_item_1line, COUNTRIES);
- AutoCompleteTextView textView = findViewById(R.id.edit);
- textView.setAdapter(adapter);
- }
-
- static final String[] COUNTRIES = new String[] {
- "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
- "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
- "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
- "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
- "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
- "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
- "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
- "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
- "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
- "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
- "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
- "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
- "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
- "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
- "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
- "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
- "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
- "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
- "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
- "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
- "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
- "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
- "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
- "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
- "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
- "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
- "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
- "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
- "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
- "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
- "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
- "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
- "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
- "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
- "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
- "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
- "Ukraine", "United Arab Emirates", "United Kingdom",
- "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
- "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
- "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
- };
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
deleted file mode 100644
index 253c50f..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.AutoCompleteTextView;
-import android.widget.ArrayAdapter;
-
-import com.android.internal.R;
-
-/*
- * Activity with AutoCompleteTextView (Candidate bar should not appear)
- */
-public class AutoCompleteTextViewActivityPortrait extends Activity
-{
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.auto_complete_list);
-
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_dropdown_item_1line, COUNTRIES);
- AutoCompleteTextView textView = findViewById(R.id.edit);
- textView.setAdapter(adapter);
- }
-
- static final String[] COUNTRIES = new String[] {
- "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
- "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
- "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
- "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
- "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
- "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
- "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
- "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
- "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
- "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
- "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
- "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
- "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
- "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
- "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
- "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
- "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
- "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
- "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
- "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
- "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
- "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
- "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
- "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
- "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
- "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
- "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
- "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
- "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
- "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
- "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
- "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
- "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
- "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
- "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
- "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
- "Ukraine", "United Arab Emirates", "United Kingdom",
- "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
- "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
- "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
- };
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java
deleted file mode 100644
index 033082f..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-
-public class BigEditTextActivityNonScrollablePanScan extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false);
-
- ((LinearLayout) mRootView).addView(view);
-
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java
deleted file mode 100644
index 8a16dea..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-
-public class BigEditTextActivityNonScrollableResize extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false);
-
- ((LinearLayout) mRootView).addView(view);
-
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java
deleted file mode 100644
index b4fdc4c..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-
-public class BigEditTextActivityScrollablePanScan extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
- private LinearLayout mLayout;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
-
- mRootView = new ScrollView(this);
- ((ScrollView) mRootView).setFillViewport(true);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((ScrollView) mRootView), false);
-
- mLayout.addView(view);
-
- ((ScrollView) mRootView).addView(mLayout);
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java
deleted file mode 100644
index 757b6b5..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-
-public class BigEditTextActivityScrollableResize extends Activity {
-
- private View mRootView;
- private View mDefaultFocusedView;
- private LinearLayout mLayout;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
-
- mRootView = new ScrollView(this);
- ((ScrollView) mRootView).setFillViewport(true);
- mRootView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- View view = getLayoutInflater().inflate(
- R.layout.full_screen_edit_text, ((ScrollView) mRootView), false);
-
- mLayout.addView(view);
-
- ((ScrollView) mRootView).addView(mLayout);
- mDefaultFocusedView = view.findViewById(R.id.data);
-
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java
deleted file mode 100644
index 91a329d..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.android.imftest.R;
-
-/*
- * Activity with EditText at the bottom (Pan&Scan)
- */
-public class BottomEditTextActivityPanScan extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
-
- View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false);
- mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text);
- ((LinearLayout) mRootView).addView(view);
-
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java
deleted file mode 100644
index c4c41bc..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.android.imftest.R;
-
-/*
- * Activity with EditText at the bottom (Resize)
- */
-public class BottomEditTextActivityResize extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
-
- View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false);
- mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text);
- ((LinearLayout) mRootView).addView(view);
-
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
deleted file mode 100644
index dbaedf9..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-
-public class ButtonActivity extends Activity
-{
- static boolean mKeyboardIsActive = false;
- public static final int BUTTON_ID = 0;
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- final ButtonActivity instance = this;
-
- final Button myButton = new Button(this);
- myButton.setClickable(true);
- myButton.setText("Keyboard UP!");
- myButton.setId(BUTTON_ID);
- myButton.setFocusableInTouchMode(true);
- myButton.setOnClickListener(new View.OnClickListener()
- {
- public void onClick (View v)
- {
- InputMethodManager imm = InputMethodManager.getInstance();
- if (mKeyboardIsActive)
- {
- imm.hideSoftInputFromInputMethod(v.getWindowToken(), 0);
- myButton.setText("Keyboard UP!");
-
- }
- else
- {
- myButton.requestFocusFromTouch();
- imm.showSoftInput(v, 0);
- myButton.setText("Keyboard DOWN!");
- }
-
- mKeyboardIsActive = !mKeyboardIsActive;
- }
- });
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.addView(myButton);
- setContentView(layout);
- mRootView = layout;
- }
-
- public View getRootView() {
- return mRootView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java b/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java
deleted file mode 100644
index 3ed0386..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.Button;
-import android.view.LayoutInflater;
-import android.app.Dialog;
-
-public class DialogActivity extends Activity {
-
- private static final int DIALOG_WITHOUT_EDITTEXT = 0;
- private static final int DIALOG_WITH_EDITTEXT = 1;
-
- private LinearLayout mLayout;
- private LayoutInflater mInflater;
- private Button mButton1;
- private Button mButton2;
- private EditText mEditText;
-
-
- @Override
- protected void onCreate(Bundle icicle)
- {
- super.onCreate(icicle);
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mButton1 = new Button(this);
- mButton1.setText("Dialog WITHOUT EditText");//(R.string.open_dialog_scrollable);
- mButton1.setOnClickListener(new View.OnClickListener()
- {
- public void onClick(View v)
- {
- showDialog(DIALOG_WITHOUT_EDITTEXT);
- }
- });
-
- mButton2 = new Button(this);
- mButton2.setText("Dialog WITH EditText");//(R.string.open_dialog_nonscrollable);
- mButton2.setOnClickListener(new View.OnClickListener()
- {
- public void onClick(View v)
- {
- showDialog(DIALOG_WITH_EDITTEXT);
- }
- });
-
- mEditText = new EditText(this);
- mLayout.addView(mEditText);
- mLayout.addView(mButton1);
- mLayout.addView(mButton2);
-
- setContentView(mLayout);
- }
-
- @Override
- protected Dialog onCreateDialog(int id)
- {
- switch (id)
- {
- case DIALOG_WITHOUT_EDITTEXT:
- return createDialog(false);
- case DIALOG_WITH_EDITTEXT:
- return createDialog(true);
- }
-
- return super.onCreateDialog(id);
- }
-
- protected Dialog createDialog(boolean bEditText)
- {
- LinearLayout layout;
- layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- if(bEditText)
- {
- EditText editText;
- editText = new EditText(this);
- layout.addView(editText);
- }
-
- Dialog d = new Dialog(this);
- d.setTitle("The DIALOG!!!");
- d.setCancelable(true);
- d.setContentView(layout);
- return d;
- }
-
- }
diff --git a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java
deleted file mode 100644
index 2591b7c..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-
-public class EditTextActivityDialog extends Activity {
-
- private static final int SCROLLABLE_DIALOG_ID = 0;
- private static final int NONSCROLLABLE_DIALOG_ID = 1;
-
- private LinearLayout mLayout;
- private ScrollView mScrollView;
- private LayoutInflater mInflater;
- private Button mButton1;
- private Button mButton2;
-
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mButton1 = new Button(this);
- mButton1.setText(R.string.open_dialog_scrollable);
- mButton1.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- showDialog(SCROLLABLE_DIALOG_ID);
- }
- });
-
- mButton2 = new Button(this);
- mButton2.setText(R.string.open_dialog_nonscrollable);
- mButton2.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- showDialog(NONSCROLLABLE_DIALOG_ID);
- }
- });
-
- mLayout.addView(mButton1);
- mLayout.addView(mButton2);
-
- setContentView(mLayout);
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case SCROLLABLE_DIALOG_ID:
- return createDialog(true);
- case NONSCROLLABLE_DIALOG_ID:
- return createDialog(false);
- }
-
- return super.onCreateDialog(id);
- }
-
- protected Dialog createDialog(boolean scrollable) {
- View layout;
- EditText editText;
-
- if (scrollable) {
- layout = new ScrollView(EditTextActivityDialog.this);
- ((ScrollView) layout).setMinimumHeight(mLayout.getHeight());
-
- ((ScrollView) layout).addView((
- LinearLayout) View.inflate(EditTextActivityDialog.this,
- R.layout.dialog_edit_text_no_scroll, null));
- } else {
- layout = View.inflate(EditTextActivityDialog.this,
- R.layout.dialog_edit_text_no_scroll, null);
- }
-
- Dialog d = new Dialog(EditTextActivityDialog.this);
- d.setTitle(getString(R.string.test_dialog));
- d.setCancelable(true);
- d.setContentView(layout);
- return d;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java b/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java
deleted file mode 100644
index 299e6bb..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.app.Activity;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-
-public class InputTypeActivity extends Activity {
-
- private LinearLayout mLayout;
- private ScrollView mScrollView;
- private LayoutInflater mInflater;
- private ViewGroup mParent;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mScrollView = new ScrollView(this);
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- mInflater = getLayoutInflater();
- mParent = mLayout;
-
- /* Normal Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL,
- R.string.normal_edit_text_label));
-
- /* Normal Edit Text w/Cap Chars Flag*/
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS,
- R.string.cap_chars_edit_text_label));
-
- /* Normal Edit Text w/Cap Words Flag*/
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS,
- R.string.cap_words_edit_text_label));
-
- /* Normal Edit Text w/Cap Multiline Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE,
- R.string.multiline_edit_text_label));
-
- /* Normal Edit Text w/Cap Sentences Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES,
- R.string.cap_sentences_edit_text_label));
-
- /* Normal Edit Text w/Auto-complete Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE,
- R.string.auto_complete_edit_text_label));
-
- /* Normal Edit Text w/Auto-correct Flag */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT,
- R.string.auto_correct_edit_text_label));
-
- /* Uri Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_URI,
- R.string.uri_edit_text_label));
-
- /* Email Address Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS,
- R.string.email_address_edit_text_label));
-
- /* Email Subject Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT,
- R.string.email_subject_edit_text_label));
-
- /* Email Content Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE,
- R.string.email_content_edit_text_label));
-
- /* Person Name Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME,
- R.string.person_name_edit_text_label));
-
- /* Postal Address Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS,
- R.string.postal_address_edit_text_label));
-
- /* Password Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PASSWORD,
- R.string.password_edit_text_label));
-
- /* Web Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT,
- R.string.web_edit_text_label));
-
- /* Signed Number Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_NUMBER|EditorInfo.TYPE_NUMBER_FLAG_SIGNED,
- R.string.signed_number_edit_text_label));
-
- /* Decimal Number Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_NUMBER|EditorInfo.TYPE_NUMBER_FLAG_DECIMAL,
- R.string.decimal_number_edit_text_label));
-
- /* Phone Number Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_PHONE,
- R.string.phone_number_edit_text_label));
-
- /* Normal Datetime Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_NORMAL,
- R.string.normal_datetime_edit_text_label));
-
- /* Date Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_DATE,
- R.string.date_edit_text_label));
-
- /* Time Edit Text */
- mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_TIME,
- R.string.time_edit_text_label));
-
- mScrollView.addView(mLayout);
- setContentView(mScrollView);
- }
-
- private View buildEntryView(int inputType, int label) {
-
-
- View view = mInflater.inflate(R.layout.sample_edit_text, mParent, false);
-
- EditText editText = (EditText) view.findViewById(R.id.data);
- editText.setInputType(inputType);
-
- TextView textView = (TextView) view.findViewById(R.id.label);
- textView.setText(label);
-
- return view;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java
deleted file mode 100644
index 646e480..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Full screen of EditTexts (Non-Scrollable, Pan&Scan)
- */
-public class ManyEditTextActivityNoScrollPanScan extends Activity
-{
- public static final int NUM_EDIT_TEXTS = 9;
-
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- mRootView = new LinearLayout(this);
- ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL);
-
- for (int i=0; i<NUM_EDIT_TEXTS; i++)
- {
- final EditText editText = new EditText(this);
- editText.setText(String.valueOf(i));
- editText.setId(i);
- ((LinearLayout) mRootView).addView(editText);
- }
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java
deleted file mode 100644
index 0387e1e..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Full screen of EditTexts (Scrollable, Pan&Scan)
- */
-public class ManyEditTextActivityScrollPanScan extends Activity
-{
- public static final int NUM_EDIT_TEXTS = 12;
-
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- mRootView = new ScrollView(this);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- for (int i=0; i<NUM_EDIT_TEXTS; i++)
- {
- final EditText editText = new EditText(this);
- editText.setText(String.valueOf(i));
- editText.setId(i);
- layout.addView(editText);
- }
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java
deleted file mode 100644
index 7793b55..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.widget.EditText;
-import android.widget.ScrollView;
-
-/*
- * Full screen of EditTexts (Scrollable, Resize)
- */
-public class ManyEditTextActivityScrollResize extends Activity
-{
- public static final int NUM_EDIT_TEXTS = 12;
-
- private View mRootView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- mRootView = new ScrollView(this);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- for (int i=0; i<NUM_EDIT_TEXTS; i++)
- {
- final EditText editText = new EditText(this);
- editText.setText(String.valueOf(i));
- editText.setId(i);
- layout.addView(editText);
- }
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- public View getRootView() {
- return mRootView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java
deleted file mode 100644
index c4be21c..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Debug;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Activity with non-EditText view selected initially
- */
-public class OneEditTextActivityNotSelected extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- mRootView = new ScrollView(this);
-
- EditText editText = new EditText(this);
- Button button = new Button(this);
- button.setText("The focus is here.");
- button.setFocusableInTouchMode(true);
- button.requestFocus();
- mDefaultFocusedView = button;
- layout.addView(button);
- layout.addView(editText);
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java
deleted file mode 100644
index 64882aa..0000000
--- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.ScrollView;
-
-import com.android.internal.R;
-
-/*
- * Activity with EditText selected initially
- */
-public class OneEditTextActivitySelected extends Activity
-{
- private View mRootView;
- private View mDefaultFocusedView;
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- mRootView = new ScrollView(this);
-
- EditText editText = new EditText(this);
- editText.requestFocus();
- mDefaultFocusedView = editText;
- layout.addView(editText);
-
- ((ScrollView) mRootView).addView(layout);
- setContentView(mRootView);
-
- // set to resize so IME is always shown (and also so
- // ImfBaseTestCase#destructiveCheckImeInitialState thinks it should always be shown
- this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- }
-
- public View getRootView() {
- return mRootView;
- }
-
- public View getDefaultFocusedView() {
- return mDefaultFocusedView;
- }
-}
diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk
deleted file mode 100644
index 14186d7..0000000
--- a/tests/ImfTest/tests/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-LOCAL_PACKAGE_NAME := ImfTestTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_INSTRUMENTATION_FOR := ImfTest
-
-include $(BUILD_PACKAGE)
diff --git a/tests/ImfTest/tests/AndroidManifest.xml b/tests/ImfTest/tests/AndroidManifest.xml
deleted file mode 100644
index c02fa0b..0000000
--- a/tests/ImfTest/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.imftest.tests">
-
- <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <!--
- This declares that this app uses the instrumentation test runner targeting
- the package of com.android.imftest. To run the tests use the command:
- "adb shell am instrument -w com.android.imftest.tests/android.test.InstrumentationTestRunner"
- -->
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.imftest"
- android:label="imf tests"/>
-
-</manifest>
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java
deleted file mode 100644
index 2db11c5..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityNonScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityNonScrollablePanScan> {
-
- public final String TAG = "BigEditTextActivityNonScrollablePanScanTests";
-
- public BigEditTextActivityNonScrollablePanScanTests() {
- super(BigEditTextActivityNonScrollablePanScan.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java
deleted file mode 100644
index 1050794..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityNonScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityNonScrollableResize> {
-
- public final String TAG = "BigEditTextActivityNonScrollableResizeTests";
-
- public BigEditTextActivityNonScrollableResizeTests() {
- super(BigEditTextActivityNonScrollableResize.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java
deleted file mode 100644
index 1e848b0..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityScrollablePanScan> {
-
- public final String TAG = "BigEditTextActivityScrollablePanScanTests";
-
- public BigEditTextActivityScrollablePanScanTests() {
- super(BigEditTextActivityScrollablePanScan.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java
deleted file mode 100644
index de607d6..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BigEditTextActivityScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityScrollableResize> {
-
- public final String TAG = "BigEditTextActivityScrollableResizeTests";
-
- public BigEditTextActivityScrollableResizeTests() {
- super(BigEditTextActivityScrollableResize.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BigEditTextActivityScrollableResize) mTargetActivity).getRootView();
- View servedView = ((BigEditTextActivityScrollableResize) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java
deleted file mode 100644
index c521905..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BottomEditTextActivityPanScanTests extends ImfBaseTestCase<BottomEditTextActivityPanScan> {
-
- public final String TAG = "BottomEditTextActivityPanScanTests";
-
- public BottomEditTextActivityPanScanTests() {
- super(BottomEditTextActivityPanScan.class);
- }
-
- @LargeTest
- public void testAppAdjustmentPanScan() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BottomEditTextActivityPanScan) mTargetActivity).getRootView();
- View servedView = ((BottomEditTextActivityPanScan) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java
deleted file mode 100644
index 9a69fd5..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-import com.android.imftest.R;
-
-
-public class BottomEditTextActivityResizeTests extends ImfBaseTestCase<BottomEditTextActivityResize> {
-
- public final String TAG = "BottomEditTextActivityResizeTests";
-
- public BottomEditTextActivityResizeTests() {
- super(BottomEditTextActivityResize.class);
- }
-
- @LargeTest
- public void testAppAdjustmentResize() {
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- View rootView = ((BottomEditTextActivityResize) mTargetActivity).getRootView();
- View servedView = ((BottomEditTextActivityResize) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java
deleted file mode 100644
index f6f97b5..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.KeyEvent;
-import android.widget.Button;
-
-
-public class ButtonActivityTest extends ImfBaseTestCase<ButtonActivity> {
-
- final public String TAG = "ButtonActivityTest";
-
- public ButtonActivityTest() {
- super(ButtonActivity.class);
- }
-
- @LargeTest
- public void testButtonActivatesIme() {
-
- final Button button = (Button) mTargetActivity.findViewById(ButtonActivity.BUTTON_ID);
-
- // Push button
- // Bring the target EditText into focus.
- mTargetActivity.runOnUiThread(new Runnable() {
- public void run() {
- button.requestFocus();
- }
- });
-
- sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-
- // Give it a couple seconds
- pause(2000);
-
- // We should have initialized imm.mServedView and imm.mCurrentTextBoxAttribute
- assertTrue(mImm.isActive());
- // imm.mServedInputConnection should be null since Button doesn't override onCreateInputConnection().
- assertFalse(mImm.isAcceptingText());
-
- destructiveCheckImeInitialState(mTargetActivity.getRootView(), button);
-
- }
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
deleted file mode 100644
index 32f80a3..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.imftest.R;
-
-public abstract class ImfBaseTestCase<T extends Activity> extends InstrumentationTestCase {
-
- /*
- * The amount of time we are willing to wait for the IME to appear after a user action
- * before we give up and fail the test.
- */
- public final long WAIT_FOR_IME = 5000;
-
- /*
- * Unfortunately there is now way for us to know how tall the IME is,
- * so we have to hard code a minimum and maximum value.
- */
- public final int IME_MIN_HEIGHT = 150;
- public final int IME_MAX_HEIGHT = 300;
-
- protected InputMethodManager mImm;
- protected T mTargetActivity;
- protected boolean mExpectAutoPop;
- private Class<T> mTargetActivityClass;
-
- public ImfBaseTestCase(Class<T> activityClass) {
- mTargetActivityClass = activityClass;
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- final String packageName = getInstrumentation().getTargetContext().getPackageName();
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- mTargetActivity = launchActivityWithIntent(packageName, mTargetActivityClass, intent);
- // expect ime to auto pop up if device has no hard keyboard
- int keyboardType = mTargetActivity.getResources().getConfiguration().keyboard;
- mExpectAutoPop = (keyboardType == Configuration.KEYBOARD_NOKEYS ||
- keyboardType == Configuration.KEYBOARD_UNDEFINED);
-
- mImm = InputMethodManager.getInstance();
-
- KeyguardManager keyguardManager =
- (KeyguardManager) getInstrumentation().getContext().getSystemService(
- Context.KEYGUARD_SERVICE);
- keyguardManager.newKeyguardLock("imftest").disableKeyguard();
- }
-
- // Utility test methods
- public void verifyEditTextAdjustment(final View editText, int rootViewHeight) {
-
- int[] origLocation = new int[2];
- int[] newLocation = new int[2];
-
- // Tell the keyboard to go away.
- mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
-
- // Bring the target EditText into focus.
- mTargetActivity.runOnUiThread(new Runnable() {
- public void run() {
- editText.requestFocus();
- }
- });
-
- // Get the original location of the EditText.
- editText.getLocationOnScreen(origLocation);
-
- // Tap the EditText to bring up the IME.
- sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-
- // Wait until the EditText pops above the IME or until we hit the timeout.
- editText.getLocationOnScreen(newLocation);
- long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME;
- while (newLocation[1] > rootViewHeight - IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) {
- editText.getLocationOnScreen(newLocation);
- pause(100);
- }
-
- assertTrue(newLocation[1] <= rootViewHeight - IME_MIN_HEIGHT);
-
- // Tell the keyboard to go away.
- mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
- }
-
- public void destructiveCheckImeInitialState(View rootView, View servedView) {
- int windowSoftInputMode = mTargetActivity.getWindow().getAttributes().softInputMode;
- int adjustMode = windowSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
- if (mExpectAutoPop && adjustMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
- assertTrue(destructiveCheckImeUp(rootView, servedView));
- } else {
- assertFalse(destructiveCheckImeUp(rootView, servedView));
- }
- }
-
- public boolean destructiveCheckImeUp(View rootView, View servedView) {
- int origHeight;
- int newHeight;
-
- origHeight = rootView.getHeight();
-
- // Tell the keyboard to go away.
- mImm.hideSoftInputFromWindow(servedView.getWindowToken(), 0);
-
- // Give it five seconds to adjust
- newHeight = rootView.getHeight();
- long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME;
- while (Math.abs(newHeight - origHeight) < IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) {
- newHeight = rootView.getHeight();
- }
-
- return (Math.abs(origHeight - newHeight) >= IME_MIN_HEIGHT);
- }
-
- void pause(int millis) {
- try {
- Thread.sleep(millis);
- } catch (InterruptedException e) {
- }
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java
deleted file mode 100644
index 278efb1..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.app.Activity;
-import android.widget.EditText;
-
-
-public abstract class ManyEditTextActivityBaseTestCase<T extends Activity> extends ImfBaseTestCase<T> {
-
- public ManyEditTextActivityBaseTestCase(Class<T> activityClass){
- super(activityClass);
- }
-
- public abstract void testAllEditTextsAdjust();
-
- public void verifyAllEditTextAdjustment(int numEditTexts, int rootViewHeight) {
-
- for (int i = 0; i < numEditTexts; i++) {
- final EditText lastEditText = (EditText) mTargetActivity.findViewById(i);
- verifyEditTextAdjustment(lastEditText, rootViewHeight);
- }
-
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java
deleted file mode 100644
index 4f8d14e..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-
-
-public class ManyEditTextActivityNoScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityNoScrollPanScan> {
-
- public final String TAG = "ManyEditTextActivityNoScrollPanScanTests";
-
- public ManyEditTextActivityNoScrollPanScanTests() {
- super(ManyEditTextActivityNoScrollPanScan.class);
- }
-
-
- @LargeTest
- public void testAllEditTextsAdjust() {
- verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS,
- mTargetActivity.getRootView().getMeasuredHeight());
- }
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java
deleted file mode 100644
index 7f98f7f..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-
-
-public class ManyEditTextActivityScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollPanScan> {
-
- public final String TAG = "ManyEditTextActivityScrollPanScanTests";
-
-
- public ManyEditTextActivityScrollPanScanTests() {
- super(ManyEditTextActivityScrollPanScan.class);
- }
-
- @LargeTest
- public void testAllEditTextsAdjust() {
- verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS,
- mTargetActivity.getRootView().getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java
deleted file mode 100644
index 68dae87..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-
-
-public class ManyEditTextActivityScrollResizeTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollResize> {
-
- public final String TAG = "ManyEditTextActivityScrollResizeTests";
-
-
- public ManyEditTextActivityScrollResizeTests() {
- super(ManyEditTextActivityScrollResize.class);
- }
-
- @LargeTest
- public void testAllEditTextsAdjust() {
- verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS,
- mTargetActivity.getRootView().getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java
deleted file mode 100644
index 6147d3c..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.View;
-
-
-public class OneEditTextActivityNotSelectedTests extends ImfBaseTestCase<OneEditTextActivityNotSelected> {
-
- public final String TAG = "OneEditTextActivityNotSelectedTests";
-
- public OneEditTextActivityNotSelectedTests() {
- super(OneEditTextActivityNotSelected.class);
- }
-
- @LargeTest
- public void testSoftKeyboardNoAutoPop() {
-
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- assertFalse(mImm.isAcceptingText());
-
- View rootView = ((OneEditTextActivityNotSelected) mTargetActivity).getRootView();
- View servedView = ((OneEditTextActivityNotSelected) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java
deleted file mode 100644
index 42fcd66..0000000
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.imftest.samples;
-
-import com.android.imftest.R;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.KeyEvent;
-import android.view.View;
-
-
-public class OneEditTextActivitySelectedTests extends ImfBaseTestCase<OneEditTextActivitySelected> {
-
- public final String TAG = "OneEditTextActivitySelectedTests";
-
- public OneEditTextActivitySelectedTests() {
- super(OneEditTextActivitySelected.class);
- }
-
- @LargeTest
- public void testSoftKeyboardAutoPop() {
-
- // Give the IME 2 seconds to appear.
- pause(2000);
-
- assertTrue(mImm.isAcceptingText());
-
- View rootView = ((OneEditTextActivitySelected) mTargetActivity).getRootView();
- View servedView = ((OneEditTextActivitySelected) mTargetActivity).getDefaultFocusedView();
-
- assertNotNull(rootView);
- assertNotNull(servedView);
-
- destructiveCheckImeInitialState(rootView, servedView);
-
- verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight());
- }
-
-}
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
index 579d972..6500428 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -11,33 +11,36 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.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;
+ private Device mDevice;
- public Cujs(ITestDevice device) {
- this.device = device;
+ public Cujs(Device device) {
+ this.mDevice = device;
}
/**
* Runs the critical user journeys.
*/
- public void run() throws DeviceNotAvailableException {
+ public void run() throws TestException {
+ // Do an explicit GC in the system server process as part of the test
+ // case to reduce GC-related sources of noise.
+ // SIGUSR1 = 10 is the magic signal to trigger the GC.
+ int pid = mDevice.getPidForProcess("system_server");
+ mDevice.executeShellCommand("kill -10 " + pid);
+
// 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);
+ mDevice.executeShellCommand(command);
}
}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
new file mode 100644
index 0000000..03503ce
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.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.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+/**
+ * Wrapper around ITestDevice exposing useful device functions.
+ */
+class Device {
+
+ private ITestDevice mDevice;
+
+ Device(ITestDevice device) {
+ mDevice = device;
+ }
+
+ /**
+ * Execute a shell command and return the output as a string.
+ */
+ public String executeShellCommand(String command) throws TestException {
+ try {
+ return mDevice.executeShellCommand(command);
+ } catch (DeviceNotAvailableException e) {
+ throw new TestException(e);
+ }
+ }
+
+ /**
+ * Enable adb root
+ */
+ public void enableAdbRoot() throws TestException {
+ try {
+ mDevice.enableAdbRoot();
+ } catch (DeviceNotAvailableException e) {
+ throw new TestException(e);
+ }
+ }
+
+ /**
+ * Returns the pid for the process with the given name.
+ */
+ public int getPidForProcess(String name) throws TestException {
+ String psout = 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 TestException("unexpected ps output format: " + psout, e);
+ }
+
+ throw new TestException("failed to get pid for process " + name);
+ }
+}
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
index bbec065..48b7f39 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
@@ -11,59 +11,64 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.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;
+/**
+ * Runs a system memory test.
+ */
@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;
+ private ITestDevice mTestDevice;
+ private int mIterations = 0; // Number of times cujs have been run.
+ private Metrics mMetrics;
+ private Cujs mCujs;
@Override
- public void setDevice(ITestDevice device) {
- testDevice = device;
- metrics = new Metrics(device, testMetrics, testLogs);
- cujs = new Cujs(device);
+ public void setDevice(ITestDevice testDevice) {
+ mTestDevice = testDevice;
+ Device device = new Device(testDevice);
+ mMetrics = new Metrics(device, testMetrics, testLogs);
+ mCujs = new Cujs(device);
}
@Override
public ITestDevice getDevice() {
- return testDevice;
+ return mTestDevice;
}
// Invoke a single iteration of running the cujs.
- private void runCujs() throws DeviceNotAvailableException {
- cujs.run();
- iterations++;
+ private void runCujs() throws TestException {
+ mCujs.run();
+ mIterations++;
}
// Sample desired memory.
- private void sample()
- throws DeviceNotAvailableException, IOException, Metrics.MetricsException {
- metrics.sample(String.format("%03d", iterations));
+ private void sample() throws TestException {
+ mMetrics.sample(String.format("%03d", mIterations));
}
+ /**
+ * Runs the memory tests.
+ */
@Test
- public void run() throws Exception {
+ public void run() throws TestException {
sample(); // Sample before running cujs
runCujs();
sample(); // Sample after first iteration of cujs
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
index 616983e..b408a81 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -11,17 +11,16 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.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;
@@ -33,22 +32,9 @@
*/
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);
- }
- }
+ private Device mDevice;
+ private TestMetrics mMetrics;
+ private TestLogData mLogs;
/**
* Constructs a metrics instance that will output high level metrics and
@@ -59,52 +45,26 @@
* @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;
+ Metrics(Device device, TestMetrics metrics, TestLogData logs) {
+ this.mDevice = device;
+ this.mMetrics = metrics;
+ this.mLogs = logs;
}
/**
* Writes the given <code>text</code> to a log with the given label.
*/
- private void logText(String label, String text) throws IOException {
- File file = File.createTempFile(label, "txt");
- PrintStream ps = new PrintStream(file);
- ps.print(text);
- try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
- logs.addTestLog(label, LogDataType.TEXT, dataStream);
- }
- }
-
- /**
- * 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);
+ private void logText(String label, String text) throws TestException {
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;
- }
+ File file = File.createTempFile(label, "txt");
+ PrintStream ps = new PrintStream(file);
+ ps.print(text);
+ try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
+ mLogs.addTestLog(label, LogDataType.TEXT, dataStream);
}
- } catch (InputMismatchException e) {
- throw new MetricsException("unexpected ps output format: " + psout, e);
+ } catch (IOException e) {
+ throw new TestException(e);
}
-
- throw new MetricsException("failed to get pid for process " + name);
}
/**
@@ -115,22 +75,24 @@
*
* @param label prefix to use for metrics and logs output for this sample.
*/
- void sample(String label) throws DeviceNotAvailableException, IOException, MetricsException {
+ void sample(String label) throws TestException {
// adb root access is required to get showmap
- device.enableAdbRoot();
+ mDevice.enableAdbRoot();
- int pid = getPidForProcess("system_server");
+ int pid = mDevice.getPidForProcess("system_server");
// Read showmap for system server and add it as a test log
- String showmap = device.executeShellCommand("showmap " + pid);
+ String showmap = mDevice.executeShellCommand("showmap " + pid);
logText(label + ".system_server.showmap", showmap);
// Extract VSS, PSS and RSS from the showmap and output them as metrics.
// The last lines of the showmap output looks something like:
+ // CHECKSTYLE:OFF Generated code
// 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
+ // CHECKSTYLE:ON Generated code
try {
int pos = showmap.lastIndexOf("----");
Scanner sc = new Scanner(showmap.substring(pos));
@@ -139,16 +101,16 @@
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));
+ mMetrics.addTestMetric(label + ".system_server.vss", Long.toString(vss));
+ mMetrics.addTestMetric(label + ".system_server.rss", Long.toString(rss));
+ mMetrics.addTestMetric(label + ".system_server.pss", Long.toString(pss));
} catch (InputMismatchException e) {
- throw new MetricsException("unexpected showmap format", e);
+ throw new TestException("unexpected showmap format", e);
}
// Run debuggerd -j to get GC stats for system server and add it as a
// test log
- String debuggerd = device.executeShellCommand("debuggerd -j " + pid);
+ String debuggerd = mDevice.executeShellCommand("debuggerd -j " + pid);
logText(label + ".system_server.debuggerd", debuggerd);
// TODO: Experiment with other additional metrics.
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java
new file mode 100644
index 0000000..dc3b68f
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.sysmem.host;
+
+/**
+ * Exception thrown in case of unexpected error encountered when executing the
+ * test.
+ */
+class TestException extends Exception {
+ TestException(Exception cause) {
+ super(cause);
+ }
+
+ TestException(String msg) {
+ super(msg);
+ }
+
+ TestException(String msg, Exception cause) {
+ super(msg, cause);
+ }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 2ecf25b..c02ca21 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -83,6 +83,7 @@
"compile/Pseudolocalizer.cpp",
"compile/XmlIdCollector.cpp",
"configuration/ConfigurationParser.cpp",
+ "dump/DumpManifest.cpp",
"filter/AbiFilter.cpp",
"filter/ConfigFilter.cpp",
"format/Archive.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index a73d56c..a20b9b7 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -69,8 +69,8 @@
return {};
}
- io::ZeroCopyInputAdaptor adaptor(in.get());
- if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
+ io::ProtoInputStreamReader proto_reader(in.get());
+ if (!proto_reader.ReadMessage(&pb_table)) {
diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
return {};
}
@@ -97,8 +97,8 @@
}
pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
- if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+ io::ProtoInputStreamReader proto_reader(manifest_in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
return {};
}
@@ -255,7 +255,7 @@
}
std::unique_ptr<xml::XmlResource> LoadedApk::LoadXml(const std::string& file_path,
- IDiagnostics* diag) {
+ IDiagnostics* diag) const {
io::IFile* file = apk_->FindFile(file_path);
if (file == nullptr) {
diag->Error(DiagMessage() << "failed to find file");
@@ -270,9 +270,9 @@
return nullptr;
}
- io::ZeroCopyInputAdaptor adaptor(in.get());
pb::XmlNode pb_node;
- if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+ io::ProtoInputStreamReader proto_reader(in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
diag->Error(DiagMessage() << "failed to parse file as proto XML");
return nullptr;
}
@@ -317,8 +317,8 @@
std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
if (manifest_in != nullptr) {
pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
- if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+ io::ProtoInputStreamReader proto_reader(manifest_in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
return ApkFormat::kProto;
}
}
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index dcb085a..84c57c1 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -111,7 +111,7 @@
IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
/** Loads the file as an xml document. */
- std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path, IDiagnostics* diag);
+ std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path, IDiagnostics* diag) const;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedApk);
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 0778564..9a7238b 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -303,7 +303,7 @@
}
}
-TEST(StringPoolTest, FlattenModifiedUTF8) {
+TEST(StringPoolTest, ModifiedUTF8) {
using namespace android; // For NO_ERROR on Windows.
StdErrDiagnostics diag;
StringPool pool;
@@ -315,12 +315,24 @@
StringPool::FlattenUtf8(&buffer, pool, &diag);
std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
- // Check that the 4 byte utf-8 codepoint is encoded using 2 3 byte surrogate pairs
+ // Check that the codepoints are encoded using two three-byte surrogate pairs
ResStringPool test;
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- EXPECT_THAT(util::GetString(test, 0), Eq("\xED\xA0\x81\xED\xB0\x80"));
- EXPECT_THAT(util::GetString(test, 1), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
- EXPECT_THAT(util::GetString(test, 2), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+ size_t len;
+ const char* str = test.string8At(0, &len);
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80"));
+ str = test.string8At(1, &len);
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(std::string(str, len), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
+ str = test.string8At(2, &len);
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+
+ // Check that retrieving the strings returns the original UTF-8 character bytes
+ EXPECT_THAT(util::GetString(test, 0), Eq("\xF0\x90\x90\x80"));
+ EXPECT_THAT(util::GetString(test, 1), Eq("foo \xF0\x90\x90\xB7 bar"));
+ EXPECT_THAT(util::GetString(test, 2), Eq("\xF0\x90\x90\x80\xF0\x90\x90\xB7"));
}
TEST(StringPoolTest, MaxEncodingLength) {
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 86b1f4c..954f1ed 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -170,8 +170,8 @@
}
pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor adaptor(in.get());
- if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+ io::ProtoInputStreamReader proto_reader(in.get());
+ if (!proto_reader.ReadMessage(&pb_node)) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
<< "failed to parse proto XML " << *file->path);
return false;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 5cb30b6..91e3977 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -223,6 +223,12 @@
return 1;
}
+ ResourceTable* table = loaded_apk->GetResourceTable();
+ if (!table) {
+ diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+ return 1;
+ }
+
io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
Printer printer(&fout);
@@ -233,7 +239,7 @@
// Insert the configurations into a set in order to keep every configuarion seen
std::set<ConfigDescription, decltype(compare)> configs(compare);
- for (auto& package : loaded_apk->GetResourceTable()->packages) {
+ for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
for (auto& value : entry->values) {
@@ -267,10 +273,15 @@
return 1;
}
+ ResourceTable* table = loaded_apk->GetResourceTable();
+ if (!table) {
+ diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+ return 1;
+ }
+
// Load the run-time xml string pool using the flattened data
BigBuffer buffer(4096);
- StringPool::FlattenUtf8(&buffer, loaded_apk->GetResourceTable()->string_pool,
- context.GetDiagnostics());
+ StringPool::FlattenUtf8(&buffer, table->string_pool, context.GetDiagnostics());
auto data = buffer.to_string();
android::ResStringPool pool(data.data(), data.size(), false);
Debug::DumpResStringPool(&pool, &printer);
@@ -304,7 +315,13 @@
printer.Println("Binary APK");
}
- Debug::PrintTable(*loaded_apk->GetResourceTable(), print_options, &printer);
+ ResourceTable* table = loaded_apk->GetResourceTable();
+ if (!table) {
+ diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+ return 1;
+ }
+
+ Debug::PrintTable(*table, print_options, &printer);
}
return 0;
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 0724d62..9ec820d 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -19,6 +19,7 @@
#include "Command.h"
#include "Debug.h"
+#include "dump/DumpManifest.h"
namespace aapt {
@@ -133,8 +134,10 @@
public:
explicit DumpCommand(IDiagnostics* diag) : Command("dump", "d"), diag_(diag) {
AddOptionalSubcommand(util::make_unique<DumpAPCCommand>(diag_));
+ AddOptionalSubcommand(util::make_unique<DumpBadgingCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpConfigsCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpPackageNameCommand>(diag_));
+ AddOptionalSubcommand(util::make_unique<DumpPermissionsCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpTableCommand>(diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(diag_));
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 119f56a..13c1047 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1842,9 +1842,15 @@
} else {
// Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
// equal to the minSdk.
+ const size_t origConstraintSize = options_.split_constraints.size();
options_.split_constraints =
AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
+ if (origConstraintSize != options_.split_constraints.size()) {
+ context_->GetDiagnostics()->Warn(DiagMessage()
+ << "requested to split resources prior to min sdk of "
+ << context_->GetMinSdkVersion());
+ }
TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options);
if (!table_splitter.VerifySplitConstraints(context_)) {
return 1;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index c6c82b0..5862d31 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -73,6 +73,7 @@
}
*out_path = parts[0];
+ out_split->name = parts[1];
for (const StringPiece& config_str : util::Tokenize(parts[1], ',')) {
ConfigDescription config;
if (!ConfigDescription::Parse(config_str, &config)) {
@@ -119,12 +120,15 @@
for (const SplitConstraints& constraints : split_constraints) {
SplitConstraints constraint;
for (const ConfigDescription& config : constraints.configs) {
- if (config.sdkVersion <= min_sdk) {
- constraint.configs.insert(config.CopyWithoutSdkVersion());
- } else {
- constraint.configs.insert(config);
+ const ConfigDescription &configToInsert = (config.sdkVersion <= min_sdk)
+ ? config.CopyWithoutSdkVersion()
+ : config;
+ // only add the config if it actually selects something
+ if (configToInsert != ConfigDescription::DefaultConfig()) {
+ constraint.configs.insert(configToInsert);
}
}
+ constraint.name = constraints.name;
adjusted_constraints.push_back(std::move(constraint));
}
return adjusted_constraints;
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index b9fb5b2..158ef29 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -22,6 +22,10 @@
#include "test/Test.h"
namespace aapt {
+#define EXPECT_CONFIG_EQ(constraints, config) \
+ EXPECT_EQ(constraints.configs.size(), 1); \
+ EXPECT_EQ(*constraints.configs.begin(), config); \
+ constraints.configs.clear();
TEST(UtilTest, SplitNamesAreSanitized) {
AppInfo app_info{"com.pkg"};
@@ -84,4 +88,287 @@
EXPECT_EQ(compiled_version_code_major->value.data, 0x61);
}
+
+TEST (UtilTest, ParseSplitParameter) {
+ IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics();
+ std::string path;
+ SplitConstraints constraints;
+ ConfigDescription expected_configuration;
+
+ // ========== Test IMSI ==========
+ // mcc: 'mcc[0-9]{3}'
+ // mnc: 'mnc[0-9]{1,3}'
+ ASSERT_TRUE(ParseSplitParameter(":mcc310",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setMcc(0x0136)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc004",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setMcc(0x0136)
+ .setMnc(0x0004)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc000",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setMcc(0x0136)
+ .setMnc(0xFFFF)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test LOCALE ==========
+ // locale: '[a-z]{2,3}(-r[a-z]{2})?'
+ // locale: 'b+[a-z]{2,3}(+[a-z[0-9]]{2})?'
+ ASSERT_TRUE(ParseSplitParameter(":es",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setLanguage(0x6573)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":fr-rCA",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setLanguage(0x6672)
+ .setCountry(0x4341)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":b+es+419",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setLanguage(0x6573)
+ .setCountry(0xA424)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_TYPE ==========
+ // orientation: '(port|land|square)'
+ // touchscreen: '(notouch|stylus|finger)'
+ // density" '(anydpi|nodpi|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|[0-9]*dpi)'
+ ASSERT_TRUE(ParseSplitParameter(":square",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setOrientation(0x03)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":stylus",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setTouchscreen(0x02)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":xxxhdpi",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setDensity(0x0280)
+ .setSdkVersion(0x0004) // version [any density requires donut]
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":land-xhdpi-finger",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setOrientation(0x02)
+ .setTouchscreen(0x03)
+ .setDensity(0x0140)
+ .setSdkVersion(0x0004) // version [any density requires donut]
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test INPUT ==========
+ // keyboard: '(nokeys|qwerty|12key)'
+ // navigation: '(nonav|dpad|trackball|wheel)'
+ // inputFlags: '(keysexposed|keyshidden|keyssoft)'
+ // inputFlags: '(navexposed|navhidden)'
+ ASSERT_TRUE(ParseSplitParameter(":qwerty",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setKeyboard(0x02)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":dpad",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setNavigation(0x02)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":keyssoft-navhidden",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setInputFlags(0x0B)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":keyshidden-nokeys-navexposed-trackball",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setKeyboard(0x01)
+ .setNavigation(0x03)
+ .setInputFlags(0x06)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_SIZE ==========
+ // screenWidth/screenHeight: '[0-9]+x[0-9]+'
+ ASSERT_TRUE(ParseSplitParameter(":1920x1080",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenWidth(0x0780)
+ .setScreenHeight(0x0438)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test VERSION ==========
+ // version 'v[0-9]+'
+
+ // ========== Test SCREEN_CONFIG ==========
+ // screenLayout [direction]: '(ldltr|ldrtl)'
+ // screenLayout [size]: '(small|normal|large|xlarge)'
+ // screenLayout [long]: '(long|notlong)'
+ // uiMode [type]: '(desk|car|television|appliance|watch|vrheadset)'
+ // uiMode [night]: '(night|notnight)'
+ // smallestScreenWidthDp: 'sw[0-9]dp'
+ ASSERT_TRUE(ParseSplitParameter(":ldrtl",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x80)
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":small",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x01)
+ .setSdkVersion(0x0004) // screenLayout (size) requires donut
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":notlong",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x10)
+ .setSdkVersion(0x0004) // screenLayout (long) requires donut
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":ldltr-normal-long",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout(0x62)
+ .setSdkVersion(0x0004) // screenLayout (size|long) requires donut
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":car",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setUiMode(0x03)
+ .setSdkVersion(0x0008) // uiMode requires froyo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":vrheadset",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setUiMode(0x07)
+ .setSdkVersion(0x001A) // uiMode 'vrheadset' requires oreo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":television-night",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setUiMode(0x24)
+ .setSdkVersion(0x0008) // uiMode requires froyo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":sw1920dp",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setSmallestScreenWidthDp(0x0780)
+ .setSdkVersion(0x000D) // smallestScreenWidthDp requires honeycomb mr2
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_SIZE_DP ==========
+ // screenWidthDp: 'w[0-9]dp'
+ // screenHeightDp: 'h[0-9]dp'
+ ASSERT_TRUE(ParseSplitParameter(":w1920dp",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenWidthDp(0x0780)
+ .setSdkVersion(0x000D) // screenWidthDp requires honeycomb mr2
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":h1080dp",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenHeightDp(0x0438)
+ .setSdkVersion(0x000D) // screenHeightDp requires honeycomb mr2
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ // ========== Test SCREEN_CONFIG_2 ==========
+ // screenLayout2: '(round|notround)'
+ // colorMode: '(widecg|nowidecg)'
+ // colorMode: '(highhdr|lowdr)'
+ ASSERT_TRUE(ParseSplitParameter(":round",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setScreenLayout2(0x02)
+ .setSdkVersion(0x0017) // screenLayout2 (round) requires marshmallow
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+ ASSERT_TRUE(ParseSplitParameter(":widecg-highdr",
+ diagnostics, &path, &constraints));
+ expected_configuration = test::ConfigDescriptionBuilder()
+ .setColorMode(0x0A)
+ .setSdkVersion(0x001A) // colorMode (hdr|colour gamut) requires oreo
+ .Build();
+ EXPECT_CONFIG_EQ(constraints, expected_configuration);
+}
+
+TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ IDiagnostics* diagnostics = context.get()->GetDiagnostics();
+ std::vector<SplitConstraints> test_constraints;
+ std::string path;
+
+ test_constraints.push_back({});
+ ASSERT_TRUE(ParseSplitParameter(":v7",
+ diagnostics, &path, &test_constraints.back()));
+ test_constraints.push_back({});
+ ASSERT_TRUE(ParseSplitParameter(":xhdpi",
+ diagnostics, &path, &test_constraints.back()));
+ EXPECT_EQ(test_constraints.size(), 2);
+ EXPECT_EQ(test_constraints[0].name, "v7");
+ EXPECT_EQ(test_constraints[0].configs.size(), 1);
+ EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
+ EXPECT_EQ(test_constraints[1].name, "xhdpi");
+ EXPECT_EQ(test_constraints[1].configs.size(), 1);
+ EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
+
+ auto adjusted_contraints = AdjustSplitConstraintsForMinSdk(26, test_constraints);
+ EXPECT_EQ(adjusted_contraints.size(), 2);
+ EXPECT_EQ(adjusted_contraints[0].name, "v7");
+ EXPECT_EQ(adjusted_contraints[0].configs.size(), 0);
+ EXPECT_EQ(adjusted_contraints[1].name, "xhdpi");
+ EXPECT_EQ(adjusted_contraints[1].configs.size(), 1);
+ EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/development.md b/tools/aapt2/development.md
new file mode 100644
index 0000000..8ee873a
--- /dev/null
+++ b/tools/aapt2/development.md
@@ -0,0 +1,11 @@
+# AAPT2 development
+
+## Building
+All build targets can be found in `Android.bp` file. The main ones are `make -j aapt2` and `make -j aapt2_tests`
+
+`make -j aapt2` will create an aapt2 executable in `out/host/linux-x86/bin/aapt2` (on Linux). This `aapt2` executable will then be used for all the apps in the platform.
+
+Static version of the tool (without shared libraries) can be built with `make -j static_sdk_tools dist DIST_DIR=$OUTPUT_DIRECTORY BUILD_HOST_static=1`. Note, in addition to aapt2 this command will also output other statically built tools to the `$OUTPUT_DIRECTORY`.
+
+## Running tests
+Build `make -j aapt2_tests` and then (on Linux) execute `out/host/linux-x86/nativetest64/aapt2_tests/aapt2_tests`
\ No newline at end of file
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
new file mode 100644
index 0000000..2c356d1
--- /dev/null
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -0,0 +1,2197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "DumpManifest.h"
+
+#include "LoadedApk.h"
+#include "SdkConstants.h"
+#include "ValueVisitor.h"
+#include "io/File.h"
+#include "io/FileStream.h"
+#include "process/IResourceTableConsumer.h"
+#include "xml/XmlDom.h"
+
+using ::android::base::StringPrintf;
+
+namespace aapt {
+
+/**
+ * These are attribute resource constants for the platform, as found in android.R.attr.
+ */
+enum {
+ LABEL_ATTR = 0x01010001,
+ ICON_ATTR = 0x01010002,
+ NAME_ATTR = 0x01010003,
+ PERMISSION_ATTR = 0x01010006,
+ EXPORTED_ATTR = 0x01010010,
+ GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
+ RESOURCE_ATTR = 0x01010025,
+ DEBUGGABLE_ATTR = 0x0101000f,
+ VALUE_ATTR = 0x01010024,
+ VERSION_CODE_ATTR = 0x0101021b,
+ VERSION_NAME_ATTR = 0x0101021c,
+ SCREEN_ORIENTATION_ATTR = 0x0101001e,
+ MIN_SDK_VERSION_ATTR = 0x0101020c,
+ MAX_SDK_VERSION_ATTR = 0x01010271,
+ REQ_TOUCH_SCREEN_ATTR = 0x01010227,
+ REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
+ REQ_HARD_KEYBOARD_ATTR = 0x01010229,
+ REQ_NAVIGATION_ATTR = 0x0101022a,
+ REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
+ TARGET_SDK_VERSION_ATTR = 0x01010270,
+ TEST_ONLY_ATTR = 0x01010272,
+ ANY_DENSITY_ATTR = 0x0101026c,
+ GL_ES_VERSION_ATTR = 0x01010281,
+ SMALL_SCREEN_ATTR = 0x01010284,
+ NORMAL_SCREEN_ATTR = 0x01010285,
+ LARGE_SCREEN_ATTR = 0x01010286,
+ XLARGE_SCREEN_ATTR = 0x010102bf,
+ REQUIRED_ATTR = 0x0101028e,
+ INSTALL_LOCATION_ATTR = 0x010102b7,
+ SCREEN_SIZE_ATTR = 0x010102ca,
+ SCREEN_DENSITY_ATTR = 0x010102cb,
+ REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
+ COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
+ LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
+ PUBLIC_KEY_ATTR = 0x010103a6,
+ CATEGORY_ATTR = 0x010103e8,
+ BANNER_ATTR = 0x10103f2,
+ ISGAME_ATTR = 0x10103f4,
+ REQUIRED_FEATURE_ATTR = 0x1010557,
+ REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+ COMPILE_SDK_VERSION_ATTR = 0x01010572,
+ COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
+};
+
+const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
+
+/** Retrieves the attribute of the element with the specified attribute resource id. */
+static xml::Attribute* FindAttribute(xml::Element *el, uint32_t resd_id) {
+ for (auto& a : el->attributes) {
+ if (a.compiled_attribute && a.compiled_attribute.value().id) {
+ if (a.compiled_attribute.value().id.value() == resd_id) {
+ return std::move(&a);
+ }
+ }
+ }
+ return nullptr;
+}
+
+/** Retrieves the attribute of the element that has the specified namespace and attribute name. */
+static xml::Attribute* FindAttribute(xml::Element *el, const std::string &package,
+ const std::string &name) {
+ return el->FindAttribute(package, name);
+}
+
+class CommonFeatureGroup;
+
+class ManifestExtractor {
+ public:
+ struct Options {
+ /** Include meta information from <meta-data> elements in the output. */
+ bool include_meta_data = false;
+
+ /** Only output permission information. */
+ bool only_permissions = false;
+ };
+
+ explicit ManifestExtractor(LoadedApk* apk, ManifestExtractor::Options options)
+ : apk_(apk), options_(options) { }
+
+ class Element {
+ public:
+ Element() = default;
+ virtual ~Element() = default;
+
+ static std::unique_ptr<Element> Inflate(ManifestExtractor* extractor, xml::Element* el);
+
+ /** Writes out the extracted contents of the element. */
+ virtual void Print(text::Printer& printer) { }
+
+ /** Adds an element to the list of children of the element. */
+ void AddChild(std::unique_ptr<Element>& child) { children_.push_back(std::move(child)); }
+
+ /** Retrieves the list of children of the element. */
+ const std::vector<std::unique_ptr<Element>>& children() const {
+ return children_;
+ }
+
+ /** Retrieves the extracted xml element tag. */
+ const std::string tag() const {
+ return tag_;
+ }
+
+ protected:
+ ManifestExtractor* extractor() const {
+ return extractor_;
+ }
+
+ /** Retrieves and stores the information extracted from the xml element. */
+ virtual void Extract(xml::Element* el) { }
+
+ /*
+ * Retrieves a configuration value of the resource entry that best matches the specified
+ * configuration.
+ */
+ static Value* BestConfigValue(ResourceEntry* entry,
+ const ConfigDescription& match) {
+ if (!entry) {
+ return nullptr;
+ }
+
+ // Determine the config that best matches the desired config
+ ResourceConfigValue* best_value = nullptr;
+ for (auto& value : entry->values) {
+ if (!value->config.match(match)) {
+ continue;
+ }
+
+ if (best_value != nullptr) {
+ if (!value->config.isBetterThan(best_value->config, &match)) {
+ if (value->config.compare(best_value->config) != 0) {
+ continue;
+ }
+ }
+ }
+
+ best_value = value.get();
+ }
+
+ // The entry has no values
+ if (!best_value) {
+ return nullptr;
+ }
+
+ return best_value->value.get();
+ }
+
+ /** Retrieves the resource assigned to the specified resource id if one exists. */
+ Value* FindValueById(const ResourceTable* table, const ResourceId& res_id,
+ const ConfigDescription& config = DummyConfig()) {
+ if (table) {
+ for (auto& package : table->packages) {
+ if (package->id && package->id.value() == res_id.package_id()) {
+ for (auto& type : package->types) {
+ if (type->id && type->id.value() == res_id.type_id()) {
+ for (auto& entry : type->entries) {
+ if (entry->id && entry->id.value() == res_id.entry_id()) {
+ if (auto value = BestConfigValue(entry.get(), config)) {
+ return value;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ /** Attempts to resolve the reference to a non-reference value. */
+ Value* ResolveReference(Reference* ref, const ConfigDescription& config = DummyConfig()) {
+ const int kMaxIterations = 40;
+ int i = 0;
+ while (ref && ref->id && i++ < kMaxIterations) {
+ auto table = extractor_->apk_->GetResourceTable();
+ if (auto value = FindValueById(table, ref->id.value(), config)) {
+ if (ValueCast<Reference>(value)) {
+ ref = ValueCast<Reference>(value);
+ } else {
+ return value;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ /**
+ * Retrieves the integer value of the attribute . If the value of the attribute is a reference,
+ * this will attempt to resolve the reference to an integer value.
+ **/
+ int32_t* GetAttributeInteger(xml::Attribute* attr,
+ const ConfigDescription& config = DummyConfig()) {
+ if (attr != nullptr) {
+ if (attr->compiled_value) {
+ // Resolve references using the dummy configuration
+ Value* value = attr->compiled_value.get();
+ if (ValueCast<Reference>(value)) {
+ value = ResolveReference(ValueCast<Reference>(value), config);
+ } else {
+ value = attr->compiled_value.get();
+ }
+ // Retrieve the integer data if possible
+ if (value != nullptr) {
+ if (BinaryPrimitive* intValue = ValueCast<BinaryPrimitive>(value)) {
+ return (int32_t*) &intValue->value.data;
+ }
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ /**
+ * A version of GetAttributeInteger that returns a default integer if the attribute does not
+ * exist or cannot be resolved to an integer value.
+ **/
+ int32_t GetAttributeIntegerDefault(xml::Attribute* attr, int32_t def,
+ const ConfigDescription& config = DummyConfig()) {
+ auto value = GetAttributeInteger(attr, config);
+ if (value) {
+ return *value;
+ }
+ return def;
+ }
+
+ /**
+ * Retrieves the string value of the attribute. If the value of the attribute is a reference,
+ * this will attempt to resolve the reference to a string value.
+ **/
+ const std::string* GetAttributeString(xml::Attribute* attr,
+ const ConfigDescription& config = DummyConfig()) {
+ if (attr != nullptr) {
+ if (attr->compiled_value) {
+ // Resolve references using the dummy configuration
+ Value* value = attr->compiled_value.get();
+ if (ValueCast<Reference>(value)) {
+ value = ResolveReference(ValueCast<Reference>(value), config);
+ } else {
+ value = attr->compiled_value.get();
+ }
+
+ // Retrieve the string data of the value if possible
+ if (value != nullptr) {
+ if (String* intValue = ValueCast<String>(value)) {
+ return &(*intValue->value);
+ } else if (RawString* rawValue = ValueCast<RawString>(value)) {
+ return &(*rawValue->value);
+ } else if (FileReference* strValue = ValueCast<FileReference>(value)) {
+ return &(*strValue->path);
+ }
+ }
+ }
+ return &attr->value;
+ }
+ return nullptr;
+ }
+
+ /**
+ * A version of GetAttributeString that returns a default string if the attribute does not
+ * exist or cannot be resolved to an string value.
+ **/
+ std::string GetAttributeStringDefault(xml::Attribute* attr, std::string def,
+ const ConfigDescription& config = DummyConfig()) {
+ auto value = GetAttributeString(attr, config);
+ if (value) {
+ return *value;
+ }
+ return def;
+ }
+
+ private:
+ ManifestExtractor* extractor_;
+ std::vector<std::unique_ptr<Element>> children_;
+ std::string tag_;
+ };
+
+ friend Element;
+
+ /** Creates a default configuration used to retrieve resources. */
+ static ConfigDescription DummyConfig() {
+ ConfigDescription config;
+ config.orientation = android::ResTable_config::ORIENTATION_PORT;
+ config.density = android::ResTable_config::DENSITY_MEDIUM;
+ config.sdkVersion = 10000; // Very high.
+ config.screenWidthDp = 320;
+ config.screenHeightDp = 480;
+ config.smallestScreenWidthDp = 320;
+ config.screenLayout |= android::ResTable_config::SCREENSIZE_NORMAL;
+ return config;
+ }
+
+ bool Dump(text::Printer& printer, IDiagnostics* diag);
+
+ /** Recursively visit the xml element tree and return a processed badging element tree. */
+ std::unique_ptr<Element> Visit(xml::Element* element);
+
+ /** Raises the target sdk value if the min target is greater than the current target. */
+ void RaiseTargetSdk(int32_t min_target) {
+ if (min_target > target_sdk_) {
+ target_sdk_ = min_target;
+ }
+ }
+
+ /**
+ * Retrieves the default feature group that features are added into when <uses-feature>
+ * are not in a <feature-group> element.
+ **/
+ CommonFeatureGroup* GetCommonFeatureGroup() {
+ return commonFeatureGroup_.get();
+ }
+
+ /**
+ * Retrieves a mapping of density values to Configurations for retrieving resources that would be
+ * used for that density setting.
+ **/
+ const std::map<uint16_t, ConfigDescription> densities() const {
+ return densities_;
+ }
+
+ /**
+ * Retrieves a mapping of locale BCP 47 strings to Configurations for retrieving resources that
+ * would be used for that locale setting.
+ **/
+ const std::map<std::string, ConfigDescription> locales() const {
+ return locales_;
+ }
+
+ /** Retrieves the current stack of parent during data extraction. */
+ const std::vector<Element*> parent_stack() const {
+ return parent_stack_;
+ }
+
+ int32_t target_sdk() const {
+ return target_sdk_;
+ }
+
+ LoadedApk* const apk_;
+ const Options options_;
+
+ private:
+ std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_ = util::make_unique<CommonFeatureGroup>();
+ std::map<std::string, ConfigDescription> locales_;
+ std::map<uint16_t, ConfigDescription> densities_;
+ std::vector<Element*> parent_stack_;
+ int32_t target_sdk_ = 0;
+};
+
+template<typename T> T* ElementCast(ManifestExtractor::Element* element);
+
+/** Recurs through the children of the specified root in depth-first order. */
+static void ForEachChild(ManifestExtractor::Element* root,
+ std::function<void(ManifestExtractor::Element*)> f) {
+ for (auto& child : root->children()) {
+ f(child.get());
+ ForEachChild(child.get(), f);
+ }
+}
+
+/**
+ * Checks the element and its recursive children for an element that makes the specified
+ * conditional function return true. Returns the first element that makes the conditional function
+ * return true.
+ **/
+static ManifestExtractor::Element* FindElement(ManifestExtractor::Element* root,
+ std::function<bool(ManifestExtractor::Element*)> f) {
+ if (f(root)) {
+ return root;
+ }
+ for (auto& child : root->children()) {
+ if (auto b2 = FindElement(child.get(), f)) {
+ return b2;
+ }
+ }
+ return nullptr;
+}
+
+/** Represents the <manifest> elements **/
+class Manifest : public ManifestExtractor::Element {
+ public:
+ Manifest() = default;
+ std::string package;
+ int32_t versionCode;
+ std::string versionName;
+ const std::string* split = nullptr;
+ const std::string* platformVersionName = nullptr;
+ const std::string* platformVersionCode = nullptr;
+ const int32_t* compilesdkVersion = nullptr;
+ const std::string* compilesdkVersionCodename = nullptr;
+ const int32_t* installLocation = nullptr;
+
+ void Extract(xml::Element* manifest) override {
+ package = GetAttributeStringDefault(FindAttribute(manifest, {}, "package"), "");
+ versionCode = GetAttributeIntegerDefault(FindAttribute(manifest, VERSION_CODE_ATTR), 0);
+ versionName = GetAttributeStringDefault(FindAttribute(manifest, VERSION_NAME_ATTR), "");
+ split = GetAttributeString(FindAttribute(manifest, {}, "split"));
+
+ // Extract the platform build info
+ platformVersionName = GetAttributeString(FindAttribute(manifest, {},
+ "platformBuildVersionName"));
+ platformVersionCode = GetAttributeString(FindAttribute(manifest, {},
+ "platformBuildVersionCode"));
+
+ // Extract the compile sdk info
+ compilesdkVersion = GetAttributeInteger(FindAttribute(manifest, COMPILE_SDK_VERSION_ATTR));
+ compilesdkVersionCodename = GetAttributeString(
+ FindAttribute(manifest, COMPILE_SDK_VERSION_CODENAME_ATTR));
+ installLocation = GetAttributeInteger(FindAttribute(manifest, INSTALL_LOCATION_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print(StringPrintf("package: name='%s' ", package.data()));
+ printer.Print(StringPrintf("versionCode='%s' ",
+ (versionCode > 0) ? std::to_string(versionCode).data() : ""));
+ printer.Print(StringPrintf("versionName='%s'", versionName.data()));
+
+ if (split) {
+ printer.Print(StringPrintf(" split='%s'", split->data()));
+ }
+ if (platformVersionName) {
+ printer.Print(StringPrintf(" platformBuildVersionName='%s'", platformVersionName->data()));
+ }
+ if (platformVersionCode) {
+ printer.Print(StringPrintf(" platformBuildVersionCode='%s'", platformVersionCode->data()));
+ }
+ if (compilesdkVersion) {
+ printer.Print(StringPrintf(" compileSdkVersion='%d'", *compilesdkVersion));
+ }
+ if (compilesdkVersionCodename) {
+ printer.Print(StringPrintf(" compileSdkVersionCodename='%s'",
+ compilesdkVersionCodename->data()));
+ }
+ printer.Print("\n");
+
+ if (installLocation) {
+ switch (*installLocation) {
+ case 0:
+ printer.Print("install-location:'auto'\n");
+ break;
+ case 1:
+ printer.Print("install-location:'internalOnly'\n");
+ break;
+ case 2:
+ printer.Print("install-location:'preferExternal'\n");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+};
+
+/** Represents <application> elements. **/
+class Application : public ManifestExtractor::Element {
+ public:
+ Application() = default;
+ std::string label;
+ std::string icon;
+ std::string banner;
+ int32_t is_game;
+ int32_t debuggable;
+ int32_t test_only;
+ bool has_multi_arch;
+
+ /** Mapping from locales to app names. */
+ std::map<std::string, std::string> locale_labels;
+
+ /** Mapping from densities to app icons. */
+ std::map<uint16_t, std::string> density_icons;
+
+ void Extract(xml::Element* element) override {
+ label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+ icon = GetAttributeStringDefault(FindAttribute(element, ICON_ATTR), "");
+ test_only = GetAttributeIntegerDefault(FindAttribute(element, TEST_ONLY_ATTR), 0);
+ banner = GetAttributeStringDefault(FindAttribute(element, BANNER_ATTR), "");
+ is_game = GetAttributeIntegerDefault(FindAttribute(element, ISGAME_ATTR), 0);
+ debuggable = GetAttributeIntegerDefault(FindAttribute(element, DEBUGGABLE_ATTR), 0);
+
+ // We must search by name because the multiArch flag hasn't been API
+ // frozen yet.
+ has_multi_arch = (GetAttributeIntegerDefault(
+ FindAttribute(element, kAndroidNamespace, "multiArch"), 0) != 0);
+
+ // Retrieve the app names for every locale the app supports
+ auto attr = FindAttribute(element, LABEL_ATTR);
+ for (auto& config : extractor()->locales()) {
+ if (auto label = GetAttributeString(attr, config.second)) {
+ if (label) {
+ locale_labels.insert(std::make_pair(config.first, *label));
+ }
+ }
+ }
+
+ // Retrieve the icons for the densities the app supports
+ attr = FindAttribute(element, ICON_ATTR);
+ for (auto& config : extractor()->densities()) {
+ if (auto resource = GetAttributeString(attr, config.second)) {
+ if (resource) {
+ density_icons.insert(std::make_pair(config.first, *resource));
+ }
+ }
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ // Print the labels for every locale
+ for (auto p : locale_labels) {
+ if (p.first.empty()) {
+ printer.Print(StringPrintf("application-label:'%s'\n",
+ android::ResTable::normalizeForOutput(p.second.data())
+ .c_str()));
+ } else {
+ printer.Print(StringPrintf("application-label-%s:'%s'\n", p.first.data(),
+ android::ResTable::normalizeForOutput(p.second.data())
+ .c_str()));
+ }
+ }
+
+ // Print the icon paths for every density
+ for (auto p : density_icons) {
+ printer.Print(StringPrintf("application-icon-%d:'%s'\n", p.first, p.second.data()));
+ }
+
+ // Print the application info
+ printer.Print(StringPrintf("application: label='%s' ",
+ android::ResTable::normalizeForOutput(label.data()).c_str()));
+ printer.Print(StringPrintf("icon='%s'", icon.data()));
+ if (!banner.empty()) {
+ printer.Print(StringPrintf(" banner='%s'", banner.data()));
+ }
+ printer.Print("\n");
+
+ if (test_only != 0) {
+ printer.Print(StringPrintf("testOnly='%d'\n", test_only));
+ }
+ if (is_game != 0) {
+ printer.Print("application-isGame\n");
+ }
+ if (debuggable != 0) {
+ printer.Print("application-debuggable\n");
+ }
+ }
+};
+
+/** Represents <uses-sdk> elements. **/
+class UsesSdkBadging : public ManifestExtractor::Element {
+ public:
+ UsesSdkBadging() = default;
+ const int32_t* min_sdk = nullptr;
+ const std::string* min_sdk_name = nullptr;
+ const int32_t* max_sdk = nullptr;
+ const int32_t* target_sdk = nullptr;
+ const std::string* target_sdk_name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ min_sdk = GetAttributeInteger(FindAttribute(element, MIN_SDK_VERSION_ATTR));
+ min_sdk_name = GetAttributeString(FindAttribute(element, MIN_SDK_VERSION_ATTR));
+ max_sdk = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
+ target_sdk = GetAttributeInteger(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
+ target_sdk_name = GetAttributeString(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
+
+ // Detect the target sdk of the element
+ if ((min_sdk_name && *min_sdk_name == "Donut")
+ || (target_sdk_name && *target_sdk_name == "Donut")) {
+ extractor()->RaiseTargetSdk(4);
+ }
+ if (min_sdk) {
+ extractor()->RaiseTargetSdk(*min_sdk);
+ }
+ if (target_sdk) {
+ extractor()->RaiseTargetSdk(*target_sdk);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (min_sdk) {
+ printer.Print(StringPrintf("sdkVersion:'%d'\n", *min_sdk));
+ } else if (min_sdk_name) {
+ printer.Print(StringPrintf("sdkVersion:'%s'\n", min_sdk_name->data()));
+ }
+ if (max_sdk) {
+ printer.Print(StringPrintf("maxSdkVersion:'%d'\n", *max_sdk));
+ }
+ if (target_sdk) {
+ printer.Print(StringPrintf("targetSdkVersion:'%d'\n", *target_sdk));
+ } else if (target_sdk_name) {
+ printer.Print(StringPrintf("targetSdkVersion:'%s'\n", target_sdk_name->data()));
+ }
+ }
+};
+
+/** Represents <uses-configuration> elements. **/
+class UsesConfiguarion : public ManifestExtractor::Element {
+ public:
+ UsesConfiguarion() = default;
+ int32_t req_touch_screen = 0;
+ int32_t req_keyboard_type = 0;
+ int32_t req_hard_keyboard = 0;
+ int32_t req_navigation = 0;
+ int32_t req_five_way_nav = 0;
+
+ void Extract(xml::Element* element) override {
+ req_touch_screen = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_TOUCH_SCREEN_ATTR), 0);
+ req_keyboard_type = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_KEYBOARD_TYPE_ATTR), 0);
+ req_hard_keyboard = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_HARD_KEYBOARD_ATTR), 0);
+ req_navigation = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_NAVIGATION_ATTR), 0);
+ req_five_way_nav = GetAttributeIntegerDefault(
+ FindAttribute(element, REQ_FIVE_WAY_NAV_ATTR), 0);
+ }
+
+ void Print(text::Printer& printer) override {
+ printer.Print("uses-configuration:");
+ if (req_touch_screen != 0) {
+ printer.Print(StringPrintf(" reqTouchScreen='%d'", req_touch_screen));
+ }
+ if (req_keyboard_type != 0) {
+ printer.Print(StringPrintf(" reqKeyboardType='%d'", req_keyboard_type));
+ }
+ if (req_hard_keyboard != 0) {
+ printer.Print(StringPrintf(" reqHardKeyboard='%d'", req_hard_keyboard));
+ }
+ if (req_navigation != 0) {
+ printer.Print(StringPrintf(" reqNavigation='%d'", req_navigation));
+ }
+ if (req_five_way_nav != 0) {
+ printer.Print(StringPrintf(" reqFiveWayNav='%d'", req_five_way_nav));
+ }
+ printer.Print("\n");
+ }
+};
+
+/** Represents <supports-screen> elements. **/
+class SupportsScreen : public ManifestExtractor::Element {
+ public:
+ SupportsScreen() = default;
+ int32_t small_screen = 1;
+ int32_t normal_screen = 1;
+ int32_t large_screen = 1;
+ int32_t xlarge_screen = 1;
+ int32_t any_density = 1;
+ int32_t requires_smallest_width_dp = 0;
+ int32_t compatible_width_limit_dp = 0;
+ int32_t largest_width_limit_dp = 0;
+
+ void Extract(xml::Element* element) override {
+ small_screen = GetAttributeIntegerDefault(FindAttribute(element, SMALL_SCREEN_ATTR), 1);
+ normal_screen = GetAttributeIntegerDefault(FindAttribute(element, NORMAL_SCREEN_ATTR), 1);
+ large_screen = GetAttributeIntegerDefault(FindAttribute(element, LARGE_SCREEN_ATTR), 1);
+ xlarge_screen = GetAttributeIntegerDefault(FindAttribute(element, XLARGE_SCREEN_ATTR), 1);
+ any_density = GetAttributeIntegerDefault(FindAttribute(element, ANY_DENSITY_ATTR), 1);
+
+ requires_smallest_width_dp = GetAttributeIntegerDefault(
+ FindAttribute(element, REQUIRES_SMALLEST_WIDTH_DP_ATTR), 0);
+ compatible_width_limit_dp = GetAttributeIntegerDefault(
+ FindAttribute(element, COMPATIBLE_WIDTH_LIMIT_DP_ATTR), 0);
+ largest_width_limit_dp = GetAttributeIntegerDefault(
+ FindAttribute(element, LARGEST_WIDTH_LIMIT_DP_ATTR), 0);
+
+ // For modern apps, if screen size buckets haven't been specified
+ // but the new width ranges have, then infer the buckets from them.
+ if (small_screen > 0 && normal_screen > 0 && large_screen > 0 && xlarge_screen > 0
+ && requires_smallest_width_dp > 0) {
+ int32_t compat_width = (compatible_width_limit_dp > 0) ? compatible_width_limit_dp
+ : requires_smallest_width_dp;
+ small_screen = (requires_smallest_width_dp <= 240 && compat_width >= 240) ? -1 : 0;
+ normal_screen = (requires_smallest_width_dp <= 320 && compat_width >= 320) ? -1 : 0;
+ large_screen = (requires_smallest_width_dp <= 480 && compat_width >= 480) ? -1 : 0;
+ xlarge_screen = (requires_smallest_width_dp <= 720 && compat_width >= 720) ? -1 : 0;
+ }
+ }
+
+ void PrintScreens(text::Printer& printer, int32_t target_sdk) {
+ int32_t small_screen_temp = small_screen;
+ int32_t normal_screen_temp = normal_screen;
+ int32_t large_screen_temp = large_screen;
+ int32_t xlarge_screen_temp = xlarge_screen;
+ int32_t any_density_temp = any_density;
+
+ // Determine default values for any unspecified screen sizes,
+ // based on the target SDK of the package. As of 4 (donut)
+ // the screen size support was introduced, so all default to
+ // enabled.
+ if (small_screen_temp > 0) {
+ small_screen_temp = target_sdk >= 4 ? -1 : 0;
+ }
+ if (normal_screen_temp > 0) {
+ normal_screen_temp = -1;
+ }
+ if (large_screen_temp > 0) {
+ large_screen_temp = target_sdk >= 4 ? -1 : 0;
+ }
+ if (xlarge_screen_temp > 0) {
+ // Introduced in Gingerbread.
+ xlarge_screen_temp = target_sdk >= 9 ? -1 : 0;
+ }
+ if (any_density_temp > 0) {
+ any_density_temp = (target_sdk >= 4 || requires_smallest_width_dp > 0
+ || compatible_width_limit_dp > 0) ? -1 : 0;
+ }
+
+ // Print the formatted screen info
+ printer.Print("supports-screens:");
+ if (small_screen_temp != 0) {
+ printer.Print(" 'small'");
+ }
+ if (normal_screen_temp != 0) {
+ printer.Print(" 'normal'");
+ }
+ if (large_screen_temp != 0) {
+ printer.Print(" 'large'");
+ }
+ if (xlarge_screen_temp != 0) {
+ printer.Print(" 'xlarge'");
+ }
+ printer.Print("\n");
+ printer.Print(StringPrintf("supports-any-density: '%s'\n",
+ (any_density_temp ) ? "true" : "false"));
+ if (requires_smallest_width_dp > 0) {
+ printer.Print(StringPrintf("requires-smallest-width:'%d'\n", requires_smallest_width_dp));
+ }
+ if (compatible_width_limit_dp > 0) {
+ printer.Print(StringPrintf("compatible-width-limit:'%d'\n", compatible_width_limit_dp));
+ }
+ if (largest_width_limit_dp > 0) {
+ printer.Print(StringPrintf("largest-width-limit:'%d'\n", largest_width_limit_dp));
+ }
+ }
+};
+
+/** Represents <feature-group> elements. **/
+class FeatureGroup : public ManifestExtractor::Element {
+ public:
+ FeatureGroup() = default;
+ std::string label;
+ int32_t open_gles_version = 0;
+
+ void Extract(xml::Element* element) override {
+ label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+ }
+
+ virtual void PrintGroup(text::Printer& printer) {
+ printer.Print(StringPrintf("feature-group: label='%s'\n", label.data()));
+ if (open_gles_version > 0) {
+ printer.Print(StringPrintf(" uses-gl-es: '0x%x'\n", open_gles_version));
+ }
+
+ for (auto feature : features_) {
+ printer.Print(StringPrintf(" uses-feature%s: name='%s'",
+ (feature.second.required ? "" : "-not-required"),
+ feature.first.data()));
+ if (feature.second.version > 0) {
+ printer.Print(StringPrintf(" version='%d'", feature.second.version));
+ }
+ printer.Print("\n");
+ }
+ }
+
+ /** Adds a feature to the feature group. */
+ void AddFeature(const std::string& name, bool required = true, int32_t version = -1) {
+ features_.insert(std::make_pair(name, Feature{ required, version }));
+ if (required) {
+ if (name == "android.hardware.camera.autofocus" ||
+ name == "android.hardware.camera.flash") {
+ AddFeature("android.hardware.camera", true);
+ } else if (name == "android.hardware.location.gps" ||
+ name == "android.hardware.location.network") {
+ AddFeature("android.hardware.location", true);
+ } else if (name == "android.hardware.faketouch.multitouch") {
+ AddFeature("android.hardware.faketouch", true);
+ } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
+ name == "android.hardware.faketouch.multitouch.jazzhands") {
+ AddFeature("android.hardware.faketouch.multitouch", true);
+ AddFeature("android.hardware.faketouch", true);
+ } else if (name == "android.hardware.touchscreen.multitouch") {
+ AddFeature("android.hardware.touchscreen", true);
+ } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
+ name == "android.hardware.touchscreen.multitouch.jazzhands") {
+ AddFeature("android.hardware.touchscreen.multitouch", true);
+ AddFeature("android.hardware.touchscreen", true);
+ } else if (name == "android.hardware.opengles.aep") {
+ const int kOpenGLESVersion31 = 0x00030001;
+ if (kOpenGLESVersion31 > open_gles_version) {
+ open_gles_version = kOpenGLESVersion31;
+ }
+ }
+ }
+ }
+
+ /** Returns true if the feature group has the given feature. */
+ virtual bool HasFeature(const std::string& name) {
+ return features_.find(name) != features_.end();
+ }
+
+ /** Merges the features of another feature group into this group. */
+ void Merge(FeatureGroup* group) {
+ open_gles_version = std::max(open_gles_version, group->open_gles_version);
+ for (auto& feature : group->features_) {
+ features_.insert(feature);
+ }
+ }
+
+ protected:
+ struct Feature {
+ public:
+ bool required = false;
+ int32_t version = -1;
+ };
+
+ /* Mapping of feature names to their properties. */
+ std::map<std::string, Feature> features_;
+};
+
+/**
+ * Represents the default feature group for the application if no <feature-group> elements are
+ * present in the manifest.
+ **/
+class CommonFeatureGroup : public FeatureGroup {
+ public:
+ CommonFeatureGroup() = default;
+ void PrintGroup(text::Printer& printer) override {
+ FeatureGroup::PrintGroup(printer);
+
+ // Also print the implied features
+ for (auto feature : implied_features_) {
+ if (features_.find(feature.first) == features_.end()) {
+ const char* sdk23 = feature.second.implied_from_sdk_k23 ? "-sdk-23" : "";
+ printer.Print(StringPrintf(" uses-feature%s: name='%s'\n", sdk23, feature.first.data()));
+ printer.Print(StringPrintf(" uses-implied-feature%s: name='%s' reason='", sdk23,
+ feature.first.data()));
+
+ // Print the reasons as a sentence
+ size_t count = 0;
+ for (auto reason : feature.second.reasons) {
+ printer.Print(reason);
+ if (count + 2 < feature.second.reasons.size()) {
+ printer.Print(", ");
+ } else if (count + 1 < feature.second.reasons.size()) {
+ printer.Print(", and ");
+ }
+ count++;
+ }
+ printer.Print("'\n");
+ }
+ }
+ }
+
+ /** Returns true if the feature group has the given feature. */
+ bool HasFeature(const std::string& name) override {
+ return FeatureGroup::HasFeature(name)
+ || implied_features_.find(name) != implied_features_.end();
+ }
+
+ /** Adds a feature to a set of implied features not explicitly requested in the manifest. */
+ void addImpliedFeature(const std::string& name, const std::string& reason, bool sdk23 = false) {
+ auto entry = implied_features_.find(name);
+ if (entry == implied_features_.end()) {
+ implied_features_.insert(std::make_pair(name, ImpliedFeature(sdk23)));
+ entry = implied_features_.find(name);
+ }
+
+ // A non-sdk 23 implied feature takes precedence.
+ if (entry->second.implied_from_sdk_k23 && !sdk23) {
+ entry->second.implied_from_sdk_k23 = false;
+ }
+
+ entry->second.reasons.insert(reason);
+ }
+
+ /**
+ * Adds a feature to a set of implied features for all features that are implied by the presence
+ * of the permission.
+ **/
+ void addImpliedFeaturesForPermission(int32_t targetSdk, const std::string& name, bool sdk23) {
+ if (name == "android.permission.CAMERA") {
+ addImpliedFeature("android.hardware.camera",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
+ if (targetSdk < SDK_LOLLIPOP) {
+ addImpliedFeature("android.hardware.location.gps",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+ addImpliedFeature("android.hardware.location.gps",
+ StringPrintf("targetSdkVersion < %d", SDK_LOLLIPOP),
+ sdk23);
+ }
+ addImpliedFeature("android.hardware.location",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
+ if (targetSdk < SDK_LOLLIPOP) {
+ addImpliedFeature("android.hardware.location.network",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+ addImpliedFeature("android.hardware.location.network",
+ StringPrintf("targetSdkVersion < %d", SDK_LOLLIPOP),
+ sdk23);
+ }
+ addImpliedFeature("android.hardware.location",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
+ name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
+ name == "android.permission.INSTALL_LOCATION_PROVIDER") {
+ addImpliedFeature("android.hardware.location",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.BLUETOOTH" ||
+ name == "android.permission.BLUETOOTH_ADMIN") {
+ if (targetSdk > SDK_DONUT) {
+ addImpliedFeature("android.hardware.bluetooth",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+ addImpliedFeature("android.hardware.bluetooth",
+ StringPrintf("targetSdkVersion > %d", SDK_DONUT),
+ sdk23);
+ }
+
+ } else if (name == "android.permission.RECORD_AUDIO") {
+ addImpliedFeature("android.hardware.microphone",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
+ name == "android.permission.CHANGE_WIFI_STATE" ||
+ name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
+ addImpliedFeature("android.hardware.wifi",
+ StringPrintf("requested %s permission", name.data()),
+ sdk23);
+
+ } else if (name == "android.permission.CALL_PHONE" ||
+ name == "android.permission.CALL_PRIVILEGED" ||
+ name == "android.permission.MODIFY_PHONE_STATE" ||
+ name == "android.permission.PROCESS_OUTGOING_CALLS" ||
+ name == "android.permission.READ_SMS" ||
+ name == "android.permission.RECEIVE_SMS" ||
+ name == "android.permission.RECEIVE_MMS" ||
+ name == "android.permission.RECEIVE_WAP_PUSH" ||
+ name == "android.permission.SEND_SMS" ||
+ name == "android.permission.WRITE_APN_SETTINGS" ||
+ name == "android.permission.WRITE_SMS") {
+ addImpliedFeature("android.hardware.telephony",
+ "requested a telephony permission",
+ sdk23);
+ }
+ }
+
+ private:
+ /**
+ * Represents a feature that has been automatically added due to a pre-requisite or for some
+ * other reason.
+ */
+ struct ImpliedFeature {
+ explicit ImpliedFeature(bool sdk23 = false) : implied_from_sdk_k23(sdk23) {}
+
+ /** List of human-readable reasons for why this feature was implied. */
+ std::set<std::string> reasons;
+
+ // Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)
+ bool implied_from_sdk_k23;
+ };
+
+ /* Mapping of implied feature names to their properties. */
+ std::map<std::string, ImpliedFeature> implied_features_;
+};
+
+/** Represents <uses-feature> elements. **/
+class UsesFeature : public ManifestExtractor::Element {
+ public:
+ UsesFeature() = default;
+ void Extract(xml::Element* element) override {
+ const std::string* name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ int32_t* gl = GetAttributeInteger(FindAttribute(element, GL_ES_VERSION_ATTR));
+ bool required = GetAttributeIntegerDefault(
+ FindAttribute(element, REQUIRED_ATTR), true) != 0;
+ int32_t version = GetAttributeIntegerDefault(
+ FindAttribute(element, kAndroidNamespace, "version"), 0);
+
+ // Add the feature to the parent feature group element if one exists; otherwise, add it to the
+ // common feature group
+ FeatureGroup* feature_group = ElementCast<FeatureGroup>(extractor()->parent_stack()[0]);
+ if (!feature_group) {
+ feature_group = extractor()->GetCommonFeatureGroup();
+ } else {
+ // All features in side of <feature-group> elements are required.
+ required = true;
+ }
+
+ if (name) {
+ feature_group->AddFeature(*name, required, version);
+ } else if (gl) {
+ feature_group->open_gles_version = std::max(feature_group->open_gles_version, *gl);
+ }
+ }
+};
+
+/** Represents <uses-permission> elements. **/
+class UsesPermission : public ManifestExtractor::Element {
+ public:
+ UsesPermission() = default;
+ std::string name;
+ std::string requiredFeature;
+ std::string requiredNotFeature;
+ int32_t required = true;
+ int32_t maxSdkVersion = -1;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ requiredFeature = GetAttributeStringDefault(
+ FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
+ requiredNotFeature = GetAttributeStringDefault(
+ FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ maxSdkVersion = GetAttributeIntegerDefault(
+ FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
+
+ if (!name.empty()) {
+ CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+ common->addImpliedFeaturesForPermission(extractor()->target_sdk(), name, false);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (!name.empty()) {
+ printer.Print(StringPrintf("uses-permission: name='%s'", name.data()));
+ if (maxSdkVersion >= 0) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+ }
+ if (!requiredFeature.empty()) {
+ printer.Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data()));
+ }
+ if (!requiredNotFeature.empty()) {
+ printer.Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data()));
+ }
+ printer.Print("\n");
+ if (required == 0) {
+ printer.Print(StringPrintf("optional-permission: name='%s'", name.data()));
+ if (maxSdkVersion >= 0) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+ }
+ printer.Print("\n");
+ }
+ }
+ }
+
+ void PrintImplied(text::Printer& printer, const std::string& reason) {
+ printer.Print(StringPrintf("uses-implied-permission: name='%s'", name.data()));
+ if (maxSdkVersion >= 0) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+ }
+ printer.Print(StringPrintf(" reason='%s'\n", reason.data()));
+ }
+};
+
+/** Represents <uses-permission-sdk-23> elements. **/
+class UsesPermissionSdk23 : public ManifestExtractor::Element {
+ public:
+ UsesPermissionSdk23() = default;
+ const std::string* name = nullptr;
+ const int32_t* maxSdkVersion = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ maxSdkVersion = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
+
+ if (name) {
+ CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+ common->addImpliedFeaturesForPermission(extractor()->target_sdk(), *name, true);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("uses-permission-sdk-23: name='%s'", name->data()));
+ if (maxSdkVersion) {
+ printer.Print(StringPrintf(" maxSdkVersion='%d'", *maxSdkVersion));
+ }
+ printer.Print("\n");
+ }
+ }
+};
+
+/** Represents <permission> elements. These elements are only printing when dumping permissions. **/
+class Permission : public ManifestExtractor::Element {
+ public:
+ Permission() = default;
+ std::string name;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ }
+
+ void Print(text::Printer& printer) override {
+ if (extractor()->options_.only_permissions && !name.empty()) {
+ printer.Print(StringPrintf("permission: %s\n", name.data()));
+ }
+ }
+};
+
+/** Represents <activity> elements. **/
+class Activity : public ManifestExtractor::Element {
+ public:
+ Activity() = default;
+ std::string name;
+ std::string icon;
+ std::string label;
+ std::string banner;
+
+ bool has_component_ = false;
+ bool has_launcher_category = false;
+ bool has_leanback_launcher_category = false;
+ bool has_main_action = false;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+ icon = GetAttributeStringDefault(FindAttribute(element, ICON_ATTR), "");
+ banner = GetAttributeStringDefault(FindAttribute(element, BANNER_ATTR), "");
+
+ // Retrieve the package name from the manifest
+ std::string package;
+ for (auto& parent : extractor()->parent_stack()) {
+ if (auto manifest = ElementCast<Manifest>(parent)) {
+ package = manifest->package;
+ break;
+ }
+ }
+
+ // Fully qualify the activity name
+ ssize_t idx = name.find(".");
+ if (idx == 0) {
+ name = package + name;
+ } else if (idx < 0) {
+ name = package + "." + name;
+ }
+
+ auto orientation = GetAttributeInteger(FindAttribute(element, SCREEN_ORIENTATION_ATTR));
+ if (orientation) {
+ CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+ int orien = *orientation;
+ if (orien == 0 || orien == 6 || orien == 8) {
+ // Requests landscape, sensorLandscape, or reverseLandscape.
+ common->addImpliedFeature("android.hardware.screen.landscape",
+ "one or more activities have specified a landscape orientation",
+ false);
+ } else if (orien == 1 || orien == 7 || orien == 9) {
+ // Requests portrait, sensorPortrait, or reversePortrait.
+ common->addImpliedFeature("android.hardware.screen.portrait",
+ "one or more activities have specified a portrait orientation",
+ false);
+ }
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ // Print whether the activity has the HOME category and a the MAIN action
+ if (has_main_action && has_launcher_category) {
+ printer.Print("launchable-activity:");
+ if (!name.empty()) {
+ printer.Print(StringPrintf(" name='%s' ", name.data()));
+ }
+ printer.Print(StringPrintf(" label='%s' icon='%s'\n",
+ android::ResTable::normalizeForOutput(label.data()).c_str(),
+ icon.data()));
+ }
+
+ // Print wether the activity has the HOME category and a the MAIN action
+ if (has_leanback_launcher_category) {
+ printer.Print("leanback-launchable-activity:");
+ if (!name.empty()) {
+ printer.Print(StringPrintf(" name='%s' ", name.data()));
+ }
+ printer.Print(StringPrintf(" label='%s' icon='%s' banner='%s'\n",
+ android::ResTable::normalizeForOutput(label.data()).c_str(),
+ icon.data(), banner.data()));
+ }
+ }
+};
+
+/** Represents <intent-filter> elements. */
+class IntentFilter : public ManifestExtractor::Element {
+ public:
+ IntentFilter() = default;
+};
+
+/** Represents <category> elements. */
+class Category : public ManifestExtractor::Element {
+ public:
+ Category() = default;
+ std::string component = "";
+
+ void Extract(xml::Element* element) override {
+ const std::string* category = GetAttributeString(FindAttribute(element, NAME_ATTR));
+
+ auto parent_stack = extractor()->parent_stack();
+ if (category && ElementCast<IntentFilter>(parent_stack[0])
+ && ElementCast<Activity>(parent_stack[1])) {
+ Activity* activity = ElementCast<Activity>(parent_stack[1]);
+
+ if (*category == "android.intent.category.LAUNCHER") {
+ activity->has_launcher_category = true;
+ } else if (*category == "android.intent.category.LEANBACK_LAUNCHER") {
+ activity->has_leanback_launcher_category = true;
+ } else if (*category == "android.intent.category.HOME") {
+ component = "launcher";
+ }
+ }
+ }
+};
+
+/**
+ * Represents <provider> elements. The elements may have an <intent-filter> which may have <action>
+ * elements nested within.
+ **/
+class Provider : public ManifestExtractor::Element {
+ public:
+ Provider() = default;
+ bool has_required_saf_attributes = false;
+
+ void Extract(xml::Element* element) override {
+ const int32_t* exported = GetAttributeInteger(FindAttribute(element, EXPORTED_ATTR));
+ const int32_t* grant_uri_permissions = GetAttributeInteger(
+ FindAttribute(element, GRANT_URI_PERMISSIONS_ATTR));
+ const std::string* permission = GetAttributeString(
+ FindAttribute(element, PERMISSION_ATTR));
+
+ has_required_saf_attributes = ((exported && *exported != 0)
+ && (grant_uri_permissions && *grant_uri_permissions != 0)
+ && (permission && *permission == "android.permission.MANAGE_DOCUMENTS"));
+ }
+};
+
+/** Represents <receiver> elements. **/
+class Receiver : public ManifestExtractor::Element {
+ public:
+ Receiver() = default;
+ const std::string* permission = nullptr;
+ bool has_component = false;
+
+ void Extract(xml::Element* element) override {
+ permission = GetAttributeString(FindAttribute(element, PERMISSION_ATTR));
+ }
+};
+
+/**Represents <service> elements. **/
+class Service : public ManifestExtractor::Element {
+ public:
+ Service() = default;
+ const std::string* permission = nullptr;
+ bool has_component = false;
+
+ void Extract(xml::Element* element) override {
+ permission = GetAttributeString(FindAttribute(element, PERMISSION_ATTR));
+ }
+};
+
+/** Represents <uses-library> elements. **/
+class UsesLibrary : public ManifestExtractor::Element {
+ public:
+ UsesLibrary() = default;
+ std::string name;
+ int required;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ }
+ }
+
+ void Print(text::Printer& printer) override {
+ if (!name.empty()) {
+ printer.Print(StringPrintf("uses-library%s:'%s'\n",
+ (required == 0) ? "-not-required" : "", name.data()));
+ }
+ }
+};
+
+/**
+ * Represents <meta-data> elements. These tags are only printed when a flag is passed in to
+ * explicitly enable meta data printing.
+ **/
+class MetaData : public ManifestExtractor::Element {
+ public:
+ MetaData() = default;
+ std::string name;
+ const std::string* value;
+ const int* value_int;
+ const std::string* resource;
+ const int* resource_int;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ value = GetAttributeString(FindAttribute(element, VALUE_ATTR));
+ value_int = GetAttributeInteger(FindAttribute(element, VALUE_ATTR));
+ resource = GetAttributeString(FindAttribute(element, RESOURCE_ATTR));
+ resource_int = GetAttributeInteger(FindAttribute(element, RESOURCE_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (extractor()->options_.include_meta_data && !name.empty()) {
+ printer.Print(StringPrintf("meta-data: name='%s' ", name.data()));
+ if (value) {
+ printer.Print(StringPrintf("value='%s' ", value->data()));
+ } else if (value_int) {
+ printer.Print(StringPrintf("value='%d' ", *value_int));
+ } else {
+ if (resource) {
+ printer.Print(StringPrintf("resource='%s' ", resource->data()));
+ } else if (resource_int) {
+ printer.Print(StringPrintf("resource='%d' ", *resource_int));
+ }
+ }
+ printer.Print("\n");
+ }
+ }
+};
+
+/**
+ * Represents <action> elements. Detects the presence of certain activity, provider, receiver, and
+ * service components.
+ **/
+class Action : public ManifestExtractor::Element {
+ public:
+ Action() = default;
+ std::string component = "";
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ std::string action = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+
+ if (ElementCast<IntentFilter>(parent_stack[0])) {
+ if (ElementCast<Activity>(parent_stack[1])) {
+ // Detects the presence of a particular type of activity.
+ Activity* activity = ElementCast<Activity>(parent_stack[1]);
+ auto map = std::map<std::string, std::string>({
+ { "android.intent.action.MAIN" , "main" },
+ { "android.intent.action.VIDEO_CAMERA" , "camera" },
+ { "android.intent.action.STILL_IMAGE_CAMERA_SECURE" , "camera-secure" },
+ });
+
+ auto entry = map.find(action);
+ if (entry != map.end()) {
+ component = entry->second;
+ activity->has_component_ = true;
+ }
+
+ if (action == "android.intent.action.MAIN") {
+ activity->has_main_action = true;
+ }
+
+ } else if (ElementCast<Receiver>(parent_stack[1])) {
+ // Detects the presence of a particular type of receiver. If the action requires a
+ // permission, then the receiver element is checked for the permission.
+ Receiver* receiver = ElementCast<Receiver>(parent_stack[1]);
+ auto map = std::map<std::string, std::string>({
+ { "android.appwidget.action.APPWIDGET_UPDATE" , "app-widget" },
+ { "android.app.action.DEVICE_ADMIN_ENABLED" , "device-admin" },
+ });
+
+ auto permissions = std::map<std::string, std::string>({
+ { "android.app.action.DEVICE_ADMIN_ENABLED" , "android.permission.BIND_DEVICE_ADMIN" },
+ });
+
+ auto entry = map.find(action);
+ auto permission = permissions.find(action);
+ if (entry != map.end() && (permission == permissions.end()
+ || (receiver->permission && permission->second == *receiver->permission))) {
+ receiver->has_component = true;
+ component = entry->second;
+ }
+
+ } else if (ElementCast<Service>(parent_stack[1])) {
+ // Detects the presence of a particular type of service. If the action requires a
+ // permission, then the service element is checked for the permission.
+ Service* service = ElementCast<Service>(parent_stack[1]);
+ auto map = std::map<std::string, std::string>({
+ { "android.view.InputMethod" , "ime" },
+ { "android.service.wallpaper.WallpaperService" , "wallpaper" },
+ { "android.accessibilityservice.AccessibilityService" , "accessibility" },
+ { "android.printservice.PrintService" , "print-service" },
+ { "android.nfc.cardemulation.action.HOST_APDU_SERVICE" , "host-apdu" },
+ { "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE" , "offhost-apdu" },
+ { "android.service.notification.NotificationListenerService" ,"notification-listener" },
+ { "android.service.dreams.DreamService" , "dream" },
+ });
+
+ auto permissions = std::map<std::string, std::string>({
+ { "android.accessibilityservice.AccessibilityService" ,
+ "android.permission.BIND_ACCESSIBILITY_SERVICE" },
+ { "android.printservice.PrintService" , "android.permission.BIND_PRINT_SERVICE" },
+ { "android.nfc.cardemulation.action.HOST_APDU_SERVICE" ,
+ "android.permission.BIND_NFC_SERVICE" },
+ { "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE" ,
+ "android.permission.BIND_NFC_SERVICE" },
+ { "android.service.notification.NotificationListenerService" ,
+ "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" },
+ { "android.service.dreams.DreamService" , "android.permission.BIND_DREAM_SERVICE" },
+ });
+
+ auto entry = map.find(action);
+ auto permission = permissions.find(action);
+ if (entry != map.end() && (permission == permissions.end()
+ || (service->permission && permission->second == *service->permission))) {
+ service->has_component= true;
+ component = entry->second;
+ }
+
+ } else if (ElementCast<Provider>(parent_stack[1])) {
+ // Detects the presence of a particular type of receiver. If the provider requires a
+ // permission, then the provider element is checked for the permission.
+ // Detect whether this action
+ Provider* provider = ElementCast<Provider>(parent_stack[1]);
+ if (action == "android.content.action.DOCUMENTS_PROVIDER"
+ && provider->has_required_saf_attributes) {
+ component = "document-provider";
+ }
+ }
+ }
+
+ // Represents a searchable interface
+ if (action == "android.intent.action.SEARCH") {
+ component = "search";
+ }
+ }
+};
+
+/**
+ * Represents <supports-input> elements. The element may have <input-type> elements nested within.
+ **/
+class SupportsInput : public ManifestExtractor::Element {
+ public:
+ SupportsInput() = default;
+ std::vector<std::string> inputs;
+
+ void Print(text::Printer& printer) override {
+ const size_t size = inputs.size();
+ if (size > 0) {
+ printer.Print("supports-input: '");
+ for (size_t i = 0; i < size; i++) {
+ printer.Print(StringPrintf("value='%s' ", inputs[i].data()));
+ }
+ printer.Print("\n");
+ }
+ }
+};
+
+/** Represents <input-type> elements. **/
+class InputType : public ManifestExtractor::Element {
+ public:
+ InputType() = default;
+ void Extract(xml::Element* element) override {
+ auto name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ auto parent_stack = extractor()->parent_stack();
+
+ // Add the input to the set of supported inputs
+ if (name && ElementCast<SupportsInput>(parent_stack[0])) {
+ SupportsInput* supports = ElementCast<SupportsInput>(parent_stack[0]);
+ supports->inputs.push_back(*name);
+ }
+ }
+};
+
+/** Represents <original-package> elements. **/
+class OriginalPackage : public ManifestExtractor::Element {
+ public:
+ OriginalPackage() = default;
+ const std::string* name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("original-package:'%s'\n", name->data()));
+ }
+ }
+};
+
+/** * Represents <package-verifier> elements. **/
+class PackageVerifier : public ManifestExtractor::Element {
+ public:
+ PackageVerifier() = default;
+ const std::string* name = nullptr;
+ const std::string* public_key = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ public_key = GetAttributeString(FindAttribute(element, PUBLIC_KEY_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name && public_key) {
+ printer.Print(StringPrintf("package-verifier: name='%s' publicKey='%s'\n",
+ name->data(), public_key->data()));
+ }
+ }
+};
+
+/** Represents <uses-package> elements. **/
+class UsesPackage : public ManifestExtractor::Element {
+ public:
+ UsesPackage() = default;
+ const std::string* name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+ }
+ }
+};
+
+/** Represents <screen> elements found in <compatible-screens> elements. */
+class Screen : public ManifestExtractor::Element {
+ public:
+ Screen() = default;
+ const int32_t* size = nullptr;
+ const int32_t* density = nullptr;
+
+ void Extract(xml::Element* element) override {
+ size = GetAttributeInteger(FindAttribute(element, SCREEN_SIZE_ATTR));
+ density = GetAttributeInteger(FindAttribute(element, SCREEN_DENSITY_ATTR));
+ }
+};
+
+/**
+ * Represents <compatible-screens> elements. These elements have <screen> elements nested within
+ * that each denote a supported screen size and screen density.
+ **/
+class CompatibleScreens : public ManifestExtractor::Element {
+ public:
+ CompatibleScreens() = default;
+ void Print(text::Printer& printer) override {
+ printer.Print("compatible-screens:");
+
+ bool first = true;
+ ForEachChild(this, [&printer, &first](ManifestExtractor::Element* el){
+ if (auto screen = ElementCast<Screen>(el)) {
+ if (first) {
+ first = false;
+ } else {
+ printer.Print(",");
+ }
+
+ if (screen->size && screen->density) {
+ printer.Print(StringPrintf("'%d/%d'", *screen->size, *screen->density));
+ }
+ }
+ });
+ printer.Print("\n");
+ }
+};
+
+/** Represents <supports-gl-texture> elements. **/
+class SupportsGlTexture : public ManifestExtractor::Element {
+ public:
+ SupportsGlTexture() = default;
+ const std::string* name = nullptr;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+ }
+
+ void Print(text::Printer& printer) override {
+ if (name) {
+ printer.Print(StringPrintf("supports-gl-texture:'%s'\n", name->data()));
+ }
+ }
+};
+
+/** Recursively prints the extracted badging element. */
+static void Print(ManifestExtractor::Element* el, text::Printer& printer) {
+ el->Print(printer);
+ for (auto &child : el->children()) {
+ Print(child.get(), printer);
+ }
+}
+
+bool ManifestExtractor::Dump(text::Printer& printer, IDiagnostics* diag) {
+ // Load the manifest
+ std::unique_ptr<xml::XmlResource> doc = apk_->LoadXml("AndroidManifest.xml", diag);
+ if (doc == nullptr) {
+ diag->Error(DiagMessage() << "failed to find AndroidManifest.xml");
+ return false;
+ }
+
+ xml::Element* element = doc->root.get();
+ if (element->name != "manifest") {
+ diag->Error(DiagMessage() << "manifest does not start with <manifest> tag");
+ return false;
+ }
+
+ // Print only the <uses-permission>, <uses-permission-sdk23>, and <permission> elements if
+ // printing only permission elements is requested
+ if (options_.only_permissions) {
+ std::unique_ptr<ManifestExtractor::Element> manifest_element =
+ ManifestExtractor::Element::Inflate(this, element);
+
+ if (auto manifest = ElementCast<Manifest>(manifest_element.get())) {
+ for (xml::Element* child : element->GetChildElements()) {
+ if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
+ || child->name == "permission") {
+ auto permission_element = ManifestExtractor::Element::Inflate(this, child);
+ manifest->AddChild(permission_element);
+ }
+ }
+
+ printer.Print(StringPrintf("package: %s\n", manifest->package.data()));
+ ForEachChild(manifest, [&printer](ManifestExtractor::Element* el) -> void {
+ el->Print(printer);
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // Collect information about the resource configurations
+ if (apk_->GetResourceTable()) {
+ for (auto &package : apk_->GetResourceTable()->packages) {
+ for (auto &type : package->types) {
+ for (auto &entry : type->entries) {
+ for (auto &value : entry->values) {
+ std::string locale_str = value->config.GetBcp47LanguageTag();
+
+ // Collect all the unique locales of the apk
+ if (locales_.find(locale_str) == locales_.end()) {
+ ConfigDescription config = ManifestExtractor::DummyConfig();
+ config.setBcp47Locale(locale_str.data());
+ locales_.insert(std::make_pair(locale_str, config));
+ }
+
+ // Collect all the unique density of the apk
+ uint16_t density = (value->config.density == 0) ? (uint16_t) 160
+ : value->config.density;
+ if (densities_.find(density) == densities_.end()) {
+ ConfigDescription config = ManifestExtractor::DummyConfig();
+ config.density = density;
+ densities_.insert(std::make_pair(density, config));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Extract badging information
+ auto root = Visit(element);
+
+ // Print the elements in order seen
+ Print(root.get(), printer);
+
+ /** Recursively checks the extracted elements for the specified permission. **/
+ auto FindPermission = [&](ManifestExtractor::Element* root,
+ const std::string& name) -> ManifestExtractor::Element* {
+ return FindElement(root, [&](ManifestExtractor::Element* el) -> bool {
+ if (UsesPermission* permission = ElementCast<UsesPermission>(el)) {
+ return permission->name == name;
+ }
+ return false;
+ });
+ };
+
+ auto PrintPermission = [&printer](const std::string& name, const std::string& reason,
+ int32_t max_sdk_version) -> void {
+ auto permission = util::make_unique<UsesPermission>();
+ permission->name = name;
+ permission->maxSdkVersion = max_sdk_version;
+ permission->Print(printer);
+ permission->PrintImplied(printer, reason);
+ };
+
+ // Implied permissions
+ // Pre-1.6 implicitly granted permission compatibility logic
+ CommonFeatureGroup* common_feature_group = GetCommonFeatureGroup();
+ bool insert_write_external = false;
+ auto write_external_permission = ElementCast<UsesPermission>(
+ FindPermission(root.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
+
+ if (target_sdk() < 4) {
+ if (!write_external_permission) {
+ PrintPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
+ insert_write_external = true;
+ }
+
+ if (!FindPermission(root.get(), "android.permission.READ_PHONE_STATE")) {
+ PrintPermission("android.permission.READ_PHONE_STATE", "targetSdkVersion < 4", -1);
+ }
+ }
+
+ // If the application has requested WRITE_EXTERNAL_STORAGE, we will
+ // force them to always take READ_EXTERNAL_STORAGE as well. We always
+ // do this (regardless of target API version) because we can't have
+ // an app with write permission but not read permission.
+ auto read_external = FindPermission(root.get(), "android.permission.READ_EXTERNAL_STORAGE");
+ if (!read_external && (insert_write_external || write_external_permission)) {
+ PrintPermission("android.permission.READ_EXTERNAL_STORAGE",
+ "requested WRITE_EXTERNAL_STORAGE",
+ (write_external_permission) ? write_external_permission->maxSdkVersion : -1);
+ }
+
+ // Pre-JellyBean call log permission compatibility.
+ if (target_sdk() < 16) {
+ if (!FindPermission(root.get(), "android.permission.READ_CALL_LOG")
+ && FindPermission(root.get(), "android.permission.READ_CONTACTS")) {
+ PrintPermission("android.permission.READ_CALL_LOG",
+ "targetSdkVersion < 16 and requested READ_CONTACTS", -1);
+ }
+
+ if (!FindPermission(root.get(), "android.permission.WRITE_CALL_LOG")
+ && FindPermission(root.get(), "android.permission.WRITE_CONTACTS")) {
+ PrintPermission("android.permission.WRITE_CALL_LOG",
+ "targetSdkVersion < 16 and requested WRITE_CONTACTS", -1);
+ }
+ }
+
+ // If the app hasn't declared the touchscreen as a feature requirement (either
+ // directly or implied, required or not), then the faketouch feature is implied.
+ if (!common_feature_group->HasFeature("android.hardware.touchscreen")) {
+ common_feature_group->addImpliedFeature("android.hardware.faketouch",
+ "default feature for all apps", false);
+ }
+
+ // Only print the common feature group if no feature group is defined
+ std::vector<FeatureGroup*> feature_groups;
+ ForEachChild(root.get(), [&feature_groups](ManifestExtractor::Element* el) -> void {
+ if (auto feature_group = ElementCast<FeatureGroup>(el)) {
+ feature_groups.push_back(feature_group);
+ }
+ });
+
+ if (feature_groups.empty()) {
+ common_feature_group->PrintGroup(printer);
+ } else {
+ // Merge the common feature group into the feature group
+ for (auto& feature_group : feature_groups) {
+ feature_group->open_gles_version = std::max(feature_group->open_gles_version,
+ common_feature_group->open_gles_version);
+ feature_group->Merge(common_feature_group);
+ feature_group->PrintGroup(printer);
+ }
+ };
+
+ // Collect the component types of the application
+ std::set<std::string> components;
+ ForEachChild(root.get(), [&components](ManifestExtractor::Element* el) -> void {
+ if (ElementCast<Action>(el)) {
+ auto action = ElementCast<Action>(el);
+ if (!action->component.empty()) {
+ components.insert(action->component);
+ return;
+ }
+ }
+
+ if (ElementCast<Category>(el)) {
+ auto category = ElementCast<Category>(el);
+ if (!category->component.empty()) {
+ components.insert(category->component);
+ return;
+ }
+ }
+ });
+
+ // Check for the payment component
+ auto apk = apk_;
+ ForEachChild(root.get(), [&apk, &components, &diag](ManifestExtractor::Element* el) -> void {
+ if (auto service = ElementCast<Service>(el)) {
+ auto host_apdu_action = ElementCast<Action>(FindElement(service,
+ [&](ManifestExtractor::Element* el) -> bool {
+ if (auto action = ElementCast<Action>(el)) {
+ return (action->component == "host-apdu");
+ }
+ return false;
+ }));
+
+ auto offhost_apdu_action = ElementCast<Action>(FindElement(service,
+ [&](ManifestExtractor::Element* el) -> bool {
+ if (auto action = ElementCast<Action>(el)) {
+ return (action->component == "offhost-apdu");
+ }
+ return false;
+ }));
+
+ ForEachChild(service, [&apk, &components, &diag, &host_apdu_action,
+ &offhost_apdu_action](ManifestExtractor::Element* el) -> void {
+ if (auto meta_data = ElementCast<MetaData>(el)) {
+ if ((meta_data->name == "android.nfc.cardemulation.host_apdu_service" && host_apdu_action)
+ || (meta_data->name == "android.nfc.cardemulation.off_host_apdu_service"
+ && offhost_apdu_action)) {
+
+ // Attempt to load the resource file
+ if (!meta_data->resource) {
+ return;
+ }
+ auto resource = apk->LoadXml(*meta_data->resource, diag);
+ if (!resource) {
+ return;
+ }
+
+ // Look for the payment category on an <aid-group> element
+ auto& root = resource.get()->root;
+ if ((host_apdu_action && root->name == "host-apdu-service")
+ || (offhost_apdu_action && root->name == "offhost-apdu-service")) {
+
+ for (auto& child : root->GetChildElements()) {
+ if (child->name == "aid-group") {
+ auto category = FindAttribute(child, CATEGORY_ATTR);
+ if (category && category->value == "payment") {
+ components.insert("payment");
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+ });
+
+ // Print the components types if they are present
+ auto PrintComponent = [&components, &printer](const std::string& component) -> void {
+ if (components.find(component) != components.end()) {
+ printer.Print(StringPrintf("provides-component:'%s'\n", component.data()));
+ }
+ };
+
+ PrintComponent("app-widget");
+ PrintComponent("device-admin");
+ PrintComponent("ime");
+ PrintComponent("wallpaper");
+ PrintComponent("accessibility");
+ PrintComponent("print-service");
+ PrintComponent("payment");
+ PrintComponent("search");
+ PrintComponent("document-provider");
+ PrintComponent("launcher");
+ PrintComponent("notification-listener");
+ PrintComponent("dream");
+ PrintComponent("camera");
+ PrintComponent("camera-secure");
+
+ // Print presence of main activity
+ if (components.find("main") != components.end()) {
+ printer.Print("main\n");
+ }
+
+ // Print presence of activities, recivers, and services with no special components
+ FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+ if (auto activity = ElementCast<Activity>(el)) {
+ if (!activity->has_component_) {
+ printer.Print("other-activities\n");
+ return true;
+ }
+ }
+ return false;
+ });
+
+ FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+ if (auto receiver = ElementCast<Receiver>(el)) {
+ if (!receiver->has_component) {
+ printer.Print("other-receivers\n");
+ return true;
+ }
+ }
+ return false;
+ });
+
+ FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+ if (auto service = ElementCast<Service>(el)) {
+ if (!service->has_component) {
+ printer.Print("other-services\n");
+ return true;
+ }
+ }
+ return false;
+ });
+
+ // Print the supported screens
+ SupportsScreen* screen = ElementCast<SupportsScreen>(FindElement(root.get(),
+ [&](ManifestExtractor::Element* el) -> bool {
+ return ElementCast<SupportsScreen>(el) != nullptr;
+ }));
+
+ if (screen) {
+ screen->PrintScreens(printer, target_sdk_);
+ } else {
+ // Print the default supported screens
+ SupportsScreen default_screens;
+ default_screens.PrintScreens(printer, target_sdk_);
+ }
+
+ // Print all the unique locales of the apk
+ printer.Print("locales:");
+ for (auto& config : locales_) {
+ if (config.first.empty()) {
+ printer.Print(" '--_--'");
+ } else {
+ printer.Print(StringPrintf(" '%s'", config.first.data()));
+ }
+ }
+ printer.Print("\n");
+
+ // Print all the densities locales of the apk
+ printer.Print("densities:");
+ for (auto& config : densities_) {
+ printer.Print(StringPrintf(" '%d'", config.first));
+ }
+ printer.Print("\n");
+
+ // Print the supported architectures of the app
+ std::set<std::string> architectures;
+ auto it = apk_->GetFileCollection()->Iterator();
+ while (it->HasNext()) {
+ auto file_path = it->Next()->GetSource().path;
+
+
+ size_t pos = file_path.find("lib/");
+ if (pos != std::string::npos) {
+ file_path = file_path.substr(pos + 4);
+ pos = file_path.find("/");
+ if (pos != std::string::npos) {
+ file_path = file_path.substr(0, pos);
+ }
+
+ architectures.insert(file_path);
+ }
+ }
+
+ // Determine if the application has multiArch supports
+ auto has_multi_arch = FindElement(root.get(), [&](ManifestExtractor::Element* el) -> bool {
+ if (auto application = ElementCast<Application>(el)) {
+ return application->has_multi_arch;
+ }
+ return false;
+ });
+
+ bool output_alt_native_code = false;
+ // A multiArch package is one that contains 64-bit and
+ // 32-bit versions of native code and expects 3rd-party
+ // apps to load these native code libraries. Since most
+ // 64-bit systems also support 32-bit apps, the apps
+ // loading this multiArch package's code may be either
+ if (has_multi_arch) {
+ // If this is a multiArch package, report the 64-bit
+ // version only. Then as a separate entry, report the
+ // rest.
+ //
+ // If we report the 32-bit architecture, this APK will
+ // be installed on a 32-bit device, causing a large waste
+ // of bandwidth and disk space. This assumes that
+ // the developer of the multiArch package has also
+ // made a version that is 32-bit only.
+ const std::string kIntel64 = "x86_64";
+ const std::string kArm64 = "arm64-v8a";
+
+ auto arch = architectures.find(kIntel64);
+ if (arch == architectures.end()) {
+ arch = architectures.find(kArm64);
+ }
+
+ if (arch != architectures.end()) {
+ printer.Print(StringPrintf("native-code: '%s'\n", arch->data()));
+ architectures.erase(arch);
+ output_alt_native_code = true;
+ }
+ }
+
+ if (architectures.size() > 0) {
+ if (output_alt_native_code) {
+ printer.Print("alt-");
+ }
+ printer.Print("native-code:");
+ for (auto& arch : architectures) {
+ printer.Print(StringPrintf(" '%s'", arch.data()));
+ }
+ printer.Print("\n");
+ }
+
+ return true;
+}
+
+/**
+ * Returns the element casted to the type if the element is of that type. Otherwise, returns a null
+ * pointer.
+ **/
+template<typename T>
+T* ElementCast(ManifestExtractor::Element* element) {
+ if (element == nullptr) {
+ return nullptr;
+ }
+
+ const std::unordered_map<std::string, bool> kTagCheck = {
+ {"action", std::is_base_of<Action, T>::value},
+ {"activity", std::is_base_of<Activity, T>::value},
+ {"application", std::is_base_of<Application, T>::value},
+ {"category", std::is_base_of<Category, T>::value},
+ {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
+ {"feature-group", std::is_base_of<FeatureGroup, T>::value},
+ {"input-type", std::is_base_of<InputType, T>::value},
+ {"intent-filter", std::is_base_of<IntentFilter, T>::value},
+ {"meta-data", std::is_base_of<MetaData, T>::value},
+ {"manifest", std::is_base_of<Manifest, T>::value},
+ {"original-package", std::is_base_of<OriginalPackage, T>::value},
+ {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
+ {"permission", std::is_base_of<Permission, T>::value},
+ {"provider", std::is_base_of<Provider, T>::value},
+ {"receiver", std::is_base_of<Receiver, T>::value},
+ {"screen", std::is_base_of<Screen, T>::value},
+ {"service", std::is_base_of<Service, T>::value},
+ {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
+ {"supports-input", std::is_base_of<SupportsInput, T>::value},
+ {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
+ {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
+ {"uses-feature", std::is_base_of<UsesFeature, T>::value},
+ {"uses-permission", std::is_base_of<UsesPermission, T>::value},
+ {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
+ {"uses-library", std::is_base_of<UsesLibrary, T>::value},
+ {"uses-package", std::is_base_of<UsesPackage, T>::value},
+ {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ };
+
+ auto check = kTagCheck.find(element->tag());
+ if (check != kTagCheck.end() && check->second) {
+ return static_cast<T*>(element);
+ }
+ return nullptr;
+}
+
+template<typename T>
+std::unique_ptr<T> CreateType() {
+ return std::move(util::make_unique<T>());
+}
+
+std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
+ ManifestExtractor* extractor, xml::Element* el) {
+ const std::unordered_map<std::string,
+ std::function<std::unique_ptr<ManifestExtractor::Element>()>>
+ kTagCheck = {
+ {"action", &CreateType<Action>},
+ {"activity", &CreateType<Activity>},
+ {"application", &CreateType<Application>},
+ {"category", &CreateType<Category>},
+ {"compatible-screens", &CreateType<CompatibleScreens>},
+ {"feature-group", &CreateType<FeatureGroup>},
+ {"input-type", &CreateType<InputType>},
+ {"intent-filter",&CreateType<IntentFilter>},
+ {"manifest", &CreateType<Manifest>},
+ {"meta-data", &CreateType<MetaData>},
+ {"original-package", &CreateType<OriginalPackage>},
+ {"package-verifier", &CreateType<PackageVerifier>},
+ {"permission", &CreateType<Permission>},
+ {"provider", &CreateType<Provider>},
+ {"receiver", &CreateType<Receiver>},
+ {"screen", &CreateType<Screen>},
+ {"service", &CreateType<Service>},
+ {"supports-gl-texture", &CreateType<SupportsGlTexture>},
+ {"supports-input", &CreateType<SupportsInput>},
+ {"supports-screens", &CreateType<SupportsScreen>},
+ {"uses-configuration", &CreateType<UsesConfiguarion>},
+ {"uses-feature", &CreateType<UsesFeature>},
+ {"uses-permission", &CreateType<UsesPermission>},
+ {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
+ {"uses-library", &CreateType<UsesLibrary>},
+ {"uses-package", &CreateType<UsesPackage>},
+ {"uses-sdk", &CreateType<UsesSdkBadging>},
+ };
+
+ // Attempt to map the xml tag to a element inflater
+ std::unique_ptr<ManifestExtractor::Element> element;
+ auto check = kTagCheck.find(el->name);
+ if (check != kTagCheck.end()) {
+ element = check->second();
+ } else {
+ element = util::make_unique<ManifestExtractor::Element>();
+ }
+
+ element->extractor_ = extractor;
+ element->tag_ = el->name;
+ element->Extract(el);
+ return element;
+}
+
+std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Visit(xml::Element* el) {
+ auto element = ManifestExtractor::Element::Inflate(this, el);
+ parent_stack_.insert(parent_stack_.begin(), element.get());
+
+ // Process the element and recursively visit the children
+ for (xml::Element* child : el->GetChildElements()) {
+ auto v = Visit(child);
+ element->AddChild(v);
+ }
+
+ parent_stack_.erase(parent_stack_.begin());
+ return element;
+}
+
+// Use a smaller buffer so that there is less latency for dumping to stdout.
+constexpr size_t kStdOutBufferSize = 1024u;
+
+int DumpBadgingCommand::Action(const std::vector<std::string>& args) {
+ if (args.size() < 1) {
+ diag_->Error(DiagMessage() << "No dump apk specified.");
+ return 1;
+ }
+
+ ManifestExtractor::Options options;
+ options.include_meta_data = include_metadata_;
+
+ io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+ text::Printer printer(&fout);
+ for (auto apk : args) {
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
+ if (!loaded_apk) {
+ return 1;
+ }
+
+ ManifestExtractor extractor(loaded_apk.get(), options);
+ extractor.Dump(printer, diag_);
+ }
+
+ return 0;
+}
+
+int DumpPermissionsCommand::Action(const std::vector<std::string>& args) {
+ if (args.size() < 1) {
+ diag_->Error(DiagMessage() << "No dump apk specified.");
+ return 1;
+ }
+
+ ManifestExtractor::Options options;
+ options.only_permissions = true;
+
+ io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+ text::Printer printer(&fout);
+ for (auto apk : args) {
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
+ if (!loaded_apk) {
+ return 1;
+ }
+
+ ManifestExtractor extractor(loaded_apk.get(), options);
+ extractor.Dump(printer, diag_);
+ }
+
+ return 0;
+}
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/dump/DumpManifest.h b/tools/aapt2/dump/DumpManifest.h
new file mode 100644
index 0000000..a70be53
--- /dev/null
+++ b/tools/aapt2/dump/DumpManifest.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT2_DUMP_MANIFEST_H
+#define AAPT2_DUMP_MANIFEST_H
+
+#include <Diagnostics.h>
+#include <ValueVisitor.h>
+#include <io/ZipArchive.h>
+
+
+#include "cmd/Command.h"
+#include "process/IResourceTableConsumer.h"
+#include "text/Printer.h"
+#include "xml/XmlDom.h"
+
+namespace aapt {
+
+class DumpBadgingCommand : public Command {
+ public:
+ explicit DumpBadgingCommand(IDiagnostics* diag) : Command("badging"), diag_(diag) {
+ SetDescription("Print information extracted from the manifest of the APK.");
+ AddOptionalSwitch("--include-meta-data", "Include meta-data information.",
+ &include_metadata_);
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ IDiagnostics* diag_;
+ bool include_metadata_ = false;
+};
+
+class DumpPermissionsCommand : public Command {
+ public:
+ explicit DumpPermissionsCommand(IDiagnostics* diag) : Command("permissions"), diag_(diag) {
+ SetDescription("Print the permissions extracted from the manifest of the APK.");
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ IDiagnostics* diag_;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DUMP_MANIFEST_H
\ No newline at end of file
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index b07fb53..5f978a8 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -20,6 +20,7 @@
#include <string>
#include "google/protobuf/message_lite.h"
+#include "google/protobuf/io/coded_stream.h"
#include "format/Archive.h"
#include "io/File.h"
@@ -122,6 +123,23 @@
io::InputStream* in_;
};
+class ProtoInputStreamReader {
+ public:
+ explicit ProtoInputStreamReader(io::InputStream* in) : in_(in) { }
+
+ /** Deserializes a MessageLite proto from the current position in the input stream.*/
+ template <typename T> bool ReadMessage(T *message_lite) {
+ ZeroCopyInputAdaptor adapter(in_);
+ google::protobuf::io::CodedInputStream coded_stream(&adapter);
+ coded_stream.SetTotalBytesLimit(std::numeric_limits<int32_t>::max(),
+ coded_stream.BytesUntilTotalBytesLimit());
+ return message_lite->ParseFromCodedStream(&coded_stream);
+ }
+
+ private:
+ io::InputStream* in_;
+};
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index c5c78d9..fa6538d 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -252,6 +252,7 @@
xml::XmlNodeAction component_action;
component_action.Action(RequiredNameIsJavaClassName);
component_action["intent-filter"] = intent_filter_action;
+ component_action["preferred"] = intent_filter_action;
component_action["meta-data"] = meta_data_action;
// Manifest actions.
@@ -414,6 +415,8 @@
application_action["provider"]["grant-uri-permission"];
application_action["provider"]["path-permission"];
+ manifest_action["package"] = manifest_action;
+
return true;
}
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index e991743..b5c33062 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -154,6 +154,12 @@
bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
bool error = false;
for (size_t i = 0; i < split_constraints_.size(); i++) {
+ if (split_constraints_[i].configs.size() == 0) {
+ // For now, treat this as a warning. We may consider aborting processing.
+ context->GetDiagnostics()->Warn(DiagMessage()
+ << "no configurations for constraint '"
+ << split_constraints_[i].name << "'");
+ }
for (size_t j = i + 1; j < split_constraints_.size(); j++) {
for (const ConfigDescription& config : split_constraints_[i].configs) {
if (split_constraints_[j].configs.find(config) != split_constraints_[j].configs.end()) {
diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h
index 6aec257..ed24bc39 100644
--- a/tools/aapt2/split/TableSplitter.h
+++ b/tools/aapt2/split/TableSplitter.h
@@ -30,6 +30,7 @@
struct SplitConstraints {
std::set<ConfigDescription> configs;
+ std::string name;
};
struct TableSplitterOptions {
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index fd5262a..be6e510 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -204,6 +204,112 @@
configuration::PostProcessingConfiguration config_;
};
+class ConfigDescriptionBuilder {
+ public:
+ ConfigDescriptionBuilder() = default;
+
+ ConfigDescriptionBuilder& setMcc(uint16_t mcc) {
+ config_.mcc = mcc;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setMnc(uint16_t mnc) {
+ config_.mnc = mnc;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setLanguage(uint16_t language) {
+ config_.language[0] = language >> 8;
+ config_.language[1] = language & 0xff;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setCountry(uint16_t country) {
+ config_.country[0] = country >> 8;
+ config_.country[1] = country & 0xff;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setOrientation(uint8_t orientation) {
+ config_.orientation = orientation;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setTouchscreen(uint8_t touchscreen) {
+ config_.touchscreen = touchscreen;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setDensity(uint16_t density) {
+ config_.density = density;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setKeyboard(uint8_t keyboard) {
+ config_.keyboard = keyboard;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setNavigation(uint8_t navigation) {
+ config_.navigation = navigation;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setInputFlags(uint8_t inputFlags) {
+ config_.inputFlags = inputFlags;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setInputPad0(uint8_t inputPad0) {
+ config_.inputPad0 = inputPad0;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenWidth(uint16_t screenWidth) {
+ config_.screenWidth = screenWidth;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenHeight(uint16_t screenHeight) {
+ config_.screenHeight = screenHeight;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setSdkVersion(uint16_t sdkVersion) {
+ config_.sdkVersion = sdkVersion;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setMinorVersion(uint16_t minorVersion) {
+ config_.minorVersion = minorVersion;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenLayout(uint8_t screenLayout) {
+ config_.screenLayout = screenLayout;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setUiMode(uint8_t uiMode) {
+ config_.uiMode = uiMode;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setSmallestScreenWidthDp(uint16_t smallestScreenWidthDp) {
+ config_.smallestScreenWidthDp = smallestScreenWidthDp;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenWidthDp(uint16_t screenWidthDp) {
+ config_.screenWidthDp = screenWidthDp;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenHeightDp(uint16_t screenHeightDp) {
+ config_.screenHeightDp = screenHeightDp;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenLayout2(uint8_t screenLayout2) {
+ config_.screenLayout2 = screenLayout2;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setColorMode(uint8_t colorMode) {
+ config_.colorMode = colorMode;
+ return *this;
+ }
+ ConfigDescriptionBuilder& setScreenConfigPad2(uint16_t screenConfigPad2) {
+ config_.screenConfigPad2 = screenConfigPad2;
+ return *this;
+ }
+ ConfigDescription Build() {
+ return config_;
+ }
+
+ private:
+ ConfigDescription config_;
+};
+
} // namespace test
} // namespace aapt
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 9bef54e5..59b7fff 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -322,11 +322,11 @@
output.reserve(modified_size);
for (size_t i = 0; i < size; i++) {
if (((uint8_t) utf8[i] >> 4) == 0xF) {
- auto codepoint = (char32_t) utf32_from_utf8_at(utf8.data(), size, i, nullptr);
+ int32_t codepoint = utf32_from_utf8_at(utf8.data(), size, i, nullptr);
// Calculate the high and low surrogates as UTF-16 would
- char32_t high = ((codepoint - 0x10000) / 0x400) + 0xD800;
- char32_t low = ((codepoint - 0x10000) % 0x400) + 0xDC00;
+ int32_t high = ((codepoint - 0x10000) / 0x400) + 0xD800;
+ int32_t low = ((codepoint - 0x10000) % 0x400) + 0xDC00;
// Encode each surrogate in UTF-8
output.push_back((char) (0xE4 | ((high >> 12) & 0xF)));
@@ -344,6 +344,60 @@
return output;
}
+std::string ModifiedUtf8ToUtf8(const std::string& modified_utf8) {
+ // The UTF-8 representation will have a byte length less than or equal to the Modified UTF-8
+ // representation.
+ std::string output;
+ output.reserve(modified_utf8.size());
+
+ size_t index = 0;
+ const size_t modified_size = modified_utf8.size();
+ while (index < modified_size) {
+ size_t next_index;
+ int32_t high_surrogate = utf32_from_utf8_at(modified_utf8.data(), modified_size, index,
+ &next_index);
+ if (high_surrogate < 0) {
+ return {};
+ }
+
+ // Check that the first codepoint is within the high surrogate range
+ if (high_surrogate >= 0xD800 && high_surrogate <= 0xDB7F) {
+ int32_t low_surrogate = utf32_from_utf8_at(modified_utf8.data(), modified_size, next_index,
+ &next_index);
+ if (low_surrogate < 0) {
+ return {};
+ }
+
+ // Check that the second codepoint is within the low surrogate range
+ if (low_surrogate >= 0xDC00 && low_surrogate <= 0xDFFF) {
+ const char32_t codepoint = (char32_t) (((high_surrogate - 0xD800) * 0x400)
+ + (low_surrogate - 0xDC00) + 0x10000);
+
+ // The decoded codepoint should represent a 4 byte, UTF-8 character
+ const size_t utf8_length = (size_t) utf32_to_utf8_length(&codepoint, 1);
+ if (utf8_length != 4) {
+ return {};
+ }
+
+ // Encode the UTF-8 representation of the codepoint into the string
+ char* start = &output[output.size()];
+ output.resize(output.size() + utf8_length);
+ utf32_to_utf8((char32_t*) &codepoint, 1, start, utf8_length + 1);
+
+ index = next_index;
+ continue;
+ }
+ }
+
+ // Append non-surrogate pairs to the output string
+ for (size_t i = index; i < next_index; i++) {
+ output.push_back(modified_utf8[i]);
+ }
+ index = next_index;
+ }
+ return output;
+}
+
std::u16string Utf8ToUtf16(const StringPiece& utf8) {
ssize_t utf16_length = utf8_to_utf16_length(
reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
@@ -469,7 +523,7 @@
size_t len;
const char* str = pool.string8At(idx, &len);
if (str != nullptr) {
- return std::string(str, len);
+ return ModifiedUtf8ToUtf8(std::string(str, len));
}
return Utf16ToUtf8(GetString16(pool, idx));
}
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 36b7333..c6e8e6e 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -199,6 +199,7 @@
// Converts a UTF8 string into Modified UTF8
std::string Utf8ToModifiedUtf8(const std::string& utf8);
+std::string ModifiedUtf8ToUtf8(const std::string& modified_utf8);
// Converts a UTF8 string to a UTF16 string.
std::u16string Utf8ToUtf16(const android::StringPiece& utf8);
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 018e9c9..934847f 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -697,8 +697,8 @@
"android.provider",
["android.content","android.graphics.drawable"],
"android.database",
- "android.graphics",
"android.text",
+ "android.graphics",
"android.os",
"android.util"
]
diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh
index bdcc807..76a2f2d 100755
--- a/tools/hiddenapi/sort_api.sh
+++ b/tools/hiddenapi/sort_api.sh
@@ -12,8 +12,8 @@
# Sort
IFS=$'\n'
# Stash away comments
-C=( $(grep -E '^#' <<< "${A[*]}") )
-A=( $(grep -v -E '^#' <<< "${A[*]}") )
+C=( $(grep -E '^#' <<< "${A[*]}" || :) )
+A=( $(grep -v -E '^#' <<< "${A[*]}" || :) )
# Sort entries
A=( $(LC_COLLATE=C sort -f <<< "${A[*]}") )
A=( $(uniq <<< "${A[*]}") )
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index bb02dec..b4dfac6 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -127,7 +127,7 @@
public static final int OSU_STATUS_SERVICE_PROVIDER_VERIFIED = 5;
/**
- * The status code for provisioning flow to indicate starting the SOAP exchange.
+ * The status code for provisioning flow to indicate starting the first SOAP exchange.
*/
public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6;
@@ -142,6 +142,11 @@
public static final int OSU_STATUS_REDIRECT_RESPONSE_RECEIVED = 8;
/**
+ * The status code for provisioning flow to indicate starting the second SOAP exchange.
+ */
+ public static final int OSU_STATUS_SECOND_SOAP_EXCHANGE = 9;
+
+ /**
* Provisioning status for OSU failure
*
* @param status indicates error condition
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 57f3973..5a4c898 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -30,6 +30,7 @@
import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceResponse;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -488,7 +489,7 @@
* @hide - hide this because it takes in a parameter of type IWifiP2pManager, which
* is a system private class.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public WifiP2pManager(IWifiP2pManager service) {
mService = service;
}