Merge "Fix notifications showing on keyguard"
diff --git a/Android.bp b/Android.bp
index aeb3ceb..1c59e2b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1084,53 +1084,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 +1154,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,23 +1193,13 @@
],
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"
-
-doc_defaults {
+stubs_defaults {
name: "metalava-api-stubs-default",
srcs: [
":opt-telephony-srcs",
@@ -1246,30 +1224,58 @@
],
local_sourcepaths: frameworks_base_subdirs,
installable: false,
- metalava_enabled: true,
- metalava_annotations_enabled: true,
- metalava_previous_api: ":last-released-public-api",
- metalava_merge_annotations_dirs: [
+ annotations_enabled: true,
+ 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",
}
@@ -1277,11 +1283,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",
@@ -1290,13 +1299,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",
@@ -1305,12 +1316,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 " +
@@ -1331,14 +1345,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 " +
@@ -1364,12 +1380,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 " +
@@ -1390,11 +1409,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 " +
@@ -1404,11 +1426,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 ",
@@ -1417,12 +1442,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 " +
@@ -1443,13 +1471,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",
@@ -1467,10 +1498,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 {
@@ -1481,7 +1517,7 @@
],
}
-droiddoc {
+droidstubs {
name: "hiddenapi-lists-docs",
defaults: ["metalava-api-stubs-default"],
arg_files: [
@@ -1497,23 +1533,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 {
@@ -1540,7 +1570,7 @@
],
}
-droiddoc {
+droidstubs {
name: "api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
api_filename: "public_api.txt",
@@ -1560,9 +1590,10 @@
removed_api_file: "api/removed.txt",
},
},
+ jdiff_enabled: true,
}
-droiddoc {
+droidstubs {
name: "system-api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
api_tag_name: "SYSTEM",
@@ -1584,9 +1615,10 @@
removed_api_file: "api/system-removed.txt",
},
},
+ jdiff_enabled: true,
}
-droiddoc {
+droidstubs {
name: "test-api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
api_tag_name: "TEST",
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 e31f8a0..90ceee6 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -934,6 +934,7 @@
field public static final int minSdkVersion = 16843276; // 0x101020c
field public static final int minWidth = 16843071; // 0x101013f
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
+ field public static final int minimumUiTimeout = 16844174; // 0x101058e
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
field public static final int mirrorForRtl = 16843726; // 0x10103ce
@@ -2874,10 +2875,12 @@
method public int getCapabilities();
method public deprecated java.lang.String getDescription();
method public java.lang.String getId();
+ method public int getMinimumUiTimeoutMillis();
method public android.content.pm.ResolveInfo getResolveInfo();
method public java.lang.String getSettingsActivityName();
method public java.lang.String loadDescription(android.content.pm.PackageManager);
method public java.lang.CharSequence loadSummary(android.content.pm.PackageManager);
+ method public void setMinimumUiTimeoutMillis(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
@@ -2902,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;
@@ -4382,6 +4386,7 @@
public final class AutomaticZenRule implements android.os.Parcelable {
ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
+ ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, android.service.notification.ZenPolicy, boolean);
ctor public AutomaticZenRule(android.os.Parcel);
method public int describeContents();
method public android.net.Uri getConditionId();
@@ -4389,11 +4394,13 @@
method public int getInterruptionFilter();
method public java.lang.String getName();
method public android.content.ComponentName getOwner();
+ method public android.service.notification.ZenPolicy getZenPolicy();
method public boolean isEnabled();
method public void setConditionId(android.net.Uri);
method public void setEnabled(boolean);
method public void setInterruptionFilter(int);
method public void setName(java.lang.String);
+ method public void setZenPolicy(android.service.notification.ZenPolicy);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
}
@@ -5574,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();
@@ -5590,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);
@@ -5609,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
}
@@ -7727,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();
@@ -8095,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();
@@ -8511,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 {
@@ -9337,6 +9349,7 @@
ctor public ContentUris();
method public static android.net.Uri.Builder appendId(android.net.Uri.Builder, long);
method public static long parseId(android.net.Uri);
+ method public static android.net.Uri removeId(android.net.Uri);
method public static android.net.Uri withAppendedId(android.net.Uri, long);
}
@@ -11925,6 +11938,7 @@
method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
+ method public float getFloat(int);
method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException;
method public float getFraction(int, int, int);
method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
@@ -12652,7 +12666,7 @@
ctor public SQLiteMisuseException(java.lang.String);
}
- public abstract class SQLiteOpenHelper {
+ public abstract class SQLiteOpenHelper implements java.lang.AutoCloseable {
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams);
@@ -14001,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();
@@ -14072,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
@@ -16030,6 +16053,7 @@
method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys();
method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys();
method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys();
+ method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeysNeedingPermission();
method public java.util.Set<java.lang.String> getPhysicalCameraIds();
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
@@ -18666,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();
@@ -19331,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);
@@ -36585,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 {
@@ -36690,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";
}
@@ -39562,6 +39641,7 @@
method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int);
method public final void cancelNotification(java.lang.String);
method public final void cancelNotifications(java.lang.String[]);
+ method public final void clearRequestedListenerHints();
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
method public final int getCurrentInterruptionFilter();
@@ -39677,6 +39757,61 @@
field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
}
+ public final class ZenPolicy implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getPriorityCallSenders();
+ method public int getPriorityCategoryAlarms();
+ method public int getPriorityCategoryCalls();
+ method public int getPriorityCategoryEvents();
+ method public int getPriorityCategoryMedia();
+ method public int getPriorityCategoryMessages();
+ method public int getPriorityCategoryReminders();
+ method public int getPriorityCategoryRepeatCallers();
+ method public int getPriorityCategorySystem();
+ method public int getPriorityMessageSenders();
+ method public int getVisualEffectAmbient();
+ method public int getVisualEffectBadge();
+ method public int getVisualEffectFullScreenIntent();
+ method public int getVisualEffectLights();
+ method public int getVisualEffectNotificationList();
+ method public int getVisualEffectPeek();
+ method public int getVisualEffectStatusBar();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.ZenPolicy> CREATOR;
+ field public static final int PEOPLE_TYPE_ANYONE = 1; // 0x1
+ field public static final int PEOPLE_TYPE_CONTACTS = 2; // 0x2
+ field public static final int PEOPLE_TYPE_NONE = 4; // 0x4
+ field public static final int PEOPLE_TYPE_STARRED = 3; // 0x3
+ field public static final int PEOPLE_TYPE_UNSET = 0; // 0x0
+ field public static final int STATE_ALLOW = 1; // 0x1
+ field public static final int STATE_DISALLOW = 2; // 0x2
+ field public static final int STATE_UNSET = 0; // 0x0
+ }
+
+ public static class ZenPolicy.Builder {
+ ctor public ZenPolicy.Builder();
+ method public android.service.notification.ZenPolicy.Builder allowAlarms(boolean);
+ method public android.service.notification.ZenPolicy.Builder allowAllSounds();
+ method public android.service.notification.ZenPolicy.Builder allowCalls(int);
+ method public android.service.notification.ZenPolicy.Builder allowEvents(boolean);
+ method public android.service.notification.ZenPolicy.Builder allowMedia(boolean);
+ method public android.service.notification.ZenPolicy.Builder allowMessages(int);
+ method public android.service.notification.ZenPolicy.Builder allowReminders(boolean);
+ method public android.service.notification.ZenPolicy.Builder allowRepeatCallers(boolean);
+ method public android.service.notification.ZenPolicy.Builder allowSystem(boolean);
+ method public android.service.notification.ZenPolicy build();
+ method public android.service.notification.ZenPolicy.Builder disallowAllSounds();
+ method public android.service.notification.ZenPolicy.Builder hideAllVisualEffects();
+ method public android.service.notification.ZenPolicy.Builder showAllVisualEffects();
+ method public android.service.notification.ZenPolicy.Builder showBadges(boolean);
+ method public android.service.notification.ZenPolicy.Builder showFullScreenIntent(boolean);
+ method public android.service.notification.ZenPolicy.Builder showInAmbientDisplay(boolean);
+ method public android.service.notification.ZenPolicy.Builder showInNotificationList(boolean);
+ method public android.service.notification.ZenPolicy.Builder showLights(boolean);
+ method public android.service.notification.ZenPolicy.Builder showPeeking(boolean);
+ method public android.service.notification.ZenPolicy.Builder showStatusBarIcons(boolean);
+ }
+
}
package android.service.quicksettings {
@@ -42654,6 +42789,7 @@
method public static int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isNetworkRoaming(int);
+ method public static boolean isValidSubscriptionId(int);
method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public void setSubscriptionOverrideCongested(int, boolean, long);
@@ -49546,6 +49682,7 @@
method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
+ method public int getMinimumUiTimeoutMillis();
method public void interrupt();
method public static boolean isAccessibilityButtonSupported();
method public boolean isEnabled();
@@ -49629,6 +49766,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);
@@ -49690,6 +49828,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 1e1c621..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
@@ -1108,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
@@ -3807,6 +3818,14 @@
field public static final java.lang.String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
}
+ public class Environment {
+ method public static java.io.File getOdmDirectory();
+ method public static java.io.File getOemDirectory();
+ method public static java.io.File getProductDirectory();
+ method public static java.io.File getProductServicesDirectory();
+ method public static java.io.File getVendorDirectory();
+ }
+
public class HidlSupport {
method public static boolean deepEquals(java.lang.Object, java.lang.Object);
method public static int deepHashCode(java.lang.Object);
@@ -4107,6 +4126,9 @@
method public boolean isSystem();
method public static int myUserId();
method public static android.os.UserHandle of(int);
+ field public static final android.os.UserHandle ALL;
+ field public static final android.os.UserHandle CURRENT;
+ field public static final android.os.UserHandle SYSTEM;
field public static final int USER_NULL = -10000; // 0xffffd8f0
}
@@ -4177,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";
}
@@ -5382,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);
@@ -5390,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);
@@ -6572,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();
}
@@ -6579,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
}
}
@@ -6785,7 +6811,6 @@
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
method public static int loadWebViewNativeLibraryFromPackage(java.lang.String, java.lang.ClassLoader);
method public static void prepareWebViewInZygote();
- field public static final java.lang.String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize";
field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2
field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7
field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4
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/api/test-current.txt b/api/test-current.txt
index 0f89dfd..e22f516 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -259,6 +259,7 @@
}
public abstract class Context {
+ method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String getOpPackageName();
method public android.os.UserHandle getUser();
method public int getUserId();
@@ -765,6 +766,8 @@
method public static int getAppId(int);
method public int getIdentifier();
method public static boolean isApp(int);
+ field public static final android.os.UserHandle ALL;
+ field public static final android.os.UserHandle CURRENT;
field public static final android.os.UserHandle SYSTEM;
}
@@ -1336,30 +1339,47 @@
method public void writeRawZigZag64(long);
}
- public final class ProtoOutputStream {
+ public final class ProtoInputStream extends android.util.proto.ProtoStream {
+ ctor public ProtoInputStream(java.io.InputStream, int);
+ ctor public ProtoInputStream(java.io.InputStream);
+ ctor public ProtoInputStream(byte[]);
+ method public int decodeZigZag32(int);
+ method public long decodeZigZag64(long);
+ method public java.lang.String dumpDebugData();
+ method public void end(long);
+ method public int getFieldNumber();
+ method public int getOffset();
+ method public int getWireType();
+ method public boolean isNextField(long) throws java.io.IOException;
+ method public int nextField() throws java.io.IOException;
+ method public boolean readBoolean(long) throws java.io.IOException;
+ method public byte[] readBytes(long) throws java.io.IOException;
+ method public double readDouble(long) throws java.io.IOException;
+ method public float readFloat(long) throws java.io.IOException;
+ method public int readInt(long) throws java.io.IOException;
+ method public long readLong(long) throws java.io.IOException;
+ method public java.lang.String readString(long) throws java.io.IOException;
+ method public void skip() throws java.io.IOException;
+ method public long start(long) throws java.io.IOException;
+ field public static final int NO_MORE_FIELDS = -1; // 0xffffffff
+ }
+
+ public final class ProtoOutputStream extends android.util.proto.ProtoStream {
ctor public ProtoOutputStream();
ctor public ProtoOutputStream(int);
ctor public ProtoOutputStream(java.io.OutputStream);
ctor public ProtoOutputStream(java.io.FileDescriptor);
method public static int checkFieldId(long, long);
- method public static int convertObjectIdToOrdinal(int);
method public void dump(java.lang.String);
method public void end(long);
method public deprecated void endObject(long);
method public deprecated void endRepeatedObject(long);
method public void flush();
method public byte[] getBytes();
- method public static int getDepthFromToken(long);
- method public static int getObjectIdFromToken(long);
- method public static boolean getRepeatedFromToken(long);
- method public static int getSizePosFromToken(long);
- method public static int getTagSizeFromToken(long);
method public static long makeFieldId(int, long);
- method public static long makeToken(int, boolean, int, int, int);
method public long start(long);
method public deprecated long startObject(long);
method public deprecated long startRepeatedObject(long);
- method public static java.lang.String token2String(long);
method public void write(long, double);
method public void write(long, float);
method public void write(long, int);
@@ -1416,6 +1436,27 @@
method public void writeTag(int, int);
method public deprecated void writeUInt32(long, int);
method public deprecated void writeUInt64(long, long);
+ }
+
+ public class ProtoParseException extends java.lang.RuntimeException {
+ ctor public ProtoParseException(java.lang.String);
+ }
+
+ public abstract class ProtoStream {
+ ctor public ProtoStream();
+ method public static int convertObjectIdToOrdinal(int);
+ method public static int getDepthFromToken(long);
+ method public static java.lang.String getFieldCountString(long);
+ method public static java.lang.String getFieldIdString(long);
+ method public static java.lang.String getFieldTypeString(long);
+ method public static int getObjectIdFromToken(long);
+ method public static int getOffsetFromToken(long);
+ method public static boolean getRepeatedFromToken(long);
+ method public static int getTagSizeFromToken(long);
+ method public static java.lang.String getWireTypeString(int);
+ method public static long makeFieldId(int, long);
+ method public static long makeToken(int, boolean, int, int, int);
+ method public static java.lang.String token2String(long);
field public static final long FIELD_COUNT_MASK = 16492674416640L; // 0xf0000000000L
field public static final long FIELD_COUNT_PACKED = 5497558138880L; // 0x50000000000L
field public static final long FIELD_COUNT_REPEATED = 2199023255552L; // 0x20000000000L
@@ -1435,6 +1476,7 @@
field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L
field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L
field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L
+ field protected static final java.lang.String[] FIELD_TYPE_NAMES;
field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L
field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L
field public static final int FIELD_TYPE_SHIFT = 32; // 0x20
@@ -1444,7 +1486,6 @@
field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L
field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L
field public static final long FIELD_TYPE_UNKNOWN = 0L; // 0x0L
- field public static final java.lang.String TAG = "ProtoOutputStream";
field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4
field public static final int WIRE_TYPE_FIXED32 = 5; // 0x5
field public static final int WIRE_TYPE_FIXED64 = 1; // 0x1
@@ -1454,8 +1495,8 @@
field public static final int WIRE_TYPE_VARINT = 0; // 0x0
}
- public class ProtoParseException extends java.lang.RuntimeException {
- ctor public ProtoParseException(java.lang.String);
+ public class WireTypeMismatchException extends android.util.proto.ProtoParseException {
+ ctor public WireTypeMismatchException(java.lang.String);
}
}
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..e14f2eb 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -207,7 +207,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/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/atoms.proto b/cmds/statsd/src/atoms.proto
index d117f39..b8f19ee 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -91,7 +91,7 @@
ActivityForegroundStateChanged activity_foreground_state_changed = 42;
IsolatedUidChanged isolated_uid_changed = 43;
PacketWakeupOccurred packet_wakeup_occurred = 44;
- // 45 is available
+ WallClockTimeShifted wall_clock_time_shifted = 45;
AnomalyDetected anomaly_detected = 46;
AppBreadcrumbReported app_breadcrumb_reported = 47;
AppStartOccurred app_start_occurred = 48;
@@ -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;
@@ -1479,6 +1505,18 @@
}
/**
+ * Logs the wall-clock time when a significant wall-clock time shift occurs.
+ * For example, this could be due to the user manually changing the time.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/AlarmManagerService.java
+ */
+message WallClockTimeShifted {
+ // New wall-clock time in milliseconds, according to System.currentTimeMillis().
+ optional int64 wall_clock_timestamp_millis = 1;
+}
+
+/**
* Logs when statsd detects an anomaly.
*
* Logged from:
@@ -1798,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 //
//////////////////////////////////////////////////////////////////////
@@ -2143,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.
*
@@ -2163,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
*/
@@ -2228,6 +2330,14 @@
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.
optional int32 uid = 1 [(is_uid) = true];
@@ -2271,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;
}
/**
@@ -2353,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/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/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index eec90fc..afd8ec2 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -49,7 +49,6 @@
// for EventMetricData
const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
const int FIELD_ID_ATOMS = 2;
-const int FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS = 3;
EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
const int conditionIndex,
@@ -146,13 +145,9 @@
if (truncateTimestamp) {
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS,
(long long)truncateTimestampNsToFiveMinutes(event.GetElapsedTimestampNs()));
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS,
- (long long)truncateTimestampNsToFiveMinutes(getWallClockNs()));
} else {
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS,
(long long)event.GetElapsedTimestampNs());
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS,
- (long long)getWallClockNs());
}
uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index bcfcd7a..02b9773 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -63,7 +63,6 @@
// for GaugeBucketInfo
const int FIELD_ID_ATOM = 3;
const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4;
-const int FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP = 5;
const int FIELD_ID_BUCKET_NUM = 6;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
@@ -286,16 +285,9 @@
const int64_t elapsedTimestampNs = truncateTimestamp ?
truncateTimestampNsToFiveMinutes(atom.mElapsedTimestamps) :
atom.mElapsedTimestamps;
- const int64_t wallClockNs = truncateTimestamp ?
- truncateTimestampNsToFiveMinutes(atom.mWallClockTimestampNs) :
- atom.mWallClockTimestampNs;
protoOutput->write(
FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
(long long)elapsedTimestampNs);
- protoOutput->write(
- FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
- FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP,
- (long long)wallClockNs);
}
}
protoOutput->end(bucketInfoToken);
@@ -450,7 +442,7 @@
if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
return;
}
- GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs, getWallClockNs());
+ GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
(*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
// Anomaly detection on gauge metric only works when there is one numeric
// field specified.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index e3da5db..6379389 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,12 +33,11 @@
namespace statsd {
struct GaugeAtom {
- GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int64_t wallClockNs)
- : mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) {
+ GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
+ : mFields(fields), mElapsedTimestamps(elapsedTimeNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
int64_t mElapsedTimestamps;
- int64_t mWallClockTimestampNs;
};
struct GaugeBucket {
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index db7e680..ab0b23c 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -46,7 +46,7 @@
optional Atom atom = 2;
- optional int64 wall_clock_timestamp_nanos = 3;
+ optional int64 wall_clock_timestamp_nanos = 3 [deprecated = true];
}
message CountBucketInfo {
@@ -142,7 +142,7 @@
repeated int64 elapsed_timestamp_nanos = 4;
- repeated int64 wall_clock_timestamp_nanos = 5;
+ repeated int64 wall_clock_timestamp_nanos = 5 [deprecated = true];
optional int64 bucket_num = 6;
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/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index ea6eb3f..5b6f167 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -148,7 +148,7 @@
EXPECT_EQ(1, data.bucket_info(0).atom_size());
EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
@@ -271,7 +271,7 @@
EXPECT_EQ(1, data.bucket_info(0).atom_size());
EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
@@ -375,7 +375,6 @@
EXPECT_EQ(1, data.bucket_info(0).atom_size());
EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 3de8d0d..5c1ef01 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -173,7 +173,7 @@
if (sampling_type == GaugeMetric::ALL_CONDITION_CHANGES) {
EXPECT_EQ(2, data.bucket_info(0).atom_size());
EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(2, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(0).end_bucket_elapsed_nanos());
@@ -192,7 +192,6 @@
EXPECT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
- EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
@@ -206,7 +205,6 @@
EXPECT_EQ(2, data.bucket_info(2).atom_size());
EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(2, data.bucket_info(2).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
@@ -226,7 +224,6 @@
} else {
EXPECT_EQ(1, data.bucket_info(0).atom_size());
EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(0).end_bucket_elapsed_nanos());
@@ -239,7 +236,6 @@
EXPECT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
- EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
@@ -253,7 +249,6 @@
EXPECT_EQ(1, data.bucket_info(2).atom_size());
EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(1, data.bucket_info(2).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
@@ -276,7 +271,6 @@
EXPECT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).atom_size());
EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 04b33b1..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;
@@ -15605,7 +15606,6 @@
HSPLandroid/app/admin/IDevicePolicyManager;->getLockTaskPackages(Landroid/content/ComponentName;)[Ljava/lang/String;
HSPLandroid/app/admin/IDevicePolicyManager;->getLongSupportMessage(Landroid/content/ComponentName;)Ljava/lang/CharSequence;
HSPLandroid/app/admin/IDevicePolicyManager;->getLongSupportMessageForUser(Landroid/content/ComponentName;I)Ljava/lang/CharSequence;
-HSPLandroid/app/admin/IDevicePolicyManager;->getMandatoryBackupTransport()Landroid/content/ComponentName;
HSPLandroid/app/admin/IDevicePolicyManager;->getMaximumFailedPasswordsForWipe(Landroid/content/ComponentName;IZ)I
HSPLandroid/app/admin/IDevicePolicyManager;->getMaximumTimeToLock(Landroid/content/ComponentName;IZ)J
HSPLandroid/app/admin/IDevicePolicyManager;->getMeteredDataDisabledPackages(Landroid/content/ComponentName;)Ljava/util/List;
@@ -15747,7 +15747,6 @@
HSPLandroid/app/admin/IDevicePolicyManager;->setLockTaskPackages(Landroid/content/ComponentName;[Ljava/lang/String;)V
HSPLandroid/app/admin/IDevicePolicyManager;->setLogoutEnabled(Landroid/content/ComponentName;Z)V
HSPLandroid/app/admin/IDevicePolicyManager;->setLongSupportMessage(Landroid/content/ComponentName;Ljava/lang/CharSequence;)V
-HSPLandroid/app/admin/IDevicePolicyManager;->setMandatoryBackupTransport(Landroid/content/ComponentName;Landroid/content/ComponentName;)Z
HSPLandroid/app/admin/IDevicePolicyManager;->setMasterVolumeMuted(Landroid/content/ComponentName;Z)V
HSPLandroid/app/admin/IDevicePolicyManager;->setMaximumFailedPasswordsForWipe(Landroid/content/ComponentName;IZ)V
HSPLandroid/app/admin/IDevicePolicyManager;->setMaximumTimeToLock(Landroid/content/ComponentName;JZ)V
@@ -24288,7 +24287,6 @@
HSPLandroid/inputmethodservice/IInputMethodSessionWrapper;->getInternalInputMethodSession()Landroid/view/inputmethod/InputMethodSession;
HSPLandroid/inputmethodservice/IInputMethodWrapper$InputMethodSessionCallbackWrapper;->sessionCreated(Landroid/view/inputmethod/InputMethodSession;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;-><init>(Landroid/inputmethodservice/AbstractInputMethodService;Landroid/view/inputmethod/InputMethod;)V
-HSPLandroid/inputmethodservice/IInputMethodWrapper;->attachToken(Landroid/os/IBinder;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;->bindInput(Landroid/view/inputmethod/InputBinding;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;->createSession(Landroid/view/InputChannel;Lcom/android/internal/view/IInputSessionCallback;)V
HSPLandroid/inputmethodservice/IInputMethodWrapper;->executeMessage(Landroid/os/Message;)V
@@ -42280,11 +42278,9 @@
HSPLcom/android/internal/view/IInputContextCallback;->setTextAfterCursor(Ljava/lang/CharSequence;I)V
HSPLcom/android/internal/view/IInputContextCallback;->setTextBeforeCursor(Ljava/lang/CharSequence;I)V
HSPLcom/android/internal/view/IInputMethod$Stub$Proxy;->asBinder()Landroid/os/IBinder;
-HSPLcom/android/internal/view/IInputMethod$Stub$Proxy;->attachToken(Landroid/os/IBinder;)V
HSPLcom/android/internal/view/IInputMethod$Stub;-><init>()V
HSPLcom/android/internal/view/IInputMethod$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethod;
HSPLcom/android/internal/view/IInputMethod$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
-HSPLcom/android/internal/view/IInputMethod;->attachToken(Landroid/os/IBinder;)V
HSPLcom/android/internal/view/IInputMethod;->bindInput(Landroid/view/inputmethod/InputBinding;)V
HSPLcom/android/internal/view/IInputMethod;->changeInputMethodSubtype(Landroid/view/inputmethod/InputMethodSubtype;)V
HSPLcom/android/internal/view/IInputMethod;->createSession(Landroid/view/InputChannel;Lcom/android/internal/view/IInputSessionCallback;)V
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index a4f13a0..0472461 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -728,8 +728,6 @@
Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File;
Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
-Landroid/os/Environment;->getOemDirectory()Ljava/io/File;
-Landroid/os/Environment;->getVendorDirectory()Ljava/io/File;
Landroid/os/Environment;->initForCurrentUser()V
Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
Landroid/os/Environment;->sCurrentUser:Landroid/os/Environment$UserEnvironment;
@@ -1001,8 +999,6 @@
Landroid/os/UserHandle;->AID_CACHE_GID_START:I
Landroid/os/UserHandle;->AID_ROOT:I
Landroid/os/UserHandle;->AID_SHARED_GID_START:I
-Landroid/os/UserHandle;->ALL:Landroid/os/UserHandle;
-Landroid/os/UserHandle;->CURRENT:Landroid/os/UserHandle;
Landroid/os/UserHandle;->CURRENT_OR_SELF:Landroid/os/UserHandle;
Landroid/os/UserHandle;->ERR_GID:I
Landroid/os/UserHandle;->formatUid(Ljava/io/PrintWriter;I)V
@@ -2138,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
@@ -2226,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;
@@ -2258,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
@@ -2564,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/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 f0a0e88..aa0275a 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -76,6 +76,7 @@
* @attr ref android.R.styleable#AccessibilityService_notificationTimeout
* @attr ref android.R.styleable#AccessibilityService_packageNames
* @attr ref android.R.styleable#AccessibilityService_settingsActivity
+ * @attr ref android.R.styleable#AccessibilityService_minimumUiTimeout
* @see AccessibilityService
* @see android.view.accessibility.AccessibilityEvent
* @see android.view.accessibility.AccessibilityManager
@@ -313,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;
@@ -413,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;
@@ -426,6 +434,13 @@
public boolean crashed;
/**
+ * The minimum timeout in milliseconds that UI controls need to remain on the screen.
+ *
+ * @see #setMinimumUiTimeoutMillis
+ */
+ private int mMinimumUiTimeout;
+
+ /**
* The component name the accessibility service.
*/
private ComponentName mComponentName;
@@ -529,6 +544,9 @@
notificationTimeout = asAttributes.getInt(
com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
0);
+ mMinimumUiTimeout = asAttributes.getInt(
+ com.android.internal.R.styleable.AccessibilityService_minimumUiTimeout,
+ 0);
flags = asAttributes.getInt(
com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
mSettingsActivityName = asAttributes.getString(
@@ -598,6 +616,7 @@
packageNames = other.packageNames;
feedbackType = other.feedbackType;
notificationTimeout = other.notificationTimeout;
+ mMinimumUiTimeout = other.mMinimumUiTimeout;
flags = other.flags;
}
@@ -755,6 +774,29 @@
return null;
}
+ /**
+ * Set the minimum time that controls need to remain on the screen to support the user.
+ * <p>
+ * <strong>This value can be dynamically set at runtime by
+ * {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}.</strong>
+ * </p>
+ *
+ * @param timeout The timeout in milliseconds.
+ */
+ public void setMinimumUiTimeoutMillis(int timeout) {
+ mMinimumUiTimeout = timeout;
+ }
+
+ /**
+ * Get the minimum ui timeout.
+ *
+ * @see #setMinimumUiTimeoutMillis
+ * @return The timeout in milliseconds.
+ */
+ public int getMinimumUiTimeoutMillis() {
+ return mMinimumUiTimeout;
+ }
+
/** {@hide} */
public boolean isDirectBootAware() {
return ((flags & FLAG_FORCE_DIRECT_BOOT_AWARE) != 0)
@@ -773,6 +815,7 @@
parcel.writeStringArray(packageNames);
parcel.writeInt(feedbackType);
parcel.writeLong(notificationTimeout);
+ parcel.writeInt(mMinimumUiTimeout);
parcel.writeInt(flags);
parcel.writeInt(crashed ? 1 : 0);
parcel.writeParcelable(mComponentName, flagz);
@@ -790,6 +833,7 @@
packageNames = parcel.readStringArray();
feedbackType = parcel.readInt();
notificationTimeout = parcel.readLong();
+ mMinimumUiTimeout = parcel.readInt();
flags = parcel.readInt();
crashed = parcel.readInt() != 0;
mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
@@ -840,6 +884,8 @@
stringBuilder.append(", ");
stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
stringBuilder.append(", ");
+ stringBuilder.append("minimumUiTimeout: ").append(mMinimumUiTimeout);
+ stringBuilder.append(", ");
appendFlags(stringBuilder, flags);
stringBuilder.append(", ");
stringBuilder.append("id: ").append(getId());
@@ -1011,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 bf2d860..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();
@@ -2310,7 +2311,7 @@
*
* @param newConfig The new device configuration.
*/
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onConfigurationChanged " + this + ": " + newConfig);
mCalled = true;
@@ -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);
}
@@ -6350,7 +6352,8 @@
* @see android.view.Window#getLayoutInflater
*/
@Nullable
- public View onCreateView(String name, Context context, AttributeSet attrs) {
+ public View onCreateView(@NonNull String name, @NonNull Context context,
+ @NonNull AttributeSet attrs) {
return null;
}
@@ -6364,7 +6367,9 @@
* @see android.view.LayoutInflater#createView
* @see android.view.Window#getLayoutInflater
*/
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ @Nullable
+ public View onCreateView(@Nullable View parent, @NonNull String name,
+ @NonNull Context context, @NonNull AttributeSet attrs) {
if (!"fragment".equals(name)) {
return onCreateView(name, context, attrs);
}
@@ -6382,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;
@@ -7094,7 +7101,7 @@
// ------------------ Internal API ------------------
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final void setParent(Activity parent) {
mParent = parent;
}
@@ -7159,7 +7166,6 @@
mWindow.setColorMode(info.colorMode);
setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
- enableAutofillCompatibilityIfNeeded();
}
private void enableAutofillCompatibilityIfNeeded() {
@@ -7205,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..e86a49d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9120701..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());
@@ -2131,7 +2131,7 @@
ai = getPackageManager().getApplicationInfo(packageName,
PackageManager.GET_SHARED_LIBRARY_FILES
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- userId);
+ (userId < 0) ? UserHandle.myUserId() : userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -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/Application.java b/core/java/android/app/Application.java
index 636366d..68b745d 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -127,7 +127,7 @@
}
@CallSuper
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index cd4ace6..62f6bac 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -16,11 +16,14 @@
package android.app;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+
import android.app.NotificationManager.InterruptionFilter;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.service.notification.ZenPolicy;
import java.util.Objects;
@@ -35,6 +38,7 @@
private Uri conditionId;
private ComponentName owner;
private long creationTime;
+ private ZenPolicy mZenPolicy;
/**
* Creates an automatic zen rule.
@@ -58,7 +62,27 @@
}
/**
- * @SystemApi
+ * Creates an automatic zen rule.
+ *
+ * @param name The name of the rule.
+ * @param owner The Condition Provider service that owns this rule.
+ * @param conditionId A representation of the state that should cause the Condition Provider
+ * service to apply the given interruption filter.
+ * @param policy The policy defines which notifications are allowed to interrupt the user
+ * while this rule is active
+ * @param enabled Whether the rule is enabled.
+ */
+ public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy,
+ boolean enabled) {
+ this.name = name;
+ this.owner = owner;
+ this.conditionId = conditionId;
+ this.interruptionFilter = INTERRUPTION_FILTER_PRIORITY;
+ this.enabled = enabled;
+ this.mZenPolicy = policy;
+ }
+
+ /**
* @hide
*/
public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
@@ -67,6 +91,15 @@
this.creationTime = creationTime;
}
+ /**
+ * @hide
+ */
+ public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy,
+ boolean enabled, long creationTime) {
+ this(name, owner, conditionId, policy, enabled);
+ this.creationTime = creationTime;
+ }
+
public AutomaticZenRule(Parcel source) {
enabled = source.readInt() == 1;
if (source.readInt() == 1) {
@@ -76,6 +109,7 @@
conditionId = source.readParcelable(null);
owner = source.readParcelable(null);
creationTime = source.readLong();
+ mZenPolicy = source.readParcelable(null);
}
/**
@@ -114,6 +148,13 @@
}
/**
+ * Gets the zen policy.
+ */
+ public ZenPolicy getZenPolicy() {
+ return this.mZenPolicy.copy();
+ }
+
+ /**
* Returns the time this rule was created, represented as milliseconds since the epoch.
*/
public long getCreationTime() {
@@ -149,6 +190,13 @@
this.enabled = enabled;
}
+ /**
+ * Sets the zen policy.
+ */
+ public void setZenPolicy(ZenPolicy zenPolicy) {
+ this.mZenPolicy = zenPolicy;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -167,6 +215,7 @@
dest.writeParcelable(conditionId, 0);
dest.writeParcelable(owner, 0);
dest.writeLong(creationTime);
+ dest.writeParcelable(mZenPolicy, 0);
}
@Override
@@ -178,6 +227,7 @@
.append(",conditionId=").append(conditionId)
.append(",owner=").append(owner)
.append(",creationTime=").append(creationTime)
+ .append(",mZenPolicy=").append(mZenPolicy)
.append(']').toString();
}
@@ -191,12 +241,14 @@
&& other.interruptionFilter == interruptionFilter
&& Objects.equals(other.conditionId, conditionId)
&& Objects.equals(other.owner, owner)
- && other.creationTime == creationTime;
+ && other.creationTime == creationTime
+ && Objects.equals(other.mZenPolicy, mZenPolicy);
}
@Override
public int hashCode() {
- return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime);
+ return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime,
+ mZenPolicy);
}
public static final Parcelable.Creator<AutomaticZenRule> CREATOR
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/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/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 4f004d93..357420b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -112,6 +112,7 @@
ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
ParceledListSlice getSnoozedNotificationsFromListener(in INotificationListener token, int trim);
+ void clearRequestedListenerHints(in INotificationListener token);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
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/NotificationManager.java b/core/java/android/app/NotificationManager.java
index b96b39d..9f819b9 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -841,9 +841,14 @@
List<ZenModeConfig.ZenRule> rules = service.getZenRules();
Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
for (ZenModeConfig.ZenRule rule : rules) {
- ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
- rule.conditionId, zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
- rule.creationTime));
+ if (rule.zenPolicy == null) {
+ ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
+ rule.conditionId, zenModeToInterruptionFilter(rule.zenMode),
+ rule.enabled, rule.creationTime));
+ } else {
+ ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
+ rule.conditionId, rule.zenPolicy, rule.enabled, rule.creationTime));
+ }
}
return ruleMap;
} catch (RemoteException e) {
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/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 d1ecf1e..bd7a2dd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1183,14 +1183,6 @@
public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
/**
- * Constant to indicate the feature of mandatory backups. Used as argument to
- * {@link #createAdminSupportIntent(String)}.
- * @see #setMandatoryBackupTransport(ComponentName, ComponentName)
- * @hide
- */
- public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
-
- /**
* Constant to indicate the feature of suspending app. Use it as the value of
* {@link #EXTRA_RESTRICTION}.
* @hide
@@ -1200,8 +1192,7 @@
/**
* A String indicating a specific restricted feature. Can be a user restriction from the
* {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values
- * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
- * {@link #POLICY_MANDATORY_BACKUPS}.
+ * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
* @see #createAdminSupportIntent(String)
* @hide
*/
@@ -6919,7 +6910,7 @@
* @param restriction Indicates for which feature the dialog should be displayed. Can be a
* user restriction from {@link UserManager}, e.g.
* {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
- * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+ * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
* @return Intent An intent to be used to start the dialog-activity if the restriction is
* set by an admin, or null if the restriction does not exist or no admin set it.
*/
@@ -8917,55 +8908,6 @@
}
/**
- * Makes backups mandatory and enforces the usage of the specified backup transport.
- *
- * <p>When a {@code null} backup transport is specified, backups are made optional again.
- * <p>Only device owner can call this method.
- * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
- * specified, backups will be enabled.
- * <p> If the backup service is disabled after the mandatory backup transport has been set, the
- * mandatory backup transport is cleared.
- *
- * <p>NOTE: The method shouldn't be called on the main thread.
- *
- * @param admin admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
- * @return {@code true} if the backup transport was successfully set; {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device owner.
- * @hide
- */
- @WorkerThread
- public boolean setMandatoryBackupTransport(
- @NonNull ComponentName admin,
- @Nullable ComponentName backupTransportComponent) {
- throwIfParentInstance("setMandatoryBackupTransport");
- try {
- return mService.setMandatoryBackupTransport(admin, backupTransportComponent);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /**
- * Returns the backup transport which has to be used for backups if backups are mandatory or
- * {@code null} if backups are not mandatory.
- *
- * @return a {@link ComponentName} of the backup transport layer to be used if backups are
- * mandatory or {@code null} if backups are not mandatory.
- * @hide
- */
- @UnsupportedAppUsage
- public ComponentName getMandatoryBackupTransport() {
- throwIfParentInstance("getMandatoryBackupTransport");
- try {
- return mService.getMandatoryBackupTransport();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
-
- /**
* Called by a device owner to control the network logging feature.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6a5f3de..35ea250 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -362,8 +362,6 @@
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
- boolean setMandatoryBackupTransport(in ComponentName admin, in ComponentName backupTransportComponent);
- ComponentName getMandatoryBackupTransport();
void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
boolean isNetworkLoggingEnabled(in ComponentName admin);
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 1c55d8a..3e20748 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -299,8 +299,7 @@
*
* @param transport ComponentName of the service hosting the transport. This is different from
* the transport's name that is returned by {@link BackupTransport#name()}.
- * @param listener A listener object to get a callback on the transport being selected. It may
- * be {@code null}.
+ * @param listener A listener object to get a callback on the transport being selected.
*/
void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
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/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java
index b96c8d3..428545d 100644
--- a/core/java/android/content/ComponentCallbacks.java
+++ b/core/java/android/content/ComponentCallbacks.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.NonNull;
import android.content.res.Configuration;
/**
@@ -44,7 +45,7 @@
*
* @param newConfig The new device configuration.
*/
- void onConfigurationChanged(Configuration newConfig);
+ void onConfigurationChanged(@NonNull Configuration newConfig);
/**
* This is called when the overall system is running low on memory, and
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/ContentUris.java b/core/java/android/content/ContentUris.java
index dbe8a7c..fd7b372 100644
--- a/core/java/android/content/ContentUris.java
+++ b/core/java/android/content/ContentUris.java
@@ -18,6 +18,8 @@
import android.net.Uri;
+import java.util.List;
+
/**
* Utility methods useful for working with {@link android.net.Uri} objects
* that use the "content" (content://) scheme.
@@ -109,4 +111,30 @@
public static Uri withAppendedId(Uri contentUri, long id) {
return appendId(contentUri.buildUpon(), id).build();
}
+
+ /**
+ * Removes any ID from the end of the path.
+ *
+ * @param contentUri that ends with an ID
+ * @return a new URI with the ID removed from the end of the path
+ * @throws IllegalArgumentException when the given URI has no ID to remove
+ * from the end of the path
+ */
+ public static Uri removeId(Uri contentUri) {
+ // Verify that we have a valid ID to actually remove
+ final String last = contentUri.getLastPathSegment();
+ if (last == null) {
+ throw new IllegalArgumentException("No path segments to remove");
+ } else {
+ Long.parseLong(last);
+ }
+
+ final List<String> segments = contentUri.getPathSegments();
+ final Uri.Builder builder = contentUri.buildUpon();
+ builder.path(null);
+ for (int i = 0; i < segments.size() - 1; i++) {
+ builder.appendPath(segments.get(i));
+ }
+ return builder.build();
+ }
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ddd12a5..2ae3ae6 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.
@@ -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";
/**
@@ -4792,6 +4801,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public Context createPackageContextAsUser(
String packageName, @CreatePackageOptions int flags, UserHandle user)
throws PackageManager.NameNotFoundException {
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/AndroidHidlUpdater.java b/core/java/android/content/pm/AndroidHidlUpdater.java
new file mode 100644
index 0000000..69cc94f
--- /dev/null
+++ b/core/java/android/content/pm/AndroidHidlUpdater.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+
+import android.content.pm.PackageParser.Package;
+import android.os.Build;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
+ * and android.hidl.manager-V1.0-java libraries are included by default.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(Package pkg) {
+ // This was the default <= P and is maintained for backwards compatibility.
+ if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.P) {
+ prefixRequiredLibrary(pkg, ANDROID_HIDL_BASE);
+ prefixRequiredLibrary(pkg, ANDROID_HIDL_MANAGER);
+ }
+ }
+}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 46877ca..44e652f1 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/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index a16f81b..03eefed 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -53,6 +53,8 @@
"android.content.pm.OrgApacheHttpLegacyUpdater",
RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
+ packageUpdaters.add(new AndroidHidlUpdater());
+
// Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
// android.test.mock.
packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 316ace16..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/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 83e8663..387d29e8 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,6 +22,10 @@
*/
public class SharedLibraryNames {
+ static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
+
+ static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
+
static final String ANDROID_TEST_BASE = "android.test.base";
static final String ANDROID_TEST_MOCK = "android.test.mock";
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/Resources.java b/core/java/android/content/res/Resources.java
index 43e1612..88b1c88 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1129,10 +1129,8 @@
*
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist or is not a floating-point value.
- * @hide Pending API council approval.
*/
- @UnsupportedAppUsage
- public float getFloat(int id) {
+ public float getFloat(@DimenRes int id) {
final TypedValue value = obtainTempTypedValue();
try {
mResourcesImpl.getValue(id, value, true);
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/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 1377806..19c6745 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -49,7 +49,7 @@
* <p class="note"><strong>Note:</strong> this class assumes
* monotonically increasing version numbers for upgrades.</p>
*/
-public abstract class SQLiteOpenHelper {
+public abstract class SQLiteOpenHelper implements AutoCloseable {
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
private final Context mContext;
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 35584ae..46e66e0 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -188,6 +188,7 @@
@UnsupportedAppUsage
private final CameraMetadataNative mProperties;
private List<CameraCharacteristics.Key<?>> mKeys;
+ private List<CameraCharacteristics.Key<?>> mKeysNeedingPermission;
private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
private List<CaptureRequest.Key<?>> mAvailableSessionKeys;
private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys;
@@ -268,11 +269,50 @@
}
mKeys = Collections.unmodifiableList(
- getKeys(getClass(), getKeyClass(), this, filterTags));
+ getKeys(getClass(), getKeyClass(), this, filterTags, true));
return mKeys;
}
/**
+ * <p>Returns a subset of the list returned by {@link #getKeys} with all keys that
+ * require camera clients to obtain the {@link android.Manifest.permission#CAMERA} permission.
+ * </p>
+ *
+ * <p>If an application calls {@link CameraManager#getCameraCharacteristics} without holding the
+ * {@link android.Manifest.permission#CAMERA} permission,
+ * all keys in this list will not be available, and calling {@link #get} will
+ * return null for those keys. If the application obtains the
+ * {@link android.Manifest.permission#CAMERA} permission, then the
+ * CameraCharacteristics from a call to a subsequent
+ * {@link CameraManager#getCameraCharacteristics} will have the keys available.</p>
+ *
+ * <p>The list returned is not modifiable, so any attempts to modify it will throw
+ * a {@code UnsupportedOperationException}.</p>
+ *
+ * <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
+ *
+ * @return List of camera characteristic keys that require the
+ * {@link android.Manifest.permission#CAMERA} permission. The list can be null in case
+ * there are no currently present keys that need additional permission.
+ */
+ public List<Key<?>> getKeysNeedingPermission() {
+ if (mKeysNeedingPermission == null) {
+ Object crKey = CameraCharacteristics.Key.class;
+ Class<CameraCharacteristics.Key<?>> crKeyTyped =
+ (Class<CameraCharacteristics.Key<?>>)crKey;
+
+ int[] filterTags = get(REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION);
+ if (filterTags == null) {
+ return null;
+ }
+ mKeysNeedingPermission =
+ getAvailableKeyList(CameraCharacteristics.class, crKeyTyped, filterTags,
+ /*includeSynthetic*/ false);
+ }
+ return mKeysNeedingPermission;
+ }
+
+ /**
* <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that the
* camera device can pass as part of the capture session initialization.</p>
*
@@ -328,17 +368,18 @@
return null;
}
mAvailableSessionKeys =
- getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
+ getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags,
+ /*includeSynthetic*/ false);
}
return mAvailableSessionKeys;
}
/**
* <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
@@ -353,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"})
@@ -367,7 +408,8 @@
return null;
}
mAvailablePhysicalRequestKeys =
- getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
+ getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags,
+ /*includeSynthetic*/ false);
}
return mAvailablePhysicalRequestKeys;
}
@@ -399,7 +441,8 @@
+ "in the characteristics");
}
mAvailableRequestKeys =
- getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
+ getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags,
+ /*includeSynthetic*/ true);
}
return mAvailableRequestKeys;
}
@@ -430,7 +473,8 @@
throw new AssertionError("android.request.availableResultKeys must be non-null "
+ "in the characteristics");
}
- mAvailableResultKeys = getAvailableKeyList(CaptureResult.class, crKeyTyped, filterTags);
+ mAvailableResultKeys = getAvailableKeyList(CaptureResult.class, crKeyTyped, filterTags,
+ /*includeSynthetic*/ true);
}
return mAvailableResultKeys;
}
@@ -445,13 +489,16 @@
*
* @param metadataClass The subclass of CameraMetadata that you want to get the keys for.
* @param keyClass The class of the metadata key, e.g. CaptureRequest.Key.class
+ * @param filterTags An array of tags to be used for filtering
+ * @param includeSynthetic Include public syntethic tag by default.
*
* @return List of keys supported by this CameraDevice for metadataClass.
*
* @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
*/
private <TKey> List<TKey>
- getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags) {
+ getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags,
+ boolean includeSynthetic) {
if (metadataClass.equals(CameraMetadata.class)) {
throw new AssertionError(
@@ -462,7 +509,7 @@
}
List<TKey> staticKeyList = getKeys(
- metadataClass, keyClass, /*instance*/null, filterTags);
+ metadataClass, keyClass, /*instance*/null, filterTags, includeSynthetic);
return Collections.unmodifiableList(staticKeyList);
}
@@ -474,6 +521,13 @@
* REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device
* doesn't have the capability, the return value will be an empty set. </p>
*
+ * <p>Prior to API level 29, all returned IDs are guaranteed to be returned by {@link
+ * CameraManager#getCameraIdList}, and can be opened directly by
+ * {@link CameraManager#openCamera}. Starting from API level 29, for each of the returned ID,
+ * if it's also returned by {@link CameraManager#getCameraIdList}, it can be used as a
+ * standalone camera by {@link CameraManager#openCamera}. Otherwise, the camera ID can only be
+ * used as part of the current logical camera.</p>
+ *
* <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
@@ -1103,6 +1157,7 @@
* <p><b>Limited capability</b> -
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
@@ -1124,6 +1179,7 @@
* <p><b>Limited capability</b> -
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
@@ -1238,6 +1294,7 @@
* <p><b>Units</b>:
* Quaternion coefficients</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*/
@PublicKey
public static final Key<float[]> LENS_POSE_ROTATION =
@@ -1273,6 +1330,7 @@
* with PRIMARY_CAMERA.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
@@ -1344,6 +1402,7 @@
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
* coordinate system.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_POSE_ROTATION
@@ -1387,6 +1446,7 @@
* <p><b>Units</b>:
* Unitless coefficients.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @deprecated
@@ -1411,6 +1471,7 @@
* <li>{@link #LENS_POSE_REFERENCE_GYROSCOPE GYROSCOPE}</li>
* </ul></p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
* @see #LENS_POSE_REFERENCE_PRIMARY_CAMERA
@@ -1452,6 +1513,7 @@
* <p><b>Units</b>:
* Unitless coefficients.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @see CameraCharacteristics#LENS_RADIAL_DISTORTION
@@ -1860,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
@@ -1880,6 +1942,21 @@
new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class);
/**
+ * <p>A list of camera characteristics keys that are only available
+ * in case the camera client has camera permission.</p>
+ * <p>The entry contains a subset of
+ * {@link android.hardware.camera2.CameraCharacteristics#getKeys } that require camera clients
+ * to acquire the {@link android.Manifest.permission#CAMERA } permission before calling
+ * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }. If the
+ * permission is not held by the camera client, then the values of the repsective properties
+ * will not be present in {@link android.hardware.camera2.CameraCharacteristics }.</p>
+ * <p>This key is available on all devices.</p>
+ * @hide
+ */
+ public static final Key<int[]> REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION =
+ new Key<int[]>("android.request.characteristicKeysNeedingPermission", int[].class);
+
+ /**
* <p>The list of image formats that are supported by this
* camera device for output streams.</p>
* <p>All camera devices will support JPEG and YUV_420_888 formats.</p>
@@ -2703,6 +2780,7 @@
* <li>{@link #SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN ISO_STUDIO_TUNGSTEN}</li>
* </ul></p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM1
* @see CameraCharacteristics#SENSOR_COLOR_TRANSFORM1
@@ -2744,6 +2822,7 @@
* <p><b>Range of valid values:</b><br>
* Any value listed in {@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 android.sensor.referenceIlluminant1}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM2
* @see CameraCharacteristics#SENSOR_COLOR_TRANSFORM2
@@ -2766,6 +2845,7 @@
* space under the first reference illuminant
* ({@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 android.sensor.referenceIlluminant1}).</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
*/
@@ -2788,6 +2868,7 @@
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
*/
@@ -2811,6 +2892,7 @@
* match the standard white point for the first reference illuminant
* (i.e. no chromatic adaptation will be applied by this transform).</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
*/
@@ -2836,6 +2918,7 @@
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
*/
@@ -2857,6 +2940,7 @@
* illuminant in the reference sensor colorspace is mapped to D50 in the
* CIE XYZ colorspace.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
*/
@@ -2880,6 +2964,7 @@
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
*/
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 7ebe0f9..44d7364 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -34,7 +34,6 @@
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -45,7 +44,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
-
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
@@ -97,6 +95,9 @@
* identifiers, while removable cameras have a unique identifier for each
* individual device, even if they are the same model.</p>
*
+ * <p>This list doesn't contain physical cameras that can only used as part of a logical
+ * multi-camera device.</p>
+ *
* @return The list of currently connected camera devices.
*/
@NonNull
@@ -234,7 +235,13 @@
* <p>Query the capabilities of a camera device. These capabilities are
* immutable for a given camera.</p>
*
- * @param cameraId The id of the camera device to query
+ * <p>From API level 29, this function can also be used to query the capabilities of physical
+ * cameras that can only be used as part of logical multi-camera. These cameras cannot not be
+ * opened directly via {@link #openCamera}</p>
+ *
+ * @param cameraId The id of the camera device to query. This could be either a standalone
+ * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that
+ * can only used as part of a logical multi-camera.
* @return The properties of the given camera
*
* @throws IllegalArgumentException if the cameraId does not match any
@@ -262,7 +269,9 @@
"Camera service is currently unavailable");
}
try {
- if (!supportsCamera2ApiLocked(cameraId)) {
+ // First check isHiddenPhysicalCamera to avoid supportsCamera2ApiLocked throwing
+ // exception in case cameraId is a hidden physical camera.
+ if (!isHiddenPhysicalCamera(cameraId) && !supportsCamera2ApiLocked(cameraId)) {
// Legacy backwards compatibility path; build static info from the camera
// parameters
int id = Integer.parseInt(cameraId);
@@ -454,7 +463,7 @@
*
* @throws IllegalArgumentException if cameraId or the callback was null,
* or the cameraId does not match any currently or previously available
- * camera device.
+ * camera device returned by {@link #getCameraIdList}.
*
* @throws SecurityException if the application does not have permission to
* access the camera
@@ -778,6 +787,29 @@
}
/**
+ * Queries the camera service if a cameraId is a hidden physical camera that belongs to a
+ * logical camera device.
+ *
+ * A hidden physical camera is a camera that cannot be opened by the application. But it
+ * can be used as part of a logical camera.
+ *
+ * @param cameraId a non-{@code null} camera identifier
+ * @return {@code true} if cameraId is a hidden physical camera device
+ */
+ private boolean isHiddenPhysicalCamera(String cameraId) {
+ try {
+ ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
+ // If no camera service, no support
+ if (cameraService == null) return false;
+
+ return cameraService.isHiddenPhysicalCamera(cameraId);
+ } catch (RemoteException e) {
+ // Camera service is now down, no support for any API level
+ }
+ return false;
+ }
+
+ /**
* A per-process global camera manager instance, to retain a connection to the camera service,
* and to distribute camera availability notices to API-registered callbacks
*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index caa99d5..486b054 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -116,7 +116,8 @@
public List<TKey> getKeys() {
Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
return Collections.unmodifiableList(
- getKeys(thisClass, getKeyClass(), this, /*filterTags*/null));
+ getKeys(thisClass, getKeyClass(), this, /*filterTags*/null,
+ /*includeSynthetic*/ true));
}
/**
@@ -131,13 +132,14 @@
* Optionally, if {@code filterTags} is not {@code null}, then filter out any keys
* whose native {@code tag} is not in {@code filterTags}. The {@code filterTags} array will be
* sorted as a side effect.
+ * {@code includeSynthetic} Includes public syntenthic fields by default.
* </p>
*/
/*package*/ @SuppressWarnings("unchecked")
<TKey> ArrayList<TKey> getKeys(
Class<?> type, Class<TKey> keyClass,
CameraMetadata<TKey> instance,
- int[] filterTags) {
+ int[] filterTags, boolean includeSynthetic) {
if (DEBUG) Log.v(TAG, "getKeysStatic for " + type);
@@ -168,7 +170,7 @@
}
if (instance == null || instance.getProtected(key) != null) {
- if (shouldKeyBeAdded(key, field, filterTags)) {
+ if (shouldKeyBeAdded(key, field, filterTags, includeSynthetic)) {
keyList.add(key);
if (DEBUG) {
@@ -215,7 +217,8 @@
}
@SuppressWarnings("rawtypes")
- private static <TKey> boolean shouldKeyBeAdded(TKey key, Field field, int[] filterTags) {
+ private static <TKey> boolean shouldKeyBeAdded(TKey key, Field field, int[] filterTags,
+ boolean includeSynthetic) {
if (key == null) {
throw new NullPointerException("key must not be null");
}
@@ -249,8 +252,7 @@
if (field.getAnnotation(SyntheticKey.class) != null) {
// This key is synthetic, so calling #getTag will throw IAE
- // TODO: don't just assume all public+synthetic keys are always available
- return true;
+ return includeSynthetic;
}
/*
@@ -811,8 +813,14 @@
public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
/**
- * <p>The camera device is a logical camera backed by two or more physical cameras that are
- * also exposed to the application.</p>
+ * <p>The camera device is a logical camera backed by two or more physical cameras. In
+ * API level 28, the physical cameras must also be exposed to the application via
+ * {@link android.hardware.camera2.CameraManager#getCameraIdList }. Starting from API
+ * level 29, some or all physical cameras may not be independently exposed to the
+ * application, in which case the physical camera IDs will not be available in
+ * {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the application
+ * can still query the physical cameras' characteristics by calling
+ * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }.</p>
* <p>Camera application shouldn't assume that there are at most 1 rear camera and 1 front
* camera in the system. For an application that switches between front and back cameras,
* the recommendation is to switch between the first rear camera and the first front
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 3c6c11d..a7e185c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2841,6 +2841,7 @@
* <p><b>Units</b>:
* Quaternion coefficients</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*/
@PublicKey
public static final Key<float[]> LENS_POSE_ROTATION =
@@ -2876,6 +2877,7 @@
* with PRIMARY_CAMERA.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
@@ -2947,6 +2949,7 @@
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
* coordinate system.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_DISTORTION
* @see CameraCharacteristics#LENS_POSE_ROTATION
@@ -2990,6 +2993,7 @@
* <p><b>Units</b>:
* Unitless coefficients.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @deprecated
@@ -3036,6 +3040,7 @@
* <p><b>Units</b>:
* Unitless coefficients.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
* @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
* @see CameraCharacteristics#LENS_RADIAL_DISTORTION
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 1ee3c93..cb33659 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -58,7 +58,7 @@
* execute in parallel with any {@link Surface} initialization, such as waiting for a
* {@link android.view.SurfaceView} to be ready as part of the UI initialization.</li>
*
- * <li>The third and most complex usage pattern inlvolves surface sharing. Once instantiated an
+ * <li>The third and most complex usage pattern involves surface sharing. Once instantiated an
* OutputConfiguration can be enabled for surface sharing via {@link #enableSurfaceSharing}. This
* must be done before creating a new capture session and enables calls to
* {@link CameraCaptureSession#updateOutputConfiguration}. An OutputConfiguration with enabled
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 0f83c8b..20e0116 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,34 @@
}
}
- @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(
+ return context.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);
+ return context.getString(com.android.internal.R.string.face_error_timeout);
case FACE_ERROR_NO_SPACE:
- return mContext.getString(com.android.internal.R.string.face_error_no_space);
+ return context.getString(com.android.internal.R.string.face_error_no_space);
case FACE_ERROR_CANCELED:
- return mContext.getString(com.android.internal.R.string.face_error_canceled);
+ 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_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 +557,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 +596,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 +894,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 +928,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/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/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 4080ee6..1030694 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -160,11 +160,10 @@
args.recycle();
return;
}
-
case DO_INITIALIZE_INTERNAL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- inputMethod.initializeInternal((IBinder) args.arg1,
+ inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
(IInputMethodPrivilegedOperations) args.arg2);
} finally {
args.recycle();
@@ -253,9 +252,10 @@
@BinderThread
@Override
- public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps) {
+ public void initializeInternal(IBinder token, int displayId,
+ IInputMethodPrivilegedOperations privOps) {
mCaller.executeOrSendMessage(
- mCaller.obtainMessageOO(DO_INITIALIZE_INTERNAL, token, privOps));
+ mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2d12b86..b948402 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -16,6 +16,7 @@
package android.inputmethodservice;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -84,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;
@@ -461,10 +463,11 @@
*/
@MainThread
@Override
- public final void initializeInternal(IBinder token,
+ 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);
}
@@ -484,6 +487,22 @@
/**
* {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
+ public void updateInputMethodDisplay(int displayId) {
+ // Update display for adding IME window to the right display.
+ if (displayId != DEFAULT_DISPLAY) {
+ // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
+ // for update resources & configuration correctly when show soft input
+ // in non-default display.
+ updateDisplay(displayId);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
*
* <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
*/
@@ -930,6 +949,9 @@
// If the previous IME has occupied non-empty inset in the screen, we need to decide whether
// we continue to use the same size of the inset or update it
mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
+ // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
+ // for update resources & configuration correctly when show soft input
+ // in non-default display.
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
@@ -1010,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/net/Uri.java b/core/java/android/net/Uri.java
index 9bcc600..40465ce 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -197,7 +197,7 @@
*
* <p>Example: "//www.google.com/search?q=android"
*
- * @return the decoded scheme-specific-part
+ * @return the encoded scheme-specific-part
*/
public abstract String getEncodedSchemeSpecificPart();
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/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 0fef78d..3d4c00c 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -1601,12 +1601,13 @@
private void readFromParcelInner(Parcel parcel, int length) {
if (length < 0) {
throw new RuntimeException("Bad length in parcel: " + length);
-
} else if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
mParcelledByNative = false;
return;
+ } else if (length % 4 != 0) {
+ throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
}
final int magic = parcel.readInt();
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 347f60f..3c43fd18 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
@@ -165,6 +166,7 @@
*
* @hide
*/
+ @SystemApi
public static File getOemDirectory() {
return DIR_OEM_ROOT;
}
@@ -175,6 +177,7 @@
*
* @hide
*/
+ @SystemApi
public static File getOdmDirectory() {
return DIR_ODM_ROOT;
}
@@ -184,6 +187,7 @@
* software that should persist across simple reflashing of the "system" partition.
* @hide
*/
+ @SystemApi
public static File getVendorDirectory() {
return DIR_VENDOR_ROOT;
}
@@ -194,6 +198,7 @@
*
* @hide
*/
+ @SystemApi
public static File getProductDirectory() {
return DIR_PRODUCT_ROOT;
}
@@ -204,6 +209,7 @@
*
* @hide
*/
+ @SystemApi
public static File getProductServicesDirectory() {
return DIR_PRODUCT_SERVICES_ROOT;
}
@@ -1063,7 +1069,6 @@
return cur;
}
-
/**
* If the given path exists on emulated external storage, return the
* translated backing path hosted on internal storage. This bypasses any
@@ -1074,8 +1079,10 @@
* must hold {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}
* permission.
*
+ * @deprecated disabled now that FUSE has been replaced by sdcardfs
* @hide
*/
+ @Deprecated
public static File maybeTranslateEmulatedPathToInternal(File path) {
return StorageManager.maybeTranslateEmulatedPathToInternal(path);
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 1c8029c..a9cb0d9 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,18 @@
package android.os;
+import static android.os.ParcelFileDescriptor.MODE_APPEND;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.O_APPEND;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDONLY;
+import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_TRUNC;
+import static android.system.OsConstants.O_WRONLY;
import static android.system.OsConstants.SPLICE_F_MORE;
import static android.system.OsConstants.SPLICE_F_MOVE;
import static android.system.OsConstants.S_ISFIFO;
@@ -1061,8 +1073,13 @@
mimeTypeFromExt = ContentResolver.MIME_TYPE_DEFAULT;
}
- final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
- mimeType);
+ final String extFromMimeType;
+ if (ContentResolver.MIME_TYPE_DEFAULT.equals(mimeType)) {
+ extFromMimeType = null;
+ } else {
+ extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
+ }
+
if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
// Extension maps back to requested MIME type; allow it
} else {
@@ -1180,6 +1197,96 @@
}
/** {@hide} */
+ public static int translateModeStringToPosix(String mode) {
+ int res = 0;
+ if (mode.startsWith("rw")) {
+ res |= O_RDWR | O_CREAT;
+ } else if (mode.startsWith("w")) {
+ res |= O_WRONLY | O_CREAT;
+ } else if (mode.startsWith("r")) {
+ res |= O_RDONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if (mode.indexOf('t') != -1) {
+ res |= O_TRUNC;
+ }
+ if (mode.indexOf('a') != -1) {
+ res |= O_APPEND;
+ }
+ return res;
+ }
+
+ /** {@hide} */
+ public static String translateModePosixToString(int mode) {
+ String res = "";
+ if ((mode & O_RDWR) == O_RDWR) {
+ res += "rw";
+ } else if ((mode & O_WRONLY) == O_WRONLY) {
+ res += "w";
+ } else if ((mode & O_RDONLY) == O_RDONLY) {
+ res += "r";
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if ((mode & O_TRUNC) == O_TRUNC) {
+ res += "t";
+ }
+ if ((mode & O_APPEND) == O_APPEND) {
+ res += "a";
+ }
+ return res;
+ }
+
+ /** {@hide} */
+ public static int translateModePosixToPfd(int mode) {
+ int res = 0;
+ if ((mode & O_RDWR) == O_RDWR) {
+ res |= MODE_READ_WRITE;
+ } else if ((mode & O_WRONLY) == O_WRONLY) {
+ res |= MODE_WRITE_ONLY;
+ } else if ((mode & O_RDONLY) == O_RDONLY) {
+ res |= MODE_READ_ONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if ((mode & O_CREAT) == O_CREAT) {
+ res |= MODE_CREATE;
+ }
+ if ((mode & O_TRUNC) == O_TRUNC) {
+ res |= MODE_TRUNCATE;
+ }
+ if ((mode & O_APPEND) == O_APPEND) {
+ res |= MODE_APPEND;
+ }
+ return res;
+ }
+
+ /** {@hide} */
+ public static int translateModePfdToPosix(int mode) {
+ int res = 0;
+ if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
+ res |= O_RDWR;
+ } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
+ res |= O_WRONLY;
+ } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
+ res |= O_RDONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ if ((mode & MODE_CREATE) == MODE_CREATE) {
+ res |= O_CREAT;
+ }
+ if ((mode & MODE_TRUNCATE) == MODE_TRUNCATE) {
+ res |= O_TRUNC;
+ }
+ if ((mode & MODE_APPEND) == MODE_APPEND) {
+ res |= O_APPEND;
+ }
+ return res;
+ }
+
+ /** {@hide} */
@VisibleForTesting
public static class MemoryPipe extends Thread implements AutoCloseable {
private final FileDescriptor[] pipe;
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 9b202f2..e03af9d 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -33,7 +33,7 @@
* them as they come out of the message queue.
*
* <p>There are two main uses for a Handler: (1) to schedule messages and
- * runnables to be executed as some point in the future; and (2) to enqueue
+ * runnables to be executed at some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.
*
* <p>Scheduling messages is accomplished with the
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c9edc53..a54c589 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -559,28 +559,7 @@
* @throws IllegalArgumentException if the given string does not match a known file mode.
*/
public static int parseMode(String mode) {
- final int modeBits;
- if ("r".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
- } else if ("w".equals(mode) || "wt".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_TRUNCATE;
- } else if ("wa".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_APPEND;
- } else if ("rw".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_WRITE
- | ParcelFileDescriptor.MODE_CREATE;
- } else if ("rwt".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_WRITE
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_TRUNCATE;
- } else {
- throw new IllegalArgumentException("Bad mode '" + mode + "'");
- }
- return modeBits;
+ return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
}
/**
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/UserHandle.java b/core/java/android/os/UserHandle.java
index 995156a..4fe2d58 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -38,12 +38,16 @@
public static final @UserIdInt int USER_ALL = -1;
/** @hide A user handle to indicate all users on the device */
+ @SystemApi
+ @TestApi
public static final UserHandle ALL = new UserHandle(USER_ALL);
/** @hide A user id to indicate the currently active user */
public static final @UserIdInt int USER_CURRENT = -2;
/** @hide A user handle to indicate the current user of the device */
+ @SystemApi
+ @TestApi
public static final UserHandle CURRENT = new UserHandle(USER_CURRENT);
/** @hide A user id to indicate that we would like to send to the current
@@ -83,6 +87,7 @@
public static final int USER_SERIAL_SYSTEM = 0;
/** @hide A user handle to indicate the "system" user of the device */
+ @SystemApi
@TestApi
public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
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/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8d5017b..50ca4ab 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1521,7 +1521,11 @@
return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
}
- /** {@hide} */
+ /**
+ * @deprecated disabled now that FUSE has been replaced by sdcardfs
+ * @hide
+ */
+ @Deprecated
public static File maybeTranslateEmulatedPathToInternal(File path) {
// Disabled now that FUSE has been replaced by sdcardfs
return path;
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 e007398..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>
@@ -6508,6 +6508,25 @@
public static final String MULTI_PRESS_TIMEOUT = "multi_press_timeout";
/**
+ * Whether the user specifies a minimum ui timeout to override minimum ui timeout of
+ * accessibility service
+ *
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ public static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED =
+ "accessibility_minimum_ui_timeout_enabled";
+
+ /**
+ * Setting that specifies ui minimum timeout in milliseconds.
+ *
+ * @see #ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED
+ * @hide
+ */
+ public static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS =
+ "accessibility_minimum_ui_timeout_ms";
+
+ /**
* List of the enabled print services.
*
* N and beyond uses {@link #DISABLED_PRINT_SERVICES}. But this might be used in an upgrade
@@ -8195,6 +8214,8 @@
ZEN_SETTINGS_SUGGESTION_VIEWED,
CHARGING_SOUNDS_ENABLED,
CHARGING_VIBRATION_ENABLED,
+ ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED,
+ ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS,
};
/**
@@ -8349,6 +8370,8 @@
VALIDATORS.put(ZEN_SETTINGS_SUGGESTION_VIEWED, BOOLEAN_VALIDATOR);
VALIDATORS.put(CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR);
}
/**
@@ -11514,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..663bdcb 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;
@@ -70,6 +74,7 @@
* @param message error message to be displayed to the user.
*/
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..e252e96 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;
@@ -89,6 +93,7 @@
* @param message error message to be displayed to the user.
*/
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 d9ed2aa..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 */
@@ -989,6 +989,21 @@
}
/**
+ * Clears listener hints set via {@link #getCurrentListenerHints()}.
+ *
+ * <p>The service should wait for the {@link #onListenerConnected()} event
+ * before performing this operation.
+ */
+ public final void clearRequestedListenerHints() {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().clearRequestedListenerHints(mWrapper);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* Sets the desired {@link #getCurrentListenerHints() listener hints}.
*
* <p>
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f90eb14..e819c96 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,6 +16,7 @@
package android.service.notification;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
@@ -763,6 +764,145 @@
}
};
+ /**
+ * @return notification policy based on manual and automatic rules
+ */
+ public Policy getConsolidatedNotificationPolicy() {
+ ZenPolicy policy = new ZenPolicy();
+
+ // assumption: manual rule always uses the default policy
+ for (ZenRule rule : automaticRules.values()) {
+ if (rule.isAutomaticActive()) {
+ if (rule.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ policy.apply(rule.zenPolicy);
+ }
+ }
+ }
+
+ return toNotificationPolicy(policy);
+ }
+
+ /**
+ * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its
+ * defaults for all unset values in zenPolicy
+ */
+ public Policy toNotificationPolicy(ZenPolicy zenPolicy) {
+ NotificationManager.Policy defaultPolicy = toNotificationPolicy();
+ int priorityCategories = 0;
+ int suppressedVisualEffects = 0;
+ int callSenders = defaultPolicy.priorityCallSenders;
+ int messageSenders = defaultPolicy.priorityMessageSenders;
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
+ priorityCategories |= PRIORITY_CATEGORY_REMINDERS;
+ }
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS, defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+ }
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
+ messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityMessageSenders());
+ }
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
+ messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders());
+ }
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS,
+ defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+ }
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS, defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
+ }
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MEDIA, defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
+ }
+
+ if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM,
+ isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_SYSTEM, defaultPolicy))) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
+ }
+
+ if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
+ defaultPolicy))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ }
+
+ if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_LIGHTS,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
+ defaultPolicy))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
+ }
+
+ if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK,
+ defaultPolicy))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
+ }
+
+ if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_STATUS_BAR,
+ defaultPolicy))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+ }
+
+ if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_BADGE,
+ defaultPolicy))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
+ }
+
+ if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_AMBIENT,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
+ defaultPolicy))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+ }
+
+ if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
+ isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST,
+ defaultPolicy))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+ }
+
+ return new NotificationManager.Policy(priorityCategories, callSenders,
+ messageSenders, suppressedVisualEffects);
+ }
+
+ private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) {
+ return (policy.priorityCategories & categoryType) != 0;
+ }
+
+ private boolean isVisualEffectAllowed(int visualEffect, Policy policy) {
+ return (policy.suppressedVisualEffects & visualEffect) == 0;
+ }
+
+ private int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders) {
+ switch (senders) {
+ case ZenPolicy.PEOPLE_TYPE_ANYONE:
+ return Policy.PRIORITY_SENDERS_ANY;
+ case ZenPolicy.PEOPLE_TYPE_CONTACTS:
+ return Policy.PRIORITY_SENDERS_CONTACTS;
+ case ZenPolicy.PEOPLE_TYPE_STARRED:
+ default:
+ return Policy.PRIORITY_SENDERS_STARRED;
+ }
+
+ }
+
public Policy toNotificationPolicy() {
int priorityCategories = 0;
int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
@@ -1315,6 +1455,7 @@
@UnsupportedAppUsage
public long creationTime; // required for automatic
public String enabler; // package name, only used for manual rules.
+ public ZenPolicy zenPolicy;
public ZenRule() { }
@@ -1335,6 +1476,7 @@
if (source.readInt() == 1) {
enabler = source.readString();
}
+ zenPolicy = source.readParcelable(null);
}
@Override
@@ -1369,6 +1511,7 @@
} else {
dest.writeInt(0);
}
+ dest.writeParcelable(zenPolicy, 0);
}
@Override
@@ -1384,6 +1527,7 @@
.append(",component=").append(component)
.append(",creationTime=").append(creationTime)
.append(",enabler=").append(enabler)
+ .append(",zenPolicy=").append(zenPolicy)
.append(']').toString();
}
@@ -1407,7 +1551,9 @@
if (component != null) {
component.writeToProto(proto, ZenRuleProto.COMPONENT);
}
-
+ if (zenPolicy != null) {
+ zenPolicy.writeToProto(proto, ZenRuleProto.ZEN_POLICY);
+ }
proto.end(token);
}
@@ -1454,9 +1600,12 @@
if (creationTime != to.creationTime) {
d.addLine(item, "creationTime", creationTime, to.creationTime);
}
- if (enabler != to.enabler) {
+ if (!Objects.equals(enabler, to.enabler)) {
d.addLine(item, "enabler", enabler, to.enabler);
}
+ if (!Objects.equals(zenPolicy, to.zenPolicy)) {
+ d.addLine(item, "zenPolicy", zenPolicy, to.zenPolicy);
+ }
}
@Override
@@ -1472,13 +1621,14 @@
&& Objects.equals(other.condition, condition)
&& Objects.equals(other.component, component)
&& Objects.equals(other.id, id)
- && Objects.equals(other.enabler, enabler);
+ && Objects.equals(other.enabler, enabler)
+ && Objects.equals(other.zenPolicy, zenPolicy);
}
@Override
public int hashCode() {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, id, enabler);
+ component, id, enabler, zenPolicy);
}
public boolean isAutomaticActive() {
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
new file mode 100644
index 0000000..1ccf529
--- /dev/null
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -0,0 +1,897 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.service.notification;
+
+import android.annotation.IntDef;
+import android.app.Notification;
+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;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * ZenPolicy determines whether to allow certain notifications and their corresponding sounds to
+ * play when a device is in Do Not Disturb mode.
+ * ZenPolicy also dictates the visual effects of notifications that are intercepted when
+ * a device is in Do Not Disturb mode.
+ */
+public final class ZenPolicy implements Parcelable {
+ private ArrayList<Integer> mPriorityCategories;
+ private ArrayList<Integer> mVisualEffects;
+ private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET;
+ private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET;
+
+ /** @hide */
+ @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = {
+ PRIORITY_CATEGORY_REMINDERS,
+ PRIORITY_CATEGORY_EVENTS,
+ PRIORITY_CATEGORY_MESSAGES,
+ PRIORITY_CATEGORY_CALLS,
+ PRIORITY_CATEGORY_REPEAT_CALLERS,
+ PRIORITY_CATEGORY_ALARMS,
+ PRIORITY_CATEGORY_MEDIA,
+ PRIORITY_CATEGORY_SYSTEM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PriorityCategory {}
+
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_REMINDERS = 0;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_EVENTS = 1;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_MESSAGES = 2;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_CALLS = 3;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 4;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_ALARMS = 5;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_MEDIA = 6;
+ /** @hide */
+ public static final int PRIORITY_CATEGORY_SYSTEM = 7;
+
+ /** @hide */
+ @IntDef(prefix = { "VISUAL_EFFECT_" }, value = {
+ VISUAL_EFFECT_FULL_SCREEN_INTENT,
+ VISUAL_EFFECT_LIGHTS,
+ VISUAL_EFFECT_PEEK,
+ VISUAL_EFFECT_STATUS_BAR,
+ VISUAL_EFFECT_BADGE,
+ VISUAL_EFFECT_AMBIENT,
+ VISUAL_EFFECT_NOTIFICATION_LIST,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VisualEffect {}
+
+ /** @hide */
+ public static final int VISUAL_EFFECT_FULL_SCREEN_INTENT = 0;
+ /** @hide */
+ public static final int VISUAL_EFFECT_LIGHTS = 1;
+ /** @hide */
+ public static final int VISUAL_EFFECT_PEEK = 2;
+ /** @hide */
+ public static final int VISUAL_EFFECT_STATUS_BAR = 3;
+ /** @hide */
+ public static final int VISUAL_EFFECT_BADGE = 4;
+ /** @hide */
+ public static final int VISUAL_EFFECT_AMBIENT = 5;
+ /** @hide */
+ public static final int VISUAL_EFFECT_NOTIFICATION_LIST = 6;
+
+ /** @hide */
+ @IntDef(prefix = { "PEOPLE_TYPE_" }, value = {
+ PEOPLE_TYPE_UNSET,
+ PEOPLE_TYPE_ANYONE,
+ PEOPLE_TYPE_CONTACTS,
+ PEOPLE_TYPE_STARRED,
+ PEOPLE_TYPE_NONE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PeopleType {}
+
+ /**
+ * Used to indicate no preference for the type of people that can bypass dnd for either
+ * calls or messages.
+ */
+ public static final int PEOPLE_TYPE_UNSET = 0;
+
+ /**
+ * Used to indicate all calls or messages can bypass dnd.
+ */
+ public static final int PEOPLE_TYPE_ANYONE = 1;
+
+ /**
+ * Used to indicate calls or messages from contacts can bypass dnd.
+ */
+ public static final int PEOPLE_TYPE_CONTACTS = 2;
+
+ /**
+ * Used to indicate calls or messages from starred contacts can bypass dnd.
+ */
+ public static final int PEOPLE_TYPE_STARRED = 3;
+
+ /**
+ * Used to indicate no calls or messages can bypass dnd.
+ */
+ public static final int PEOPLE_TYPE_NONE = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_UNSET,
+ STATE_ALLOW,
+ STATE_DISALLOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
+ /**
+ * Indicates no preference for whether a type of sound or visual effect is or isn't allowed
+ * to play/show when DND is active. Will default to the current set policy.
+ */
+ public static final int STATE_UNSET = 0;
+
+ /**
+ * Indicates a type of sound or visual effect is allowed to play/show when DND is active.
+ */
+ public static final int STATE_ALLOW = 1;
+
+ /**
+ * Indicates a type of sound or visual effect is not allowed to play/show when DND is active.
+ */
+ public static final int STATE_DISALLOW = 2;
+
+ /** @hide */
+ public ZenPolicy() {
+ mPriorityCategories = new ArrayList<>(Collections.nCopies(8, 0));
+ mVisualEffects = new ArrayList<>(Collections.nCopies(7, 0));
+ }
+
+ /**
+ * Message senders that can bypass DND.
+ * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
+ * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
+ */
+ public @PeopleType int getPriorityMessageSenders() {
+ return mPriorityMessages;
+ }
+
+ /**
+ * Callers that can bypass DND.
+ * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
+ * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
+ */
+ public @PeopleType int getPriorityCallSenders() {
+ return mPriorityCalls;
+ }
+
+ /**
+ * Whether this policy wants to allow notifications with category
+ * {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear
+ * or to intercept them when DND is active.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryReminders() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_REMINDERS);
+ }
+
+ /**
+ * Whether this policy wants to allow notifications with category
+ * {@link Notification#CATEGORY_EVENT} to play sounds and visually appear
+ * or to intercept them when DND is active.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryEvents() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_EVENTS);
+ }
+
+ /**
+ * Whether this policy wants to allow notifications with category
+ * {@link Notification#CATEGORY_MESSAGE} to play sounds and visually appear
+ * or to intercept them when DND is active. Types of message senders that are allowed
+ * are specified by {@link #getPriorityMessageSenders}.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryMessages() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_MESSAGES);
+ }
+
+ /**
+ * Whether this policy wants to allow notifications with category
+ * {@link Notification#CATEGORY_CALL} to play sounds and visually appear
+ * or to intercept them when DND is active. Types of callers that are allowed
+ * are specified by {@link #getPriorityCallSenders()}.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryCalls() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_CALLS);
+ }
+
+ /**
+ * Whether this policy wants to allow repeat callers (notifications with category
+ * {@link Notification#CATEGORY_CALL} that have recently called) to play sounds and
+ * visually appear or to intercept them when DND is active.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryRepeatCallers() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_REPEAT_CALLERS);
+ }
+
+ /**
+ * Whether this policy wants to allow notifications with category
+ * {@link Notification#CATEGORY_ALARM} to play sounds and visually appear
+ * or to intercept them when DND is active.
+ * When alarms are {@link #STATE_DISALLOW disallowed}, the alarm stream will be muted when DND
+ * is active.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryAlarms() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_ALARMS);
+ }
+
+ /**
+ * Whether this policy wants to allow media notifications to play sounds and visually appear
+ * or to intercept them when DND is active.
+ * When media is {@link #STATE_DISALLOW disallowed}, the media stream will be muted when DND is
+ * active.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategoryMedia() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_MEDIA);
+ }
+
+ /**
+ * Whether this policy wants to allow system sounds when DND is active.
+ * When system is {@link #STATE_DISALLOW}, the system stream will be muted when DND is active.
+ * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+ */
+ public @State int getPriorityCategorySystem() {
+ return mPriorityCategories.get(PRIORITY_CATEGORY_SYSTEM);
+ }
+
+ /**
+ * Whether this policy allows {@link Notification#fullScreenIntent full screen intents} from
+ * notifications intercepted by DND.
+ */
+ public @State int getVisualEffectFullScreenIntent() {
+ return mVisualEffects.get(VISUAL_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ /**
+ * Whether this policy allows {@link NotificationChannel#shouldShowLights() notification
+ * lights} from notifications intercepted by DND.
+ */
+ public @State int getVisualEffectLights() {
+ return mVisualEffects.get(VISUAL_EFFECT_LIGHTS);
+ }
+
+ /**
+ * Whether this policy allows peeking from notifications intercepted by DND.
+ */
+ public @State int getVisualEffectPeek() {
+ return mVisualEffects.get(VISUAL_EFFECT_PEEK);
+ }
+
+ /**
+ * Whether this policy allows notifications intercepted by DND from appearing in the status bar
+ * on devices that support status bars.
+ */
+ public @State int getVisualEffectStatusBar() {
+ return mVisualEffects.get(VISUAL_EFFECT_STATUS_BAR);
+ }
+
+ /**
+ * Whether this policy allows {@link NotificationChannel#canShowBadge() badges} from
+ * notifications intercepted by DND on devices that support badging.
+ */
+ public @State int getVisualEffectBadge() {
+ return mVisualEffects.get(VISUAL_EFFECT_BADGE);
+ }
+
+ /**
+ * Whether this policy allows notifications intercepted by DND from appearing on ambient
+ * displays on devices that support ambient display.
+ */
+ public @State int getVisualEffectAmbient() {
+ return mVisualEffects.get(VISUAL_EFFECT_AMBIENT);
+ }
+
+ /**
+ * Whether this policy allows notifications intercepted by DND from appearing in notification
+ * list views like the notification shade or lockscreen on devices that support those
+ * views.
+ */
+ public @State int getVisualEffectNotificationList() {
+ return mVisualEffects.get(VISUAL_EFFECT_NOTIFICATION_LIST);
+ }
+
+ /**
+ * Builder class for {@link ZenPolicy} objects.
+ * Provides a convenient way to set the various fields of a {@link ZenPolicy}. If a field
+ * is not set, it is (@link STATE_UNSET} and will not change the current set policy.
+ */
+ public static class Builder {
+ private ZenPolicy mZenPolicy;
+
+ public Builder() {
+ mZenPolicy = new ZenPolicy();
+ }
+
+ /**
+ * Builds the current ZenPolicy.
+ */
+ public ZenPolicy build() {
+ return mZenPolicy.copy();
+ }
+
+ /**
+ * Allows all notifications to bypass DND and unmutes all streams.
+ */
+ public Builder allowAllSounds() {
+ for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
+ mZenPolicy.mPriorityCategories.set(i, STATE_ALLOW);
+ }
+ mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE;
+ mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE;
+ return this;
+ }
+
+ /**
+ * Intercepts all notifications and prevents them from playing sounds
+ * when DND is active. Also mutes alarm, system and media streams.
+ * Notification channels can still play sounds only if they
+ * {@link NotificationChannel#canBypassDnd can bypass DND}. If no channels can bypass DND,
+ * the ringer stream is also muted.
+ */
+ public Builder disallowAllSounds() {
+ for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
+ mZenPolicy.mPriorityCategories.set(i, STATE_DISALLOW);
+ }
+ mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE;
+ mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE;
+ return this;
+ }
+
+ /**
+ * Allows notifications intercepted by DND to show on all surfaces when DND is active.
+ */
+ public Builder showAllVisualEffects() {
+ for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
+ mZenPolicy.mVisualEffects.set(i, STATE_ALLOW);
+ }
+ return this;
+ }
+
+ /**
+ * Disallows notifications intercepted by DND from showing when DND is active.
+ */
+ public Builder hideAllVisualEffects() {
+ for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
+ mZenPolicy.mVisualEffects.set(i, STATE_DISALLOW);
+ }
+ return this;
+ }
+
+ /**
+ * Unsets a priority category, neither allowing or disallowing. When applying this policy,
+ * unset categories will default to the current applied policy.
+ * @hide
+ */
+ public Builder unsetPriorityCategory(@PriorityCategory int category) {
+ mZenPolicy.mPriorityCategories.set(category, STATE_UNSET);
+
+ if (category == PRIORITY_CATEGORY_MESSAGES) {
+ mZenPolicy.mPriorityMessages = STATE_UNSET;
+ } else if (category == PRIORITY_CATEGORY_CALLS) {
+ mZenPolicy.mPriorityCalls = STATE_UNSET;
+ }
+
+ return this;
+ }
+
+ /**
+ * Unsets a visual effect, neither allowing or disallowing. When applying this policy,
+ * unset effects will default to the current applied policy.
+ * @hide
+ */
+ public Builder unsetVisualEffect(@VisualEffect int effect) {
+ mZenPolicy.mVisualEffects.set(effect, STATE_UNSET);
+ return this;
+ }
+
+ /**
+ * Whether to allow notifications with category {@link Notification#CATEGORY_REMINDER}
+ * to play sounds and visually appear or to intercept them when DND is active.
+ */
+ public Builder allowReminders(boolean allow) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REMINDERS,
+ allow ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether to allow notifications with category {@link Notification#CATEGORY_EVENT}
+ * to play sounds and visually appear or to intercept them when DND is active.
+ */
+ public Builder allowEvents(boolean allow) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_EVENTS,
+ allow ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE}
+ * that match audienceType to play sounds and visually appear or to intercept
+ * them when DND is active.
+ * @param audienceType message senders that are allowed to bypass DND
+ */
+ public Builder allowMessages(@PeopleType int audienceType) {
+ if (audienceType == STATE_UNSET) {
+ return unsetPriorityCategory(PRIORITY_CATEGORY_MESSAGES);
+ }
+
+ if (audienceType == PEOPLE_TYPE_NONE) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_DISALLOW);
+ } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS
+ || audienceType == PEOPLE_TYPE_STARRED) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_ALLOW);
+ } else {
+ return this;
+ }
+
+ mZenPolicy.mPriorityMessages = audienceType;
+ return this;
+ }
+
+ /**
+ * Whether to allow notifications with category {@link Notification#CATEGORY_CALL}
+ * that match audienceType to play sounds and visually appear or to intercept
+ * them when DND is active.
+ * @param audienceType callers that are allowed to bypass DND
+ */
+ public Builder allowCalls(@PeopleType int audienceType) {
+ if (audienceType == STATE_UNSET) {
+ return unsetPriorityCategory(PRIORITY_CATEGORY_CALLS);
+ }
+
+ if (audienceType == PEOPLE_TYPE_NONE) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_DISALLOW);
+ } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS
+ || audienceType == PEOPLE_TYPE_STARRED) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_ALLOW);
+ } else {
+ return this;
+ }
+
+ mZenPolicy.mPriorityCalls = audienceType;
+ return this;
+ }
+
+ /**
+ * Whether to allow repeat callers (notifications with category
+ * {@link Notification#CATEGORY_CALL} that have recently called
+ * to play sounds and visually appear.
+ */
+ public Builder allowRepeatCallers(boolean allow) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REPEAT_CALLERS,
+ allow ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+
+ /**
+ * Whether to allow notifications with category {@link Notification#CATEGORY_ALARM}
+ * to play sounds and visually appear or to intercept them when DND is active.
+ * Disallowing alarms will mute the alarm stream when DND is active.
+ */
+ public Builder allowAlarms(boolean allow) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_ALARMS,
+ allow ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether to allow media notifications to play sounds and visually
+ * appear or to intercept them when DND is active.
+ * Disallowing media will mute the media stream when DND is active.
+ */
+ public Builder allowMedia(boolean allow) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MEDIA,
+ allow ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether to allow system sounds to play when DND is active.
+ * Disallowing system sounds will mute the system stream when DND is active.
+ */
+ public Builder allowSystem(boolean allow) {
+ mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_SYSTEM,
+ allow ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted
+ * by DND are shown.
+ */
+ public Builder showFullScreenIntent(boolean show) {
+ mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_FULL_SCREEN_INTENT,
+ show ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether {@link NotificationChannel#shouldShowLights() notification lights} from
+ * notifications intercepted by DND are blocked.
+ */
+ public Builder showLights(boolean show) {
+ mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_LIGHTS,
+ show ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether notifications intercepted by DND are prevented from peeking.
+ */
+ public Builder showPeeking(boolean show) {
+ mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_PEEK,
+ show ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether notifications intercepted by DND are prevented from appearing in the status bar
+ * on devices that support status bars.
+ */
+ public Builder showStatusBarIcons(boolean show) {
+ mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_STATUS_BAR,
+ show ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether {@link NotificationChannel#canShowBadge() badges} from
+ * notifications intercepted by DND are allowed on devices that support badging.
+ */
+ public Builder showBadges(boolean show) {
+ mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_BADGE,
+ show ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether notification intercepted by DND are prevented from appearing on ambient displays
+ * on devices that support ambient display.
+ */
+ public Builder showInAmbientDisplay(boolean show) {
+ mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_AMBIENT,
+ show ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+
+ /**
+ * Whether notification intercepted by DND are prevented from appearing in notification
+ * list views like the notification shade or lockscreen on devices that support those
+ * views.
+ */
+ public Builder showInNotificationList(boolean show) {
+ mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_NOTIFICATION_LIST,
+ show ? STATE_ALLOW : STATE_DISALLOW);
+ return this;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeList(mPriorityCategories);
+ dest.writeList(mVisualEffects);
+ dest.writeInt(mPriorityCalls);
+ dest.writeInt(mPriorityMessages);
+ }
+
+ public static final Parcelable.Creator<ZenPolicy> CREATOR =
+ new Parcelable.Creator<ZenPolicy>() {
+ @Override
+ public ZenPolicy createFromParcel(Parcel source) {
+ ZenPolicy policy = new ZenPolicy();
+ policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader());
+ policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader());
+ policy.mPriorityCalls = source.readInt();
+ policy.mPriorityMessages = source.readInt();
+ return policy;
+ }
+
+ @Override
+ public ZenPolicy[] newArray(int size) {
+ return new ZenPolicy[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return new StringBuilder(ZenPolicy.class.getSimpleName())
+ .append('{')
+ .append("priorityCategories=[").append(priorityCategoriesToString())
+ .append("], visualEffects=[").append(visualEffectsToString())
+ .append(", priorityCalls=").append(stateToString(mPriorityCalls))
+ .append("], priorityMessages=").append(stateToString(mPriorityMessages))
+ .append('}')
+ .toString();
+ }
+
+
+ private String priorityCategoriesToString() {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < mPriorityCategories.size(); i++) {
+ if (mPriorityCategories.get(i) != STATE_UNSET) {
+ builder.append(indexToCategory(i))
+ .append("=")
+ .append(stateToString(mPriorityCategories.get(i)))
+ .append(" ");
+ }
+
+ }
+ return builder.toString();
+ }
+
+ private String visualEffectsToString() {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < mVisualEffects.size(); i++) {
+ if (mVisualEffects.get(i) != STATE_UNSET) {
+ builder.append(indexToVisualEffect(i))
+ .append("=")
+ .append(stateToString(mVisualEffects.get(i)))
+ .append(" ");
+ }
+
+ }
+ return builder.toString();
+ }
+
+ private String indexToVisualEffect(@VisualEffect int visualEffectIndex) {
+ switch (visualEffectIndex) {
+ case VISUAL_EFFECT_FULL_SCREEN_INTENT:
+ return "fullScreenIntent";
+ case VISUAL_EFFECT_LIGHTS:
+ return "lights";
+ case VISUAL_EFFECT_PEEK:
+ return "peek";
+ case VISUAL_EFFECT_STATUS_BAR:
+ return "statusBar";
+ case VISUAL_EFFECT_BADGE:
+ return "badge";
+ case VISUAL_EFFECT_AMBIENT:
+ return "ambient";
+ case VISUAL_EFFECT_NOTIFICATION_LIST:
+ return "notificationList";
+ }
+ return null;
+ }
+
+ private String indexToCategory(@PriorityCategory int categoryIndex) {
+ switch (categoryIndex) {
+ case PRIORITY_CATEGORY_REMINDERS:
+ return "reminders";
+ case PRIORITY_CATEGORY_EVENTS:
+ return "events";
+ case PRIORITY_CATEGORY_MESSAGES:
+ return "messages";
+ case PRIORITY_CATEGORY_CALLS:
+ return "calls";
+ case PRIORITY_CATEGORY_REPEAT_CALLERS:
+ return "repeatCallers";
+ case PRIORITY_CATEGORY_ALARMS:
+ return "alarms";
+ case PRIORITY_CATEGORY_MEDIA:
+ return "media";
+ case PRIORITY_CATEGORY_SYSTEM:
+ return "system";
+ }
+ return null;
+ }
+
+ private String stateToString(@State int state) {
+ switch (state) {
+ case STATE_UNSET:
+ return "unset";
+ case STATE_DISALLOW:
+ return "disallow";
+ case STATE_ALLOW:
+ return "allow";
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ZenPolicy)) return false;
+ if (o == this) return true;
+ final ZenPolicy other = (ZenPolicy) o;
+
+ return Objects.equals(other.mPriorityCategories, mPriorityCategories)
+ && Objects.equals(other.mVisualEffects, mVisualEffects)
+ && other.mPriorityCalls == mPriorityCalls
+ && other.mPriorityMessages == mPriorityMessages;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages);
+ }
+
+ private @ZenPolicy.State int getZenPolicyPriorityCategoryState(@PriorityCategory int
+ category) {
+ switch (category) {
+ case PRIORITY_CATEGORY_REMINDERS:
+ return getPriorityCategoryReminders();
+ case PRIORITY_CATEGORY_EVENTS:
+ return getPriorityCategoryEvents();
+ case PRIORITY_CATEGORY_MESSAGES:
+ return getPriorityCategoryMessages();
+ case PRIORITY_CATEGORY_CALLS:
+ return getPriorityCategoryCalls();
+ case PRIORITY_CATEGORY_REPEAT_CALLERS:
+ return getPriorityCategoryRepeatCallers();
+ case PRIORITY_CATEGORY_ALARMS:
+ return getPriorityCategoryAlarms();
+ case PRIORITY_CATEGORY_MEDIA:
+ return getPriorityCategoryMedia();
+ case PRIORITY_CATEGORY_SYSTEM:
+ return getPriorityCategorySystem();
+ }
+ return -1;
+ }
+
+ private @ZenPolicy.State int getZenPolicyVisualEffectState(@VisualEffect int effect) {
+ switch (effect) {
+ case VISUAL_EFFECT_FULL_SCREEN_INTENT:
+ return getVisualEffectFullScreenIntent();
+ case VISUAL_EFFECT_LIGHTS:
+ return getVisualEffectLights();
+ case VISUAL_EFFECT_PEEK:
+ return getVisualEffectPeek();
+ case VISUAL_EFFECT_STATUS_BAR:
+ return getVisualEffectStatusBar();
+ case VISUAL_EFFECT_BADGE:
+ return getVisualEffectBadge();
+ case VISUAL_EFFECT_AMBIENT:
+ return getVisualEffectAmbient();
+ case VISUAL_EFFECT_NOTIFICATION_LIST:
+ return getVisualEffectNotificationList();
+ }
+ return -1;
+ }
+
+ /** @hide */
+ public boolean isCategoryAllowed(@PriorityCategory int category, boolean defaultVal) {
+ switch (getZenPolicyPriorityCategoryState(category)) {
+ case ZenPolicy.STATE_ALLOW:
+ return true;
+ case ZenPolicy.STATE_DISALLOW:
+ return false;
+ default:
+ return defaultVal;
+ }
+ }
+
+ /** @hide */
+ public boolean isVisualEffectAllowed(@VisualEffect int effect, boolean defaultVal) {
+ switch (getZenPolicyVisualEffectState(effect)) {
+ case ZenPolicy.STATE_ALLOW:
+ return true;
+ case ZenPolicy.STATE_DISALLOW:
+ return false;
+ default:
+ return defaultVal;
+ }
+ }
+
+ /**
+ * Applies another policy on top of this policy
+ * @hide
+ */
+ public void apply(ZenPolicy policyToApply) {
+ // apply priority categories
+ for (int category = 0; category < mPriorityCategories.size(); category++) {
+ if (mPriorityCategories.get(category) == STATE_DISALLOW) {
+ // if a priority category is already disallowed by the policy, cannot allow
+ continue;
+ }
+
+ @State int newState = policyToApply.mPriorityCategories.get(category);
+ if (newState != STATE_UNSET) {
+ mPriorityCategories.set(category, newState);
+
+ if (category == PRIORITY_CATEGORY_MESSAGES
+ && mPriorityMessages < policyToApply.mPriorityMessages) {
+ mPriorityMessages = policyToApply.mPriorityMessages;
+ } else if (category == PRIORITY_CATEGORY_CALLS
+ && mPriorityCalls < policyToApply.mPriorityCalls) {
+ mPriorityCalls = policyToApply.mPriorityCalls;
+ }
+ }
+ }
+
+ // apply visual effects
+ for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) {
+ if (mVisualEffects.get(visualEffect) == STATE_DISALLOW) {
+ // if a visual effect is already disallowed by the policy, cannot allow
+ continue;
+ }
+
+ if (policyToApply.mVisualEffects.get(visualEffect) != STATE_UNSET) {
+ mVisualEffects.set(visualEffect, policyToApply.mVisualEffects.get(visualEffect));
+ }
+ }
+ }
+
+ /**
+ * @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
+ */
+ public ZenPolicy copy() {
+ final Parcel parcel = Parcel.obtain();
+ try {
+ writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+ }
+}
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/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 b5ade2a..183e833 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -35,18 +35,20 @@
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 {
DEFAULT_FLAGS = new HashMap<>();
- DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
DEFAULT_FLAGS.put("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/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
new file mode 100644
index 0000000..209451b
--- /dev/null
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -0,0 +1,978 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.proto;
+
+import android.annotation.TestApi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+/**
+ * Class to read to a protobuf stream.
+ *
+ * Each read method takes an ID code from the protoc generated classes
+ * and return a value of the field. To read a nested object, call #start
+ * and then #end when you are done.
+ *
+ * The ID codes have type information embedded into them, so if you call
+ * the incorrect function you will get an IllegalArgumentException.
+ *
+ * nextField will return the field number of the next field, which can be
+ * matched to the protoc generated ID code and used to determine how to
+ * read the next field.
+ *
+ * It is STRONGLY RECOMMENDED to read from the ProtoInputStream with a switch
+ * statement wrapped in a while loop. Additionally, it is worth logging or
+ * storing unexpected fields or ones that do not match the expected wire type
+ *
+ * ex:
+ * void parseFromProto(ProtoInputStream stream) {
+ * while(stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ * try {
+ * switch (stream.getFieldNumber()) {
+ * case (int) DummyProto.NAME:
+ * mName = stream.readString(DummyProto.NAME);
+ * break;
+ * case (int) DummyProto.VALUE:
+ * mValue = stream.readInt(DummyProto.VALUE);
+ * break;
+ * default:
+ * LOG(TAG, "Unhandled field in proto!\n"
+ * + ProtoUtils.currentFieldToString(stream));
+ * }
+ * } catch (WireTypeMismatchException wtme) {
+ * LOG(TAG, "Wire Type mismatch in proto!\n" + ProtoUtils.currentFieldToString(stream));
+ * }
+ * }
+ * }
+ *
+ * @hide
+ */
+@TestApi
+public final class ProtoInputStream extends ProtoStream {
+
+ public static final int NO_MORE_FIELDS = -1;
+
+ /**
+ * Our stream. If there is one.
+ */
+ private InputStream mStream;
+
+ /**
+ * The field number of the current field. Will be equal to NO_MORE_FIELDS if end of message is
+ * reached
+ */
+ private int mFieldNumber;
+
+ /**
+ * The wire type of the current field
+ */
+ private int mWireType;
+
+ private static final byte STATE_STARTED_FIELD_READ = 1 << 0;
+ private static final byte STATE_READING_PACKED = 1 << 1;
+ private static final byte STATE_FIELD_MISS = 2 << 1;
+
+ /**
+ * Tracks some boolean states for the proto input stream
+ * bit 0: Started Field Read, true - tag has been read, ready to read field data.
+ * false - field data has been read, reading to start next field.
+ * bit 1: Reading Packed Field, true - currently reading values from a packed field
+ * false - not reading from packed field.
+ */
+ private byte mState = 0;
+
+ /**
+ * Keeps track of the currently read nested Objects, for end object sanity checking and debug
+ */
+ private ArrayList<Long> mExpectedObjectTokenStack = null;
+
+ /**
+ * Current nesting depth of start calls.
+ */
+ private int mDepth = -1;
+
+ /**
+ * Buffer for the to be read data. If mStream is not null, it will be constantly refilled from
+ * the stream.
+ */
+ private byte[] mBuffer;
+
+ private static final int DEFAULT_BUFFER_SIZE = 8192;
+
+ /**
+ * Size of the buffer if reading from a stream.
+ */
+ private final int mBufferSize;
+
+ /**
+ * The number of bytes that have been skipped or dropped from the buffer.
+ */
+ private int mDiscardedBytes = 0;
+
+ /**
+ * Current offset in the buffer
+ * mOffset + mDiscardedBytes = current offset in proto binary
+ */
+ private int mOffset = 0;
+
+ /**
+ * Note the offset of the last byte in the buffer. Usually will equal the size of the buffer.
+ * mEnd + mDiscardedBytes = the last known byte offset + 1
+ */
+ private int mEnd = 0;
+
+ /**
+ * Packed repeated fields are not read in one go. mPackedEnd keeps track of where the packed
+ * field ends in the proto binary if current field is packed.
+ */
+ private int mPackedEnd = 0;
+
+ /**
+ * Construct a ProtoInputStream on top of an InputStream to read a proto. Also specify the
+ * number of bytes the ProtoInputStream will buffer from the input stream
+ *
+ * @param stream from which the proto is read
+ */
+ public ProtoInputStream(InputStream stream, int bufferSize) {
+ mStream = stream;
+ if (bufferSize > 0) {
+ mBufferSize = bufferSize;
+ } else {
+ mBufferSize = DEFAULT_BUFFER_SIZE;
+ }
+ mBuffer = new byte[mBufferSize];
+ }
+
+ /**
+ * Construct a ProtoInputStream on top of an InputStream to read a proto
+ *
+ * @param stream from which the proto is read
+ */
+ public ProtoInputStream(InputStream stream) {
+ this(stream, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Construct a ProtoInputStream to read a proto directly from a byte array
+ *
+ * @param buffer - the byte array to be parsed
+ */
+ public ProtoInputStream(byte[] buffer) {
+ mBufferSize = buffer.length;
+ mEnd = buffer.length;
+ mBuffer = buffer;
+ mStream = null;
+ }
+
+ /**
+ * Get the field number of the current field.
+ */
+ public int getFieldNumber() {
+ return mFieldNumber;
+ }
+
+ /**
+ * Get the wire type of the current field.
+ *
+ * @return an int that matches one of the ProtoStream WIRE_TYPE_ constants
+ */
+ public int getWireType() {
+ if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) {
+ // mWireType got overwritten when STATE_READING_PACKED was set. Send length delimited
+ // constant instead
+ return WIRE_TYPE_LENGTH_DELIMITED;
+ }
+ return mWireType;
+ }
+
+ /**
+ * Get the current offset in the proto binary.
+ */
+ public int getOffset() {
+ return mOffset + mDiscardedBytes;
+ }
+
+ /**
+ * Reads the tag of the next field from the stream. If previous field value was not read, its
+ * data will be skipped over.
+ *
+ * @return the field number of the next field
+ * @throws IOException if an I/O error occurs
+ */
+ public int nextField() throws IOException {
+
+ if ((mState & STATE_FIELD_MISS) == STATE_FIELD_MISS) {
+ // Data from the last nextField was not used, reuse the info
+ mState &= ~STATE_FIELD_MISS;
+ return mFieldNumber;
+ }
+ if ((mState & STATE_STARTED_FIELD_READ) == STATE_STARTED_FIELD_READ) {
+ // Field data was not read, skip to the next field
+ skip();
+ mState &= ~STATE_STARTED_FIELD_READ;
+ }
+ if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) {
+ if (getOffset() < mPackedEnd) {
+ // In the middle of a packed field, return the same tag until last packed value
+ // has been read
+ mState |= STATE_STARTED_FIELD_READ;
+ return mFieldNumber;
+ } else if (getOffset() == mPackedEnd) {
+ // Reached the end of the packed field
+ mState &= ~STATE_READING_PACKED;
+ } else {
+ throw new ProtoParseException(
+ "Unexpectedly reached end of packed field at offset 0x"
+ + Integer.toHexString(mPackedEnd)
+ + dumpDebugData());
+ }
+ }
+
+ if ((mDepth >= 0) && (getOffset() == getOffsetFromToken(
+ mExpectedObjectTokenStack.get(mDepth)))) {
+ // reached end of a embedded message
+ mFieldNumber = NO_MORE_FIELDS;
+ } else {
+ readTag();
+ }
+ return mFieldNumber;
+ }
+
+ /**
+ * Attempt to guess the next field. If there is a match, the field data will be ready to read.
+ * If there is no match, nextField will need to be called to get the field number
+ *
+ * @return true if fieldId matches the next field, false if not
+ */
+ public boolean isNextField(long fieldId) throws IOException {
+ if (nextField() == (int) fieldId) {
+ return true;
+ }
+ // Note to reuse the info from the nextField call in the next call.
+ mState |= STATE_FIELD_MISS;
+ return false;
+ }
+
+ /**
+ * Read a single double.
+ * Will throw if the current wire type is not fixed64
+ *
+ * @param fieldId - must match the current field number and field type
+ */
+ public double readDouble(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+ checkPacked(fieldId);
+
+ double value;
+ switch ((int) ((fieldId & FIELD_TYPE_MASK)
+ >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_FIXED64);
+ value = Double.longBitsToDouble(readFixed64());
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId)
+ + ") cannot be read as a double"
+ + dumpDebugData());
+ }
+ // Successfully read the field
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return value;
+ }
+
+ /**
+ * Read a single float.
+ * Will throw if the current wire type is not fixed32
+ *
+ * @param fieldId - must match the current field number and field type
+ */
+ public float readFloat(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+ checkPacked(fieldId);
+
+ float value;
+ switch ((int) ((fieldId & FIELD_TYPE_MASK)
+ >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_FIXED32);
+ value = Float.intBitsToFloat(readFixed32());
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId) + ") is not a float"
+ + dumpDebugData());
+ }
+ // Successfully read the field
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return value;
+ }
+
+ /**
+ * Read a single 32bit or varint proto type field as an int.
+ * Will throw if the current wire type is not varint or fixed32
+ *
+ * @param fieldId - must match the current field number and field type
+ */
+ public int readInt(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+ checkPacked(fieldId);
+
+ int value;
+ switch ((int) ((fieldId & FIELD_TYPE_MASK)
+ >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_FIXED32);
+ value = readFixed32();
+ break;
+ case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_VARINT);
+ value = decodeZigZag32((int) readVarint());
+ break;
+ case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_VARINT);
+ value = (int) readVarint();
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId) + ") is not an int"
+ + dumpDebugData());
+ }
+ // Successfully read the field
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return value;
+ }
+
+ /**
+ * Read a single 64bit or varint proto type field as an long.
+ *
+ * @param fieldId - must match the current field number
+ */
+ public long readLong(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+ checkPacked(fieldId);
+
+ long value;
+ switch ((int) ((fieldId & FIELD_TYPE_MASK)
+ >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_FIXED64);
+ value = readFixed64();
+ break;
+ case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_VARINT);
+ value = decodeZigZag64(readVarint());
+ break;
+ case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_VARINT);
+ value = readVarint();
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId) + ") is not an long"
+ + dumpDebugData());
+ }
+ // Successfully read the field
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return value;
+ }
+
+ /**
+ * Read a single 32bit or varint proto type field as an boolean.
+ *
+ * @param fieldId - must match the current field number
+ */
+ public boolean readBoolean(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+ checkPacked(fieldId);
+
+ boolean value;
+ switch ((int) ((fieldId & FIELD_TYPE_MASK)
+ >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_VARINT);
+ value = readVarint() != 0;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId) + ") is not an boolean"
+ + dumpDebugData());
+ }
+ // Successfully read the field
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return value;
+ }
+
+ /**
+ * Read a string field
+ *
+ * @param fieldId - must match the current field number
+ */
+ public String readString(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+
+ String value;
+ switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_STRING >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_LENGTH_DELIMITED);
+ int len = (int) readVarint();
+ value = readRawString(len);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field id(" + getFieldIdString(fieldId)
+ + ") is not an string"
+ + dumpDebugData());
+ }
+ // Successfully read the field
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return value;
+ }
+
+ /**
+ * Read a bytes field
+ *
+ * @param fieldId - must match the current field number
+ */
+ public byte[] readBytes(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+
+ byte[] value;
+ switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_MESSAGE >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_BYTES >>> FIELD_TYPE_SHIFT):
+ assertWireType(WIRE_TYPE_LENGTH_DELIMITED);
+ int len = (int) readVarint();
+ value = readRawBytes(len);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field type (" + getFieldIdString(fieldId)
+ + ") cannot be read as raw bytes"
+ + dumpDebugData());
+ }
+ // Successfully read the field
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return value;
+ }
+
+ /**
+ * Start the read of an embedded Object
+ *
+ * @param fieldId - must match the current field number
+ * @return a token. The token must be handed back when finished reading embedded Object
+ */
+ public long start(long fieldId) throws IOException {
+ assertFreshData();
+ assertFieldNumber(fieldId);
+ assertWireType(WIRE_TYPE_LENGTH_DELIMITED);
+
+ int messageSize = (int) readVarint();
+
+ if (mExpectedObjectTokenStack == null) {
+ mExpectedObjectTokenStack = new ArrayList<>();
+ }
+ if (++mDepth == mExpectedObjectTokenStack.size()) {
+ // Create a token to keep track of nested Object and extend the object stack
+ mExpectedObjectTokenStack.add(makeToken(0,
+ (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth,
+ (int) fieldId, getOffset() + messageSize));
+
+ } else {
+ // Create a token to keep track of nested Object
+ mExpectedObjectTokenStack.set(mDepth, makeToken(0,
+ (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth,
+ (int) fieldId, getOffset() + messageSize));
+ }
+
+ // Sanity check
+ if (mDepth > 0
+ && getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth))
+ > getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth - 1))) {
+ throw new ProtoParseException("Embedded Object ("
+ + token2String(mExpectedObjectTokenStack.get(mDepth))
+ + ") ends after of parent Objects's ("
+ + token2String(mExpectedObjectTokenStack.get(mDepth - 1))
+ + ") end"
+ + dumpDebugData());
+ }
+ mState &= ~STATE_STARTED_FIELD_READ;
+ return mExpectedObjectTokenStack.get(mDepth);
+ }
+
+ /**
+ * Note the end of a nested object. Must be called to continue streaming the rest of the proto.
+ * end can be called mid object parse. The offset will be moved to the next field outside the
+ * object.
+ *
+ * @param token - token
+ */
+ public void end(long token) {
+ // Sanity check to make sure user is keeping track of their embedded messages
+ if (mExpectedObjectTokenStack.get(mDepth) != token) {
+ throw new ProtoParseException(
+ "end token " + token + " does not match current message token "
+ + mExpectedObjectTokenStack.get(mDepth)
+ + dumpDebugData());
+ }
+ if (getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) > getOffset()) {
+ // Did not read all of the message, skip to the end
+ incOffset(getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) - getOffset());
+ }
+ mDepth--;
+ mState &= ~STATE_STARTED_FIELD_READ;
+ }
+
+ /**
+ * Read the tag at the start of the next field and collect field number and wire type.
+ * Will set mFieldNumber to NO_MORE_FIELDS if end of buffer/stream reached.
+ */
+ private void readTag() throws IOException {
+ fillBuffer();
+ if (mOffset >= mEnd) {
+ // reached end of the stream
+ mFieldNumber = NO_MORE_FIELDS;
+ return;
+ }
+ int tag = (int) readVarint();
+ mFieldNumber = tag >>> FIELD_ID_SHIFT;
+ mWireType = tag & WIRE_TYPE_MASK;
+ mState |= STATE_STARTED_FIELD_READ;
+ }
+
+ /**
+ * Decode a 32 bit ZigZag encoded signed int.
+ *
+ * @param n - int to decode
+ * @return the decoded signed int
+ */
+ public int decodeZigZag32(final int n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Decode a 64 bit ZigZag encoded signed long.
+ *
+ * @param n - long to decode
+ * @return the decoded signed long
+ */
+ public long decodeZigZag64(final long n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Read a varint from the buffer
+ *
+ * @return the varint as a long
+ */
+ private long readVarint() throws IOException {
+ long value = 0;
+ int shift = 0;
+ while (true) {
+ fillBuffer();
+ // Limit how much bookkeeping is done by checking how far away the end of the buffer is
+ // and directly accessing buffer up until the end.
+ final int fragment = mEnd - mOffset;
+ for (int i = 0; i < fragment; i++) {
+ byte b = mBuffer[(mOffset + i)];
+ value |= (b & 0x7FL) << shift;
+ if ((b & 0x80) == 0) {
+ incOffset(i + 1);
+ return value;
+ }
+ shift += 7;
+ if (shift > 63) {
+ throw new ProtoParseException(
+ "Varint is too large at offset 0x"
+ + Integer.toHexString(getOffset() + i)
+ + dumpDebugData());
+ }
+ }
+ // Hit the end of the buffer, do some incrementing and checking, then continue
+ incOffset(fragment);
+ }
+ }
+
+ /**
+ * Read a fixed 32 bit int from the buffer
+ *
+ * @return the fixed32 as a int
+ */
+ private int readFixed32() throws IOException {
+ // check for fast path, which is likely with a reasonable buffer size
+ if (mOffset + 4 <= mEnd) {
+ // don't bother filling buffer since we know the end is plenty far away
+ incOffset(4);
+ return (mBuffer[mOffset - 4] & 0xFF)
+ | ((mBuffer[mOffset - 3] & 0xFF) << 8)
+ | ((mBuffer[mOffset - 2] & 0xFF) << 16)
+ | ((mBuffer[mOffset - 1] & 0xFF) << 24);
+ }
+
+ // the Fixed32 crosses the edge of a chunk, read the Fixed32 in multiple fragments.
+ // There will be two fragment reads except when the chunk size is 2 or less.
+ int value = 0;
+ int shift = 0;
+ int bytesLeft = 4;
+ while (bytesLeft > 0) {
+ fillBuffer();
+ // Find the number of bytes available until the end of the chunk or Fixed32
+ int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft;
+ incOffset(fragment);
+ bytesLeft -= fragment;
+ while (fragment > 0) {
+ value |= ((mBuffer[mOffset - fragment] & 0xFF) << shift);
+ fragment--;
+ shift += 8;
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Read a fixed 64 bit long from the buffer
+ *
+ * @return the fixed64 as a long
+ */
+ private long readFixed64() throws IOException {
+ // check for fast path, which is likely with a reasonable buffer size
+ if (mOffset + 8 <= mEnd) {
+ // don't bother filling buffer since we know the end is plenty far away
+ incOffset(8);
+ return (mBuffer[mOffset - 8] & 0xFFL)
+ | ((mBuffer[mOffset - 7] & 0xFFL) << 8)
+ | ((mBuffer[mOffset - 6] & 0xFFL) << 16)
+ | ((mBuffer[mOffset - 5] & 0xFFL) << 24)
+ | ((mBuffer[mOffset - 4] & 0xFFL) << 32)
+ | ((mBuffer[mOffset - 3] & 0xFFL) << 40)
+ | ((mBuffer[mOffset - 2] & 0xFFL) << 48)
+ | ((mBuffer[mOffset - 1] & 0xFFL) << 56);
+ }
+
+ // the Fixed64 crosses the edge of a chunk, read the Fixed64 in multiple fragments.
+ // There will be two fragment reads except when the chunk size is 6 or less.
+ long value = 0;
+ int shift = 0;
+ int bytesLeft = 8;
+ while (bytesLeft > 0) {
+ fillBuffer();
+ // Find the number of bytes available until the end of the chunk or Fixed64
+ int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft;
+ incOffset(fragment);
+ bytesLeft -= fragment;
+ while (fragment > 0) {
+ value |= ((mBuffer[(mOffset - fragment)] & 0xFFL) << shift);
+ fragment--;
+ shift += 8;
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Read raw bytes from the buffer
+ *
+ * @param n - number of bytes to read
+ * @return a byte array with raw bytes
+ */
+ private byte[] readRawBytes(int n) throws IOException {
+ byte[] buffer = new byte[n];
+ int pos = 0;
+ while (mOffset + n - pos > mEnd) {
+ int fragment = mEnd - mOffset;
+ if (fragment > 0) {
+ System.arraycopy(mBuffer, mOffset, buffer, pos, fragment);
+ incOffset(fragment);
+ pos += fragment;
+ }
+ fillBuffer();
+ if (mOffset >= mEnd) {
+ throw new ProtoParseException(
+ "Unexpectedly reached end of the InputStream at offset 0x"
+ + Integer.toHexString(mEnd)
+ + dumpDebugData());
+ }
+ }
+ System.arraycopy(mBuffer, mOffset, buffer, pos, n - pos);
+ incOffset(n - pos);
+ return buffer;
+ }
+
+ /**
+ * Read raw string from the buffer
+ *
+ * @param n - number of bytes to read
+ * @return a string
+ */
+ private String readRawString(int n) throws IOException {
+ fillBuffer();
+ if (mOffset + n <= mEnd) {
+ // fast path read. String is well within the current buffer
+ String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n,
+ StandardCharsets.UTF_8);
+ incOffset(n);
+ return value;
+ } else if (n <= mBufferSize) {
+ // String extends past buffer, but can be encapsulated in a buffer. Copy the first chunk
+ // of the string to the start of the buffer and then fill the rest of the buffer from
+ // the stream.
+ final int stringHead = mEnd - mOffset;
+ System.arraycopy(mBuffer, mOffset, mBuffer, 0, stringHead);
+ mEnd = stringHead + mStream.read(mBuffer, stringHead, n - stringHead);
+
+ mDiscardedBytes += mOffset;
+ mOffset = 0;
+
+ String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n,
+ StandardCharsets.UTF_8);
+ incOffset(n);
+ return value;
+ }
+ // Otherwise, the string is too large to use the buffer. Create the string from a
+ // separate byte array.
+ return StringFactory.newStringFromBytes(readRawBytes(n), 0, n, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Fill the buffer with a chunk from the stream if need be.
+ * Will skip chunks until mOffset is reached
+ */
+ private void fillBuffer() throws IOException {
+ if (mOffset >= mEnd && mStream != null) {
+ mOffset -= mEnd;
+ mDiscardedBytes += mEnd;
+ if (mOffset >= mBufferSize) {
+ int skipped = (int) mStream.skip((mOffset / mBufferSize) * mBufferSize);
+ mDiscardedBytes += skipped;
+ mOffset -= skipped;
+ }
+ mEnd = mStream.read(mBuffer);
+ }
+ }
+
+ /**
+ * Skips the rest of current field and moves to the start of the next field. This should only be
+ * called while state is STATE_STARTED_FIELD_READ
+ */
+ public void skip() throws IOException {
+ if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) {
+ incOffset(mPackedEnd - getOffset());
+ } else {
+ switch (mWireType) {
+ case WIRE_TYPE_VARINT:
+ byte b;
+ do {
+ fillBuffer();
+ b = mBuffer[mOffset];
+ incOffset(1);
+ } while ((b & 0x80) != 0);
+ break;
+ case WIRE_TYPE_FIXED64:
+ incOffset(8);
+ break;
+ case WIRE_TYPE_LENGTH_DELIMITED:
+ fillBuffer();
+ int length = (int) readVarint();
+ incOffset(length);
+ break;
+ /*
+ case WIRE_TYPE_START_GROUP:
+ // Not implemented
+ break;
+ case WIRE_TYPE_END_GROUP:
+ // Not implemented
+ break;
+ */
+ case WIRE_TYPE_FIXED32:
+ incOffset(4);
+ break;
+ default:
+ throw new ProtoParseException(
+ "Unexpected wire type: " + mWireType + " at offset 0x"
+ + Integer.toHexString(mOffset)
+ + dumpDebugData());
+ }
+ }
+ mState &= ~STATE_STARTED_FIELD_READ;
+ }
+
+ /**
+ * Increment the offset and handle all the relevant bookkeeping
+ * Refilling the buffer when its end is reached will be handled elsewhere (ideally just before
+ * a read, to avoid unnecessary reads from stream)
+ *
+ * @param n - number of bytes to increment
+ */
+ private void incOffset(int n) {
+ mOffset += n;
+
+ if (mDepth >= 0 && getOffset() > getOffsetFromToken(
+ mExpectedObjectTokenStack.get(mDepth))) {
+ throw new ProtoParseException("Unexpectedly reached end of embedded object. "
+ + token2String(mExpectedObjectTokenStack.get(mDepth))
+ + dumpDebugData());
+ }
+ }
+
+ /**
+ * Check the current wire type to determine if current numeric field is packed. If it is packed,
+ * set up to deal with the field
+ * This should only be called for primitive numeric field types.
+ *
+ * @param fieldId - used to determine what the packed wire type is.
+ */
+ private void checkPacked(long fieldId) throws IOException {
+ if (mWireType == WIRE_TYPE_LENGTH_DELIMITED) {
+ // Primitive Field is length delimited, must be a packed field.
+ final int length = (int) readVarint();
+ mPackedEnd = getOffset() + length;
+ mState |= STATE_READING_PACKED;
+
+ // Fake the wire type, based on the field type
+ switch ((int) ((fieldId & FIELD_TYPE_MASK)
+ >>> FIELD_TYPE_SHIFT)) {
+ case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT):
+ if (length % 4 != 0) {
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId)
+ + ") packed length " + length
+ + " is not aligned for fixed32"
+ + dumpDebugData());
+ }
+ mWireType = WIRE_TYPE_FIXED32;
+ break;
+ case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT):
+ if (length % 8 != 0) {
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId)
+ + ") packed length " + length
+ + " is not aligned for fixed64"
+ + dumpDebugData());
+ }
+ mWireType = WIRE_TYPE_FIXED64;
+ break;
+ case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT):
+ case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT):
+ mWireType = WIRE_TYPE_VARINT;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Requested field id (" + getFieldIdString(fieldId)
+ + ") is not a packable field"
+ + dumpDebugData());
+ }
+ }
+ }
+
+
+ /**
+ * Check a field id constant against current field number
+ *
+ * @param fieldId - throws if fieldId does not match mFieldNumber
+ */
+ private void assertFieldNumber(long fieldId) {
+ if ((int) fieldId != mFieldNumber) {
+ throw new IllegalArgumentException("Requested field id (" + getFieldIdString(fieldId)
+ + ") does not match current field number (0x" + Integer.toHexString(
+ mFieldNumber)
+ + ") at offset 0x" + Integer.toHexString(getOffset())
+ + dumpDebugData());
+ }
+ }
+
+
+ /**
+ * Check a wire type against current wire type.
+ *
+ * @param wireType - throws if wireType does not match mWireType.
+ */
+ private void assertWireType(int wireType) {
+ if (wireType != mWireType) {
+ throw new WireTypeMismatchException(
+ "Current wire type " + getWireTypeString(mWireType)
+ + " does not match expected wire type " + getWireTypeString(wireType)
+ + " at offset 0x" + Integer.toHexString(getOffset())
+ + dumpDebugData());
+ }
+ }
+
+ /**
+ * Check if there is data ready to be read.
+ */
+ private void assertFreshData() {
+ if ((mState & STATE_STARTED_FIELD_READ) != STATE_STARTED_FIELD_READ) {
+ throw new ProtoParseException(
+ "Attempting to read already read field at offset 0x" + Integer.toHexString(
+ getOffset()) + dumpDebugData());
+ }
+ }
+
+ /**
+ * Dump debugging data about the buffer.
+ */
+ public String dumpDebugData() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("\nmFieldNumber : 0x" + Integer.toHexString(mFieldNumber));
+ sb.append("\nmWireType : 0x" + Integer.toHexString(mWireType));
+ sb.append("\nmState : 0x" + Integer.toHexString(mState));
+ sb.append("\nmDiscardedBytes : 0x" + Integer.toHexString(mDiscardedBytes));
+ sb.append("\nmOffset : 0x" + Integer.toHexString(mOffset));
+ sb.append("\nmExpectedObjectTokenStack : ");
+ if (mExpectedObjectTokenStack == null) {
+ sb.append("null");
+ } else {
+ sb.append(mExpectedObjectTokenStack);
+ }
+ sb.append("\nmDepth : 0x" + Integer.toHexString(mDepth));
+ sb.append("\nmBuffer : ");
+ if (mBuffer == null) {
+ sb.append("null");
+ } else {
+ sb.append(mBuffer);
+ }
+ sb.append("\nmBufferSize : 0x" + Integer.toHexString(mBufferSize));
+ sb.append("\nmEnd : 0x" + Integer.toHexString(mEnd));
+
+ return sb.toString();
+ }
+}
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index a94806a..a1ee61c 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -100,88 +100,12 @@
* errors if they are not matched.
*/
@TestApi
-public final class ProtoOutputStream {
+public final class ProtoOutputStream extends ProtoStream {
+ /**
+ * @hide
+ */
public static final String TAG = "ProtoOutputStream";
- public static final int FIELD_ID_SHIFT = 3;
- public static final int WIRE_TYPE_MASK = (1<<FIELD_ID_SHIFT)-1;
- public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK;
-
- public static final int WIRE_TYPE_VARINT = 0;
- public static final int WIRE_TYPE_FIXED64 = 1;
- public static final int WIRE_TYPE_LENGTH_DELIMITED = 2;
- public static final int WIRE_TYPE_START_GROUP = 3;
- public static final int WIRE_TYPE_END_GROUP = 4;
- public static final int WIRE_TYPE_FIXED32 = 5;
-
- /**
- * Position of the field type in a (long) fieldId.
- */
- public static final int FIELD_TYPE_SHIFT = 32;
-
- /**
- * Mask for the field types stored in a fieldId. Leaves a whole
- * byte for future expansion, even though there are currently only 17 types.
- */
- public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT;
-
- public static final long FIELD_TYPE_UNKNOWN = 0;
-
- /**
- * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly,
- * so no extra mapping needs to be maintained in this case.
- */
- public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT;
-// public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated.
- public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT;
-
- private static final String[] FIELD_TYPE_NAMES = new String[] {
- "Double",
- "Float",
- "Int64",
- "UInt64",
- "Int32",
- "Fixed64",
- "Fixed32",
- "Bool",
- "String",
- "Group", // This field is deprecated but reserved here for indexing.
- "Message",
- "Bytes",
- "UInt32",
- "Enum",
- "SFixed32",
- "SFixed64",
- "SInt32",
- "SInt64",
- };
-
- //
- // FieldId flags for whether the field is single, repeated or packed.
- //
- public static final int FIELD_COUNT_SHIFT = 40;
- public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT;
-
- public static final long FIELD_COUNT_UNKNOWN = 0;
- public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
- public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
- public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT;
-
/**
* Our buffer.
*/
@@ -1997,94 +1921,6 @@
}
}
- //
- // Child objects
- //
-
- /**
- * Make a token.
- * Bits 61-63 - tag size (So we can go backwards later if the object had not data)
- * - 3 bits, max value 7, max value needed 5
- * Bit 60 - true if the object is repeated (lets us require endObject or endRepeatedObject)
- * Bits 59-51 - depth (For error checking)
- * - 9 bits, max value 512, when checking, value is masked (if we really
- * are more than 512 levels deep)
- * Bits 32-50 - objectId (For error checking)
- * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap
- * because of the overflow, and only the tokens are compared.
- * Bits 0-31 - offset of the first size field in the buffer.
- */
- // VisibleForTesting
- public static long makeToken(int tagSize, boolean repeated, int depth, int objectId,
- int sizePos) {
- return ((0x07L & (long)tagSize) << 61)
- | (repeated ? (1L << 60) : 0)
- | (0x01ffL & (long)depth) << 51
- | (0x07ffffL & (long)objectId) << 32
- | (0x0ffffffffL & (long)sizePos);
- }
-
- /**
- * Get the encoded tag size from the token.
- */
- public static int getTagSizeFromToken(long token) {
- return (int)(0x7 & (token >> 61));
- }
-
- /**
- * Get whether this is a call to startObject (false) or startRepeatedObject (true).
- */
- public static boolean getRepeatedFromToken(long token) {
- return (0x1 & (token >> 60)) != 0;
- }
-
- /**
- * Get the nesting depth of startObject calls from the token.
- */
- public static int getDepthFromToken(long token) {
- return (int)(0x01ff & (token >> 51));
- }
-
- /**
- * Get the object ID from the token. The object ID is a serial number for the
- * startObject calls that have happened on this object. The values are truncated
- * to 9 bits, but that is sufficient for error checking.
- */
- public static int getObjectIdFromToken(long token) {
- return (int)(0x07ffff & (token >> 32));
- }
-
- /**
- * Get the location of the childRawSize (the first 32 bit size field) in this object.
- */
- public static int getSizePosFromToken(long token) {
- return (int)token;
- }
-
- /**
- * Convert the object ID to the ordinal value -- the n-th call to startObject.
- * The object IDs start at -1 and count backwards, so that the value is unlikely
- * to alias with an actual size field that had been written.
- */
- public static int convertObjectIdToOrdinal(int objectId) {
- return (-1 & 0x07ffff) - objectId;
- }
-
- /**
- * Return a debugging string of a token.
- */
- public static String token2String(long token) {
- if (token == 0L) {
- return "Token(0)";
- } else {
- return "Token(val=0x" + Long.toHexString(token)
- + " depth=" + getDepthFromToken(token)
- + " object=" + convertObjectIdToOrdinal(getObjectIdFromToken(token))
- + " tagSize=" + getTagSizeFromToken(token)
- + " sizePos=" + getSizePosFromToken(token)
- + ')';
- }
- }
/**
* Start a child object.
@@ -2175,7 +2011,7 @@
// at which to write the size.
final int depth = getDepthFromToken(token);
final boolean expectedRepeated = getRepeatedFromToken(token);
- final int sizePos = getSizePosFromToken(token);
+ final int sizePos = getOffsetFromToken(token);
final int childRawSize = mBuffer.getWritePos() - sizePos - 8;
if (repeated != expectedRepeated) {
@@ -2346,56 +2182,6 @@
}
/**
- * Get the developer-usable name of a field type.
- */
- private static String getFieldTypeString(long fieldType) {
- int index = ((int)((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1;
- if (index >= 0 && index < FIELD_TYPE_NAMES.length) {
- return FIELD_TYPE_NAMES[index];
- } else {
- return null;
- }
- }
-
- /**
- * Get the developer-usable name of a field count.
- */
- private static String getFieldCountString(long fieldCount) {
- if (fieldCount == FIELD_COUNT_SINGLE) {
- return "";
- } else if (fieldCount == FIELD_COUNT_REPEATED) {
- return "Repeated";
- } else if (fieldCount == FIELD_COUNT_PACKED) {
- return "Packed";
- } else {
- return null;
- }
- }
-
- /**
- * Get a debug string for a fieldId.
- */
- private String getFieldIdString(long fieldId) {
- final long fieldCount = fieldId & FIELD_COUNT_MASK;
- String countString = getFieldCountString(fieldCount);
- if (countString == null) {
- countString = "fieldCount=" + fieldCount;
- }
- if (countString.length() > 0) {
- countString += " ";
- }
-
- final long fieldType = fieldId & FIELD_TYPE_MASK;
- String typeString = getFieldTypeString(fieldType);
- if (typeString == null) {
- typeString = "fieldType=" + fieldType;
- }
-
- return countString + typeString + " tag=" + ((int) fieldId)
- + " fieldId=0x" + Long.toHexString(fieldId);
- }
-
- /**
* Return how many bytes an encoded field tag will require.
*/
private static int getTagSize(int id) {
diff --git a/core/java/android/util/proto/ProtoStream.java b/core/java/android/util/proto/ProtoStream.java
new file mode 100644
index 0000000..9e2e95a
--- /dev/null
+++ b/core/java/android/util/proto/ProtoStream.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.proto;
+
+import android.annotation.TestApi;
+
+/**
+ * Abstract base class for both protobuf streams.
+ *
+ * Contains a set of useful constants and methods used by both
+ * ProtoOutputStream and ProtoInputStream
+ *
+ * @hide
+ */
+@TestApi
+public abstract class ProtoStream {
+
+ public static final int FIELD_ID_SHIFT = 3;
+ public static final int WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1;
+ public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK;
+
+ public static final int WIRE_TYPE_VARINT = 0;
+ public static final int WIRE_TYPE_FIXED64 = 1;
+ public static final int WIRE_TYPE_LENGTH_DELIMITED = 2;
+ public static final int WIRE_TYPE_START_GROUP = 3;
+ public static final int WIRE_TYPE_END_GROUP = 4;
+ public static final int WIRE_TYPE_FIXED32 = 5;
+
+ /**
+ * Position of the field type in a (long) fieldId.
+ */
+ public static final int FIELD_TYPE_SHIFT = 32;
+
+ /**
+ * Mask for the field types stored in a fieldId. Leaves a whole
+ * byte for future expansion, even though there are currently only 17 types.
+ */
+ public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT;
+
+ public static final long FIELD_TYPE_UNKNOWN = 0;
+
+ /**
+ * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly,
+ * so no extra mapping needs to be maintained in this case.
+ */
+ public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT;
+ // public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated.
+ public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT;
+
+ protected static final String[] FIELD_TYPE_NAMES = new String[]{
+ "Double",
+ "Float",
+ "Int64",
+ "UInt64",
+ "Int32",
+ "Fixed64",
+ "Fixed32",
+ "Bool",
+ "String",
+ "Group", // This field is deprecated but reserved here for indexing.
+ "Message",
+ "Bytes",
+ "UInt32",
+ "Enum",
+ "SFixed32",
+ "SFixed64",
+ "SInt32",
+ "SInt64",
+ };
+
+ //
+ // FieldId flags for whether the field is single, repeated or packed.
+ //
+ public static final int FIELD_COUNT_SHIFT = 40;
+ public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT;
+
+ public static final long FIELD_COUNT_UNKNOWN = 0;
+ public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
+ public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
+ public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT;
+
+
+ /**
+ * Get the developer-usable name of a field type.
+ */
+ public static String getFieldTypeString(long fieldType) {
+ int index = ((int) ((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1;
+ if (index >= 0 && index < FIELD_TYPE_NAMES.length) {
+ return FIELD_TYPE_NAMES[index];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the developer-usable name of a field count.
+ */
+ public static String getFieldCountString(long fieldCount) {
+ if (fieldCount == FIELD_COUNT_SINGLE) {
+ return "";
+ } else if (fieldCount == FIELD_COUNT_REPEATED) {
+ return "Repeated";
+ } else if (fieldCount == FIELD_COUNT_PACKED) {
+ return "Packed";
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the developer-usable name of a wire type.
+ */
+ public static String getWireTypeString(int wireType) {
+ switch (wireType) {
+ case WIRE_TYPE_VARINT:
+ return "Varint";
+ case WIRE_TYPE_FIXED64:
+ return "Fixed64";
+ case WIRE_TYPE_LENGTH_DELIMITED:
+ return "Length Delimited";
+ case WIRE_TYPE_START_GROUP:
+ return "Start Group";
+ case WIRE_TYPE_END_GROUP:
+ return "End Group";
+ case WIRE_TYPE_FIXED32:
+ return "Fixed32";
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Get a debug string for a fieldId.
+ */
+ public static String getFieldIdString(long fieldId) {
+ final long fieldCount = fieldId & FIELD_COUNT_MASK;
+ String countString = getFieldCountString(fieldCount);
+ if (countString == null) {
+ countString = "fieldCount=" + fieldCount;
+ }
+ if (countString.length() > 0) {
+ countString += " ";
+ }
+
+ final long fieldType = fieldId & FIELD_TYPE_MASK;
+ String typeString = getFieldTypeString(fieldType);
+ if (typeString == null) {
+ typeString = "fieldType=" + fieldType;
+ }
+
+ return countString + typeString + " tag=" + ((int) fieldId)
+ + " fieldId=0x" + Long.toHexString(fieldId);
+ }
+
+ /**
+ * Combine a fieldId (the field keys in the proto file) and the field flags.
+ * Mostly useful for testing because the generated code contains the fieldId
+ * constants.
+ */
+ public static long makeFieldId(int id, long fieldFlags) {
+ return fieldFlags | (((long) id) & 0x0ffffffffL);
+ }
+
+ //
+ // Child objects
+ //
+
+ /**
+ * Make a token.
+ * Bits 61-63 - tag size (So we can go backwards later if the object had not data)
+ * - 3 bits, max value 7, max value needed 5
+ * Bit 60 - true if the object is repeated (lets us require endObject or endRepeatedObject)
+ * Bits 59-51 - depth (For error checking)
+ * - 9 bits, max value 512, when checking, value is masked (if we really
+ * are more than 512 levels deep)
+ * Bits 32-50 - objectId (For error checking)
+ * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap
+ * because of the overflow, and only the tokens are compared.
+ * Bits 0-31 - offset of interest for the object.
+ */
+ public static long makeToken(int tagSize, boolean repeated, int depth, int objectId,
+ int offset) {
+ return ((0x07L & (long) tagSize) << 61)
+ | (repeated ? (1L << 60) : 0)
+ | (0x01ffL & (long) depth) << 51
+ | (0x07ffffL & (long) objectId) << 32
+ | (0x0ffffffffL & (long) offset);
+ }
+
+ /**
+ * Get the encoded tag size from the token.
+ */
+ public static int getTagSizeFromToken(long token) {
+ return (int) (0x7 & (token >> 61));
+ }
+
+ /**
+ * Get whether this is a call to startObject (false) or startRepeatedObject (true).
+ */
+ public static boolean getRepeatedFromToken(long token) {
+ return (0x1 & (token >> 60)) != 0;
+ }
+
+ /**
+ * Get the nesting depth of startObject calls from the token.
+ */
+ public static int getDepthFromToken(long token) {
+ return (int) (0x01ff & (token >> 51));
+ }
+
+ /**
+ * Get the object ID from the token. The object ID is a serial number for the
+ * startObject calls that have happened on this object. The values are truncated
+ * to 9 bits, but that is sufficient for error checking.
+ */
+ public static int getObjectIdFromToken(long token) {
+ return (int) (0x07ffff & (token >> 32));
+ }
+
+ /**
+ * Get the location of the offset recorded in the token.
+ */
+ public static int getOffsetFromToken(long token) {
+ return (int) token;
+ }
+
+ /**
+ * Convert the object ID to the ordinal value -- the n-th call to startObject.
+ * The object IDs start at -1 and count backwards, so that the value is unlikely
+ * to alias with an actual size field that had been written.
+ */
+ public static int convertObjectIdToOrdinal(int objectId) {
+ return (-1 & 0x07ffff) - objectId;
+ }
+
+ /**
+ * Return a debugging string of a token.
+ */
+ public static String token2String(long token) {
+ if (token == 0L) {
+ return "Token(0)";
+ } else {
+ return "Token(val=0x" + Long.toHexString(token)
+ + " depth=" + getDepthFromToken(token)
+ + " object=" + convertObjectIdToOrdinal(getObjectIdFromToken(token))
+ + " tagSize=" + getTagSizeFromToken(token)
+ + " offset=" + getOffsetFromToken(token)
+ + ')';
+ }
+ }
+}
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index c7bbb9f..03bf590 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -19,6 +19,8 @@
import android.util.AggStats;
import android.util.Duration;
+import java.io.IOException;
+
/**
* This class contains a list of helper functions to write common proto in
* //frameworks/base/core/proto/android/base directory
@@ -70,4 +72,54 @@
}
}
}
+
+ /**
+ * Provide debug data about the current field as a string
+ */
+ public static String currentFieldToString(ProtoInputStream proto) throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ final int fieldNumber = proto.getFieldNumber();
+ final int wireType = proto.getWireType();
+ long fieldConstant;
+
+ sb.append("Offset : 0x" + Integer.toHexString(proto.getOffset()));
+ sb.append("\nField Number : 0x" + Integer.toHexString(proto.getFieldNumber()));
+ sb.append("\nWire Type : ");
+ switch (wireType) {
+ case ProtoStream.WIRE_TYPE_VARINT:
+ sb.append("varint");
+ fieldConstant = ProtoStream.makeFieldId(fieldNumber,
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64);
+ sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant)));
+ break;
+ case ProtoStream.WIRE_TYPE_FIXED64:
+ sb.append("fixed64");
+ fieldConstant = ProtoStream.makeFieldId(fieldNumber,
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64);
+ sb.append("\nField Value : 0x" + Long.toHexString(proto.readLong(fieldConstant)));
+ break;
+ case ProtoStream.WIRE_TYPE_LENGTH_DELIMITED:
+ sb.append("length delimited");
+ fieldConstant = ProtoStream.makeFieldId(fieldNumber,
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES);
+ sb.append("\nField Bytes : " + proto.readBytes(fieldConstant));
+ break;
+ case ProtoStream.WIRE_TYPE_START_GROUP:
+ sb.append("start group");
+ break;
+ case ProtoStream.WIRE_TYPE_END_GROUP:
+ sb.append("end group");
+ break;
+ case ProtoStream.WIRE_TYPE_FIXED32:
+ sb.append("fixed32");
+ fieldConstant = ProtoStream.makeFieldId(fieldNumber,
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32);
+ sb.append("\nField Value : 0x" + Integer.toHexString(proto.readInt(fieldConstant)));
+ break;
+ default:
+ sb.append("unknown(" + proto.getWireType() + ")");
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/util/proto/WireTypeMismatchException.java b/core/java/android/util/proto/WireTypeMismatchException.java
new file mode 100644
index 0000000..d90b4f8
--- /dev/null
+++ b/core/java/android/util/proto/WireTypeMismatchException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.proto;
+
+import android.annotation.TestApi;
+
+/**
+ * Thrown when there is an error parsing protobuf data.
+ *
+ * @hide
+ */
+@TestApi
+public class WireTypeMismatchException extends ProtoParseException {
+
+ /**
+ * Construct a WireTypeMismatchException.
+ *
+ * @param msg The message.
+ */
+ public WireTypeMismatchException(String msg) {
+ super(msg);
+ }
+}
+
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 45fa561..eb41e07 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -32,6 +32,7 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.text.style.AccessibilityClickableSpan;
import android.text.style.ClickableSpan;
import android.util.LongSparseArray;
@@ -702,6 +703,14 @@
// Handle this hidden action separately
succeeded = handleClickableSpanActionUiThread(
target, virtualDescendantId, arguments);
+ } else if (action == R.id.accessibilityActionOutsideTouch) {
+ // trigger ACTION_OUTSIDE to notify windows
+ final long now = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE,
+ 0, 0, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mViewRootImpl.dispatchInputEvent(event);
+ succeeded = true;
} else {
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
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/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/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index c520a99..9aab419 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.LayoutRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
@@ -154,7 +155,9 @@
* @return View Newly created view. Return null for the default
* behavior.
*/
- public View onCreateView(String name, Context context, AttributeSet attrs);
+ @Nullable
+ View onCreateView(@NonNull String name, @NonNull Context context,
+ @NonNull AttributeSet attrs);
}
public interface Factory2 extends Factory {
@@ -172,7 +175,9 @@
* @return View Newly created view. Return null for the default
* behavior.
*/
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
+ @Nullable
+ View onCreateView(@Nullable View parent, @NonNull String name,
+ @NonNull Context context, @NonNull AttributeSet attrs);
}
private static class FactoryMerger implements Factory2 {
@@ -186,13 +191,17 @@
mF22 = f22;
}
- public View onCreateView(String name, Context context, AttributeSet attrs) {
+ @Nullable
+ public View onCreateView(@NonNull String name, @NonNull Context context,
+ @NonNull AttributeSet attrs) {
View v = mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF2.onCreateView(name, context, attrs);
}
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ @Nullable
+ public View onCreateView(@Nullable View parent, @NonNull String name,
+ @NonNull Context context, @NonNull AttributeSet attrs) {
View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
: mF1.onCreateView(name, context, attrs);
if (v != null) return v;
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/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/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 7bae28a..82e9a5c 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -49,6 +49,7 @@
public CharSequence title;
public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
public boolean inPictureInPicture;
+ public boolean hasFlagWatchOutsideTouch;
private WindowInfo() {
/* do nothing - hide constructor */
@@ -74,6 +75,7 @@
window.title = other.title;
window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor;
window.inPictureInPicture = other.inPictureInPicture;
+ window.hasFlagWatchOutsideTouch = other.hasFlagWatchOutsideTouch;
if (other.childTokens != null && !other.childTokens.isEmpty()) {
if (window.childTokens == null) {
@@ -108,6 +110,7 @@
parcel.writeCharSequence(title);
parcel.writeLong(accessibilityIdOfAnchor);
parcel.writeInt(inPictureInPicture ? 1 : 0);
+ parcel.writeInt(hasFlagWatchOutsideTouch ? 1 : 0);
if (childTokens != null && !childTokens.isEmpty()) {
parcel.writeInt(1);
@@ -130,6 +133,8 @@
builder.append(", focused=").append(focused);
builder.append(", children=").append(childTokens);
builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor);
+ builder.append(", pictureInPicture=").append(inPictureInPicture);
+ builder.append(", watchOutsideTouch=").append(hasFlagWatchOutsideTouch);
builder.append(']');
return builder.toString();
}
@@ -145,6 +150,7 @@
title = parcel.readCharSequence();
accessibilityIdOfAnchor = parcel.readLong();
inPictureInPicture = (parcel.readInt() == 1);
+ hasFlagWatchOutsideTouch = (parcel.readInt() == 1);
final boolean hasChildren = (parcel.readInt() == 1);
if (hasChildren) {
@@ -167,6 +173,7 @@
childTokens.clear();
}
inPictureInPicture = false;
+ hasFlagWatchOutsideTouch = false;
}
public static final Parcelable.Creator<WindowInfo> CREATOR =
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..e20acf1 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;
@@ -213,7 +214,7 @@
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static IWindowSession peekWindowSession() {
synchronized (WindowManagerGlobal.class) {
return sWindowSession;
@@ -467,7 +468,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/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index c59c491..e88682e 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -139,6 +139,8 @@
int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ int mMinimumUiTimeout;
+
boolean mIsTouchExplorationEnabled;
@UnsupportedAppUsage
@@ -320,6 +322,11 @@
public void setRelevantEventTypes(int eventTypes) {
mRelevantEventTypes = eventTypes;
}
+
+ @Override
+ public void setMinimumUiTimeout(int uiTimeout) {
+ mMinimumUiTimeout = uiTimeout;
+ }
};
/**
@@ -827,6 +834,19 @@
}
/**
+ * Get the minimum timeout for changes to the UI needed by this user. Controls should remain
+ * on the screen for at least this long to give users time to react. Some users may need
+ * extra time to review the controls, or to reach them, or to activate assistive technology
+ * to activate the controls automatically.
+ *
+ * @return The minimum ui timeout for the current user in milliseconds.
+ * {@link Integer#MAX_VALUE} if timeout is infinite.
+ */
+ public int getMinimumUiTimeoutMillis() {
+ return mMinimumUiTimeout;
+ }
+
+ /**
* Get the preparers that are registered for an accessibility ID
*
* @param id The ID of interest
@@ -1139,6 +1159,7 @@
final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);
setStateLocked(IntPair.first(userStateAndRelevantEvents));
mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
+ mMinimumUiTimeout = service.getMinimumUiTimeout();
mService = service;
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
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/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index c93e2c1..3e2ef18 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -72,4 +72,6 @@
// System process only
boolean sendFingerprintGesture(int gestureKeyCode);
+
+ int getMinimumUiTimeout();
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
index 9cc0315..d2ddca3 100644
--- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -29,4 +29,6 @@
void notifyServicesStateChanged();
void setRelevantEventTypes(int eventTypes);
+
+ void setMinimumUiTimeout(int uiTimeout);
}
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/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index f45507c..e1600c4 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -89,14 +89,17 @@
*
* @param token special token for the system to identify
* {@link InputMethodService}
+ * @param displayId The id of the display that current IME shown.
+ * Used for {{@link #updateInputMethodDisplay(int)}}
* @param privilegedOperations IPC endpoint to do some privileged
* operations that are allowed only to the
* current IME.
* @hide
*/
@MainThread
- default void initializeInternal(IBinder token,
+ default void initializeInternal(IBinder token, int displayId,
IInputMethodPrivilegedOperations privilegedOperations) {
+ updateInputMethodDisplay(displayId);
attachToken(token);
}
@@ -115,6 +118,15 @@
public void attachToken(IBinder token);
/**
+ * Update context display according to given displayId.
+ *
+ * @param displayId The id of the display that need to update for context.
+ * @hide
+ */
+ @MainThread
+ public void updateInputMethodDisplay(int displayId);
+
+ /**
* Bind a new application environment in to the input method, so that it
* can later start and stop input processing.
* Typically this method is called when this input method is enabled in an
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index c51c5e2..2f677f9 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -57,7 +57,6 @@
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
-import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
@@ -336,7 +335,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 +386,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;
@@ -665,10 +661,12 @@
}
/**
- * 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;
@@ -739,7 +737,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 +747,7 @@
*/
@Deprecated
public void hideStatusIcon(IBinder imeToken) {
- mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
+ InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
}
/** @hide */
@@ -1790,7 +1788,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 +1797,7 @@
}
return;
}
- mPrivOpsRegistry.get(token).setInputMethod(id);
+ InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
}
/**
@@ -1819,7 +1817,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 +1826,7 @@
}
return;
}
- mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
+ InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
}
/**
@@ -1848,7 +1846,7 @@
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
- mPrivOpsRegistry.get(token).hideMySoftInput(flags);
+ InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
}
/**
@@ -1869,7 +1867,7 @@
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
- mPrivOpsRegistry.get(token).showMySoftInput(flags);
+ InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
}
/**
@@ -2229,7 +2227,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 +2235,7 @@
throw e.rethrowFromSystemServer();
}
}
- return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
}
/**
@@ -2257,7 +2255,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 +2263,8 @@
throw e.rethrowFromSystemServer();
}
}
- return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+ .switchToNextInputMethod(onlyCurrentIme);
}
/**
@@ -2284,7 +2283,8 @@
*/
@Deprecated
public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
- return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
+ return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+ .shouldOfferSwitchingToNextInputMethod();
}
/**
@@ -2420,34 +2420,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 61a6972..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.LocaleUtils;
+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 = LocaleUtils.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 41b70b6..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.LocaleUtils;
+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 LocaleUtils.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/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index e9a9e8f..46b1f6e 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -51,9 +51,6 @@
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
- public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
- "persist.sys.webview.vmsize";
-
private static final String LOGTAG = "WebViewFactory";
private static final boolean DEBUG = false;
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index cabba06..5a6aeba 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -17,15 +17,14 @@
package android.webkit;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
-import android.content.pm.ApplicationInfo;
+import android.app.ActivityThread;
+import android.app.LoadedApk;
+import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -33,11 +32,7 @@
import dalvik.system.VMRuntime;
-import java.io.File;
-import java.io.IOException;
import java.util.Arrays;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
/**
* @hide
@@ -50,7 +45,6 @@
"/data/misc/shared_relro/libwebviewchromium32.relro";
private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
"/data/misc/shared_relro/libwebviewchromium64.relro";
- private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024;
private static final boolean DEBUG = false;
@@ -69,18 +63,26 @@
boolean result = false;
boolean is64Bit = VMRuntime.getRuntime().is64Bit();
try {
- if (args.length != 1 || args[0] == null) {
+ if (args.length != 2 || args[0] == null || args[1] == null) {
Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
return;
}
- Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), lib: " + args[0]);
+ String packageName = args[0];
+ String libraryFileName = args[1];
+ Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), package: "
+ + packageName + " library: " + libraryFileName);
if (!sAddressSpaceReserved) {
Log.e(LOGTAG, "can't create relro file; address space not reserved");
return;
}
- result = nativeCreateRelroFile(args[0] /* path */,
+ LoadedApk apk = ActivityThread.currentActivityThread().getPackageInfo(
+ packageName,
+ null,
+ Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+ result = nativeCreateRelroFile(libraryFileName,
is64Bit ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
- CHROMIUM_WEBVIEW_NATIVE_RELRO_32);
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+ apk.getClassLoader());
if (result && DEBUG) Log.v(LOGTAG, "created relro file");
} finally {
// We must do our best to always notify the update service, even if something fails.
@@ -101,7 +103,8 @@
/**
* Create a single relro file by invoking an isolated process that to do the actual work.
*/
- static void createRelroFile(final boolean is64Bit, @NonNull WebViewNativeLibrary nativeLib) {
+ static void createRelroFile(final boolean is64Bit, @NonNull String packageName,
+ @NonNull String libraryFileName) {
final String abi =
is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
@@ -119,13 +122,10 @@
};
try {
- if (nativeLib == null || nativeLib.path == null) {
- throw new IllegalArgumentException(
- "Native library paths to the WebView RelRo process must not be null!");
- }
boolean success = LocalServices.getService(ActivityManagerInternal.class)
.startIsolatedProcess(
- RelroFileCreator.class.getName(), new String[] { nativeLib.path },
+ RelroFileCreator.class.getName(),
+ new String[] { packageName, libraryFileName },
"WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
if (!success) throw new Exception("Failed to start the relro file creator process");
} catch (Throwable t) {
@@ -140,83 +140,50 @@
* be called whenever we change WebView provider.
* @return the number of relro processes started.
*/
- static int prepareNativeLibraries(PackageInfo webviewPackageInfo)
- throws WebViewFactory.MissingWebViewPackageException {
- WebViewNativeLibrary nativeLib32bit =
- getWebViewNativeLibrary(webviewPackageInfo, false /* is64bit */);
- WebViewNativeLibrary nativeLib64bit =
- getWebViewNativeLibrary(webviewPackageInfo, true /* is64bit */);
- updateWebViewZygoteVmSize(nativeLib32bit, nativeLib64bit);
-
- return createRelros(nativeLib32bit, nativeLib64bit);
+ static int prepareNativeLibraries(@NonNull PackageInfo webViewPackageInfo) {
+ // TODO(torne): new way of calculating VM size
+ // updateWebViewZygoteVmSize(nativeLib32bit, nativeLib64bit);
+ String libraryFileName = WebViewFactory.getWebViewLibrary(
+ webViewPackageInfo.applicationInfo);
+ if (libraryFileName == null) {
+ // Can't do anything with no filename, don't spawn any processes.
+ return 0;
+ }
+ return createRelros(webViewPackageInfo.packageName, libraryFileName);
}
/**
* @return the number of relro processes started.
*/
- private static int createRelros(@Nullable WebViewNativeLibrary nativeLib32bit,
- @Nullable WebViewNativeLibrary nativeLib64bit) {
+ private static int createRelros(@NonNull String packageName, @NonNull String libraryFileName) {
if (DEBUG) Log.v(LOGTAG, "creating relro files");
int numRelros = 0;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- if (nativeLib32bit == null) {
- Log.e(LOGTAG, "No 32-bit WebView library path, skipping relro creation.");
- } else {
- if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
- createRelroFile(false /* is64Bit */, nativeLib32bit);
- numRelros++;
- }
+ if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
+ createRelroFile(false /* is64Bit */, packageName, libraryFileName);
+ numRelros++;
}
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- if (nativeLib64bit == null) {
- Log.e(LOGTAG, "No 64-bit WebView library path, skipping relro creation.");
- } else {
- if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
- createRelroFile(true /* is64Bit */, nativeLib64bit);
- numRelros++;
- }
+ if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
+ createRelroFile(true /* is64Bit */, packageName, libraryFileName);
+ numRelros++;
}
return numRelros;
}
/**
- *
- * @return the native WebView libraries in the new WebView APK.
- */
- private static void updateWebViewZygoteVmSize(
- @Nullable WebViewNativeLibrary nativeLib32bit,
- @Nullable WebViewNativeLibrary nativeLib64bit)
- throws WebViewFactory.MissingWebViewPackageException {
- // Find the native libraries of the new WebView package, to change the size of the
- // memory region in the Zygote reserved for the library.
- long newVmSize = 0L;
-
- if (nativeLib32bit != null) newVmSize = Math.max(newVmSize, nativeLib32bit.size);
- if (nativeLib64bit != null) newVmSize = Math.max(newVmSize, nativeLib64bit.size);
-
- if (DEBUG) {
- Log.v(LOGTAG, "Based on library size, need " + newVmSize
- + " bytes of address space.");
- }
- // The required memory can be larger than the file on disk (due to .bss), and an
- // upgraded version of the library will likely be larger, so always attempt to
- // reserve twice as much as we think to allow for the library to grow during this
- // boot cycle.
- newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
- Log.d(LOGTAG, "Setting new address space to " + newVmSize);
- setWebViewZygoteVmSize(newVmSize);
- }
-
- /**
* Reserve space for the native library to be loaded into.
*/
static void reserveAddressSpaceInZygote() {
System.loadLibrary("webviewchromium_loader");
- long addressSpaceToReserve =
- SystemProperties.getLong(WebViewFactory.CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
- CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
+ boolean is64Bit = VMRuntime.getRuntime().is64Bit();
+ // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
+ // On 32-bit it's fairly scarce and we should keep it to a realistic number that
+ // permits some future growth but doesn't hog space: we use 100MB which is more than 2x
+ // the current requirement.
+ long addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 100 * 1024 * 1024;
sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
if (sAddressSpaceReserved) {
@@ -253,106 +220,7 @@
return result;
}
- /**
- * Fetch WebView's native library paths from {@param packageInfo}.
- * @hide
- */
- @Nullable
- @VisibleForTesting
- public static WebViewNativeLibrary getWebViewNativeLibrary(PackageInfo packageInfo,
- boolean is64bit) throws WebViewFactory.MissingWebViewPackageException {
- ApplicationInfo ai = packageInfo.applicationInfo;
- final String nativeLibFileName = WebViewFactory.getWebViewLibrary(ai);
-
- String dir = getWebViewNativeLibraryDirectory(ai, is64bit /* 64bit */);
-
- WebViewNativeLibrary lib = findNativeLibrary(ai, nativeLibFileName,
- is64bit ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS, dir);
-
- if (DEBUG) {
- Log.v(LOGTAG, String.format("Native %d-bit lib: %s", is64bit ? 64 : 32, lib.path));
- }
- return lib;
- }
-
- /**
- * @return the directory of the native WebView library with bitness {@param is64bit}.
- * @hide
- */
- @VisibleForTesting
- public static String getWebViewNativeLibraryDirectory(ApplicationInfo ai, boolean is64bit) {
- // Primary arch has the same bitness as the library we are looking for.
- if (is64bit == VMRuntime.is64BitAbi(ai.primaryCpuAbi)) return ai.nativeLibraryDir;
-
- // Secondary arch has the same bitness as the library we are looking for.
- if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
- return ai.secondaryNativeLibraryDir;
- }
-
- return "";
- }
-
- /**
- * @return an object describing a native WebView library given the directory path of that
- * library, or null if the library couldn't be found.
- */
- @Nullable
- private static WebViewNativeLibrary findNativeLibrary(ApplicationInfo ai,
- String nativeLibFileName, String[] abiList, String libDirectory)
- throws WebViewFactory.MissingWebViewPackageException {
- if (TextUtils.isEmpty(libDirectory)) return null;
- String libPath = libDirectory + "/" + nativeLibFileName;
- File f = new File(libPath);
- if (f.exists()) {
- return new WebViewNativeLibrary(libPath, f.length());
- } else {
- return getLoadFromApkPath(ai.sourceDir, abiList, nativeLibFileName);
- }
- }
-
- /**
- * @hide
- */
- @VisibleForTesting
- public static class WebViewNativeLibrary {
- public final String path;
- public final long size;
-
- WebViewNativeLibrary(String path, long size) {
- this.path = path;
- this.size = size;
- }
- }
-
- private static WebViewNativeLibrary getLoadFromApkPath(String apkPath,
- String[] abiList,
- String nativeLibFileName)
- throws WebViewFactory.MissingWebViewPackageException {
- // Search the APK for a native library conforming to a listed ABI.
- try (ZipFile z = new ZipFile(apkPath)) {
- for (String abi : abiList) {
- final String entry = "lib/" + abi + "/" + nativeLibFileName;
- ZipEntry e = z.getEntry(entry);
- if (e != null && e.getMethod() == ZipEntry.STORED) {
- // Return a path formatted for dlopen() load from APK.
- return new WebViewNativeLibrary(apkPath + "!/" + entry, e.getSize());
- }
- }
- } catch (IOException e) {
- throw new WebViewFactory.MissingWebViewPackageException(e);
- }
- return null;
- }
-
- /**
- * Sets the size of the memory area in which to store the relro section.
- */
- private static void setWebViewZygoteVmSize(long vmSize) {
- SystemProperties.set(WebViewFactory.CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
- Long.toString(vmSize));
- }
-
static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
- static native boolean nativeCreateRelroFile(String lib, String relro);
+ static native boolean nativeCreateRelroFile(String lib, String relro, ClassLoader clazzLoader);
static native int nativeLoadWithRelroFile(String lib, String relro, ClassLoader clazzLoader);
}
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/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java
index 7b25a06..3fd88db 100644
--- a/core/java/com/android/internal/colorextraction/types/Tonal.java
+++ b/core/java/com/android/internal/colorextraction/types/Tonal.java
@@ -51,10 +51,8 @@
private static final boolean DEBUG = true;
- public static final int THRESHOLD_COLOR_LIGHT = 0xffe0e0e0;
public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0;
- public static final int THRESHOLD_COLOR_DARK = 0xff212121;
- public static final int MAIN_COLOR_DARK = 0xff000000;
+ public static final int MAIN_COLOR_DARK = 0xff212121;
private final TonalPalette mGreyPalette;
private final ArrayList<TonalPalette> mTonalPalettes;
@@ -197,12 +195,12 @@
// light fallback or darker than our dark fallback.
ColorUtils.colorToHSL(mainColor, mTmpHSL);
final float mainLuminosity = mTmpHSL[2];
- ColorUtils.colorToHSL(THRESHOLD_COLOR_LIGHT, mTmpHSL);
+ ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL);
final float lightLuminosity = mTmpHSL[2];
if (mainLuminosity > lightLuminosity) {
return false;
}
- ColorUtils.colorToHSL(THRESHOLD_COLOR_DARK, mTmpHSL);
+ ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL);
final float darkLuminosity = mTmpHSL[2];
if (mainLuminosity < darkLuminosity) {
return false;
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/LocaleUtils.java b/core/java/com/android/internal/inputmethod/LocaleUtils.java
deleted file mode 100644
index f7360eb..0000000
--- a/core/java/com/android/internal/inputmethod/LocaleUtils.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.icu.util.ULocale;
-import android.os.LocaleList;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-
-public final class LocaleUtils {
-
- @VisibleForTesting
- public interface LocaleExtractor<T> {
- @Nullable
- Locale get(@Nullable T source);
- }
-
- /**
- * Calculates a matching score for the single desired locale.
- *
- * @see LocaleUtils#filterByLanguage(List, LocaleExtractor, LocaleList, ArrayList)
- *
- * @param supported The locale supported by IME subtype.
- * @param desired The locale preferred by user.
- * @return A score based on the locale matching for the default subtype enabling.
- */
- @IntRange(from=1, to=3)
- private static byte calculateMatchingSubScore(@NonNull final ULocale supported,
- @NonNull final ULocale desired) {
- // Assuming supported/desired is fully expanded.
- if (supported.equals(desired)) {
- return 3; // Exact match.
- }
-
- // Skip language matching since it was already done in calculateMatchingScore.
-
- final String supportedScript = supported.getScript();
- if (supportedScript.isEmpty() || !supportedScript.equals(desired.getScript())) {
- // TODO: Need subscript matching. For example, Hanb should match with Bopo.
- return 1;
- }
-
- final String supportedCountry = supported.getCountry();
- if (supportedCountry.isEmpty() || !supportedCountry.equals(desired.getCountry())) {
- return 2;
- }
-
- // Ignore others e.g. variants, extensions.
- return 3;
- }
-
- private static final class ScoreEntry implements Comparable<ScoreEntry> {
- public int mIndex = -1;
- @NonNull public final byte[] mScore; // matching score of the i-th system languages.
-
- ScoreEntry(@NonNull byte[] score, int index) {
- mScore = new byte[score.length];
- set(score, index);
- }
-
- private void set(@NonNull byte[] score, int index) {
- for (int i = 0; i < mScore.length; ++i) {
- mScore[i] = score[i];
- }
- mIndex = index;
- }
-
- /**
- * Update score and index if the given score is better than this.
- */
- public void updateIfBetter(@NonNull byte[] score, int index) {
- if (compare(mScore, score) == -1) { // mScore < score
- set(score, index);
- }
- }
-
- /**
- * Provides comaprison for bytes[].
- *
- * <p> Comparison does as follows. If the first value of {@code left} is larger than the
- * first value of {@code right}, {@code left} is large than {@code right}. If the first
- * value of {@code left} is less than the first value of {@code right}, {@code left} is less
- * than {@code right}. If the first value of {@code left} and the first value of
- * {@code right} is equal, do the same comparison to the next value. Finally if all values
- * in {@code left} and {@code right} are equal, {@code left} and {@code right} is equal.</p>
- *
- * @param left The length must be equal to {@code right}.
- * @param right The length must be equal to {@code left}.
- * @return 1 if {@code left} is larger than {@code right}. -1 if {@code left} is less than
- * {@code right}. 0 if {@code left} and {@code right} is equal.
- */
- @IntRange(from=-1, to=1)
- private static int compare(@NonNull byte[] left, @NonNull byte[] right) {
- for (int i = 0; i < left.length; ++i) {
- if (left[i] > right[i]) {
- return 1;
- } else if (left[i] < right[i]) {
- return -1;
- }
- }
- return 0;
- }
-
- @Override
- public int compareTo(final ScoreEntry other) {
- return -1 * compare(mScore, other.mScore); // Order by descending order.
- }
- }
-
- /**
- * Filters the given items based on language preferences.
- *
- * <p>For each language found in {@code preferredLocales}, this method tries to copy at most
- * one best-match item from {@code source} to {@code dest}. For example, if
- * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLocales},
- * this method tries to copy at most one English locale, at most one Japanese, and at most one
- * French locale from {@code source} to {@code dest}. Here the best matching English locale
- * will be searched from {@code source} based on matching score. For the score design, see
- * {@link LocaleUtils#calculateMatchingSubScore(ULocale, ULocale)}</p>
- *
- * @param sources Source items to be filtered.
- * @param extractor Type converter from the source items to {@link Locale} object.
- * @param preferredLocales Ordered list of locales with which the input items will be
- * filtered.
- * @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,
- @NonNull LocaleList preferredLocales,
- @NonNull ArrayList<T> dest) {
- if (preferredLocales.isEmpty()) {
- return;
- }
-
- final int numPreferredLocales = preferredLocales.size();
- final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
- final byte[] score = new byte[numPreferredLocales];
- final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
-
- final int sourceSize = sources.size();
- for (int i = 0; i < sourceSize; ++i) {
- final Locale locale = extractor.get(sources.get(i));
- if (locale == null) {
- continue;
- }
-
- boolean canSkip = true;
- for (int j = 0; j < numPreferredLocales; ++j) {
- final Locale preferredLocale = preferredLocales.get(j);
- if (!TextUtils.equals(locale.getLanguage(), preferredLocale.getLanguage())) {
- score[j] = 0;
- continue;
- }
- if (preferredULocaleCache[j] == null) {
- preferredULocaleCache[j] = ULocale.addLikelySubtags(
- ULocale.forLocale(preferredLocale));
- }
- score[j] = calculateMatchingSubScore(
- preferredULocaleCache[j],
- ULocale.addLikelySubtags(ULocale.forLocale(locale)));
- if (canSkip && score[j] != 0) {
- canSkip = false;
- }
- }
- if (canSkip) {
- continue;
- }
-
- final String lang = locale.getLanguage();
- final ScoreEntry bestScore = scoreboard.get(lang);
- if (bestScore == null) {
- scoreboard.put(lang, new ScoreEntry(score, i));
- } else {
- bestScore.updateIfBetter(score, i);
- }
- }
-
- final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
- Arrays.sort(result);
- for (final ScoreEntry entry : result) {
- dest.add(sources.get(entry.mIndex));
- }
- }
-
- 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;
- }
-
- /**
- * 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/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..0650d0af 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -106,6 +106,7 @@
synchronized (entry) {
entry.exceptionCount++;
}
+
recycleSession(session);
}
@@ -116,29 +117,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. */
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 16ca4fc..1038199 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -455,7 +455,7 @@
* Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
*/
public static int resolveColor(Context context, int color, boolean defaultBackgroundIsDark) {
- if (color == Notification.COLOR_DEFAULT) {
+ if (color == Notification.COLOR_DEFAULT || defaultBackgroundIsDark) {
int res = defaultBackgroundIsDark
? com.android.internal.R.color.notification_default_color_dark
: com.android.internal.R.color.notification_default_color_light;
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 21d750c..e65d114 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,24 +1,4 @@
-per-file AsyncChannel*=lorenzo@google.com
-per-file AsyncChannel*=satk@google.com
-per-file AsyncChannel*=silberst@google.com
-per-file BitUtils*=ek@google.com
-per-file BitUtils*=lorenzo@google.com
-per-file BitUtils*=satk@google.com
-per-file MessageUtils*=ek@google.com
-per-file MessageUtils*=lorenzo@google.com
-per-file MessageUtils*=satk@google.com
-per-file Protocol*=ek@google.com
-per-file Protocol*=lorenzo@google.com
-per-file Protocol*=quiche@google.com
-per-file Protocol*=satk@google.com
-per-file Protocol*=silberst@google.com
-per-file RingBuffer*=ek@google.com
-per-file RingBuffer*=lorenzo@google.com
-per-file RingBuffer*=satk@google.com
-per-file State*=ek@google.com
-per-file State*=lorenzo@google.com
-per-file State*=quiche@google.com
-per-file State*=silberst@google.com
-per-file TokenBucket*=ek@google.com
-per-file TokenBucket*=lorenzo@google.com
-per-file TokenBucket*=satk@google.com
+per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
+per-file BitUtils*, MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file Protocol* = etancohen@google.com, lorenzo@google.com
+per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
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/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index b6a654a..97d5a65 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -33,7 +33,7 @@
* {@hide}
*/
oneway interface IInputMethod {
- void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps);
+ void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps);
void bindInput(in InputBinding binding);
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/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6e661e1..3ead633 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -72,6 +72,9 @@
// List of the accessibility services to which the user has granted
// permission to put the device into touch exploration mode.
optional SettingProto touch_exploration_granted_accessibility_services = 31;
+ optional SettingProto minimum_ui_timeout_enabled = 32 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto minimum_ui_timeout_ms = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
}
optional Accessibility accessibility = 2;
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 25059be..8836c2e 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -72,7 +72,8 @@
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 hint = 1;
- repeated ManagedServiceInfoProto listeners = 2;
+ reserved 2; // ManagedServiceInfoProto listeners
+ repeated android.content.ComponentNameProto listener_components = 3;
}
message ManagedServiceInfoProto {
@@ -199,6 +200,7 @@
optional string condition_id = 8;
optional ConditionProto condition = 9;
optional android.content.ComponentNameProto component = 10;
+ optional ZenPolicyProto zenPolicy = 11;
}
// A dump from com.android.server.notification.ZenModeHelper.
@@ -211,3 +213,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 9ab55d6..1df3f7f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3054,6 +3054,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.
@@ -3756,6 +3768,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" />
@@ -4112,7 +4128,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-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6932be3..e153082 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -284,7 +284,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'envoyer et d\'afficher des SMS ?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Stockage"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"accéder à des photos, à des contenus multimédias et à des fichiers sur votre appareil"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder aux photos, contenus multimédias et fichiers sur votre appareil ?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux photos, contenus multimédias et fichiers sur votre appareil ?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"enregistrer des fichiers audio"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'enregistrer des contenus audio ?"</string>
@@ -1340,7 +1340,7 @@
<string name="ext_media_unmountable_notification_message" msgid="4193858924381066522">"Appuyez sur la notification pour résoudre le problème"</string>
<string name="ext_media_unmountable_notification_message" product="tv" msgid="3941179940297874950">"La <xliff:g id="NAME">%s</xliff:g> est corrompue. Sélectionnez cette option pour résoudre le problème."</string>
<string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"<xliff:g id="NAME">%s</xliff:g> non compatible"</string>
- <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Cet appareil n\'est pas compatible avec la mémoire de stockage \"<xliff:g id="NAME">%s</xliff:g>\". Appuyez ici pour le configurer dans un format accepté."</string>
+ <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Cet appareil n\'est pas compatible avec le support \"<xliff:g id="NAME">%s</xliff:g>\". Appuyez ici pour le configurer dans un format accepté."</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"Cet appareil n\'est pas compatible avec cette <xliff:g id="NAME">%s</xliff:g>. Sélectionnez cette option pour la configurer dans un format accepté."</string>
<string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"Retrait inattendu de mémoire \"<xliff:g id="NAME">%s</xliff:g>\""</string>
<string name="ext_media_badremoval_notification_message" msgid="8556885808951260574">"Éjectez le périphérique externe avant de le retirer pour éviter toute perte de contenu"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d4b800a..6afe603 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1895,10 +1895,10 @@
<string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> को <b><xliff:g id="LABEL">%2$s</xliff:g></b> में सेव करें?"</string>
<string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को <b><xliff:g id="LABEL">%3$s</xliff:g></b> में सेव करें?"</string>
<string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में सेव करें?"</string>
- <string name="autofill_update_title" msgid="4879673117448810818">"<b><xliff:g id="LABEL">%1$s</xliff:g></b>? में अपडेट करें?"</string>
- <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> को to <b><xliff:g id="LABEL">%2$s</xliff:g></b> में अपडेट करें?"</string>
+ <string name="autofill_update_title" msgid="4879673117448810818">"<b><xliff:g id="LABEL">%1$s</xliff:g></b> में अपडेट करें?"</string>
+ <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> को <b><xliff:g id="LABEL">%2$s</xliff:g></b> में अपडेट करें?"</string>
<string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को <b><xliff:g id="LABEL">%3$s</xliff:g></b> में अपडेट करें?"</string>
- <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में अपडेट करें."</string>
+ <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में अपडेट करें?"</string>
<string name="autofill_save_yes" msgid="6398026094049005921">"सेव करें"</string>
<string name="autofill_save_no" msgid="2625132258725581787">"नहीं, धन्यवाद"</string>
<string name="autofill_update_yes" msgid="310358413273276958">"अपडेट करें"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 8b3f287..6a5f287 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -514,7 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Թույլ է տալիս հավելվածին փոփոխել ձեր լուսանկարների հավաքածուն:"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ճանաչել տեղադրության մասին տվյալները մեդիա բովանդակության հավաքածուից"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Թույլ է տալիս հավելվածին ճանաչել տեղադրության մասին տվյալները ձեր մեդիա բովանդակության հավաքածուից:"</string>
- <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Կենսաչափական սարք չի գտնվել"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Կենսաչափական սարքը հասանելի չէ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4cada60..4915111 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -284,7 +284,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"SMS メッセージの送信と表示を <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"ストレージ"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"端末内の写真、メディア、ファイルへのアクセス"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"端末内の写真、メディア、ファイルへのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"端末内の写真、メディア、ファイルへのアクセスを「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"マイク"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"音声の録音"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"音声の録音を <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index dca94ff..e228d35 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -284,7 +284,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"ಎಸ್ಎಂಎಸ್ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಮತ್ತು ವೀಕ್ಷಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"ಸಂಗ್ರಹಣೆ"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"ಸಾಧನದಲ್ಲಿ ಫೋಟೋಗಳು, ಮಾಧ್ಯಮ ಮತ್ತು ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಫೋಟೋಗಳು, ಮಾಧ್ಯಮ ಮತ್ತು ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"ಸಾಧನದಲ್ಲಿ ಫೋಟೋಗಳು, ಮಾಧ್ಯಮ, ಫೈಲ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"ಮೈಕ್ರೋಫೋನ್"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
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-night/colors.xml b/core/res/res/values-night/colors.xml
index 351f8ea..6bbd258 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -27,5 +27,5 @@
<color name="notification_default_color_dark">#ddffffff</color>
<!-- The background color of a notification card. -->
- <color name="notification_material_background_color">@*android:color/material_grey_900</color>
+ <color name="notification_material_background_color">@color/black</color>
</resources>
\ No newline at end of file
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 5e3675a..c8d4d05 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -51,4 +51,7 @@
-->
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
<style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault"/>
+
+ <!-- Theme for the dialog shown when an app crashes or ANRs. -->
+ <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" />
</resources>
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 4eb2ff3..45cf0f0 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -26,7 +26,7 @@
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
<!-- QS panel background -->
- <item name="colorBackgroundFloating">@color/material_grey_900</item>
+ <item name="colorBackgroundFloating">@color/black</item>
<!-- volume background -->
<item name="panelColorBackground">@color/material_grey_800</item>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index e31f161..c14bce4 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -290,7 +290,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> отправлять и просматривать SMS?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Хранилище"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"доступ к фото, мультимедиа и файлам на вашем устройстве"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к фото, мультимедиа и файлам на устройстве?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к фото, мультимедиа и файлам на устройстве?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"записывать аудио"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> записывать аудио?"</string>
@@ -305,7 +305,7 @@
<string name="permgrouprequest_phone" msgid="9166979577750581037">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> совершать звонки и управлять ими?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Нательные датчики"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"доступ к данным датчиков о состоянии организма"</string>
- <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к данным датчиков о состоянии организма?"</string>
+ <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к данным датчиков о состоянии организма?"</string>
<string name="permgrouplab_aural" msgid="965607064083134896">"Музыка"</string>
<string name="permgroupdesc_aural" msgid="4870189506255958055">"доступ к музыке"</string>
<string name="permgrouprequest_aural" msgid="6787926123071735620">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к музыке?"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 8b89a97..0f353f2 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -514,8 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"ඔබගේ ඡායාරූප එකතුව වෙනස් කිරීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ඔබගේ මාධ්ය එකතුවෙන් ස්ථාන කියවන්න"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ඔබගේ මාධ්ය එකතුවෙන් ස්ථාන කියවීමට යෙදුමට ඉඩ දෙයි."</string>
- <!-- no translation found for biometric_error_hw_unavailable (645781226537551036) -->
- <skip />
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ජීවමිතික දෘඪාංග ලබා ගත නොහැකිය"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ඇඟිලි සලකුණ අඩ වශයෙන් අනාවරණය කර ගැනිණි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ඇඟිලි සලකුණු සංවේදකය අපිරිසිදුයි. කරුණාකර පිරිසිදු කර නැවත උත්සාහ කරන්න."</string>
@@ -523,8 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ඇඟිල්ල වඩා සෙමෙන් ගෙන යන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <!-- no translation found for biometric_not_recognized (5770511773560736082) -->
- <skip />
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"හඳුනා නොගන්නා ලදී"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ඇඟිලි සලකුණ ගබඩා කළ නොහැක. දැනට පවතින ඇඟිලි සලකුණක් ඉවත් කරන්න."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index b175f75..35592a6 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -284,7 +284,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"மெசேஜ்களை அனுப்பவும், பார்க்கவும் <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"சேமிப்பிடம்"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"உங்கள் சாதனத்தில் உள்ள படங்கள், மீடியா மற்றும் கோப்புகளை அணுக வேண்டும்"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"உங்கள் சாதனத்திலுள்ள படங்கள், மீடியா, ஃபைல்கள் ஆகியவற்றை அணுகுவதற்கு <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> பயன்பாட்டை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"உங்கள் சாதனத்திலுள்ள படங்கள், மீடியா, ஃபைல்கள் ஆகியவற்றை அணுகுவதற்கு <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"மைக்ரோஃபோன்"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ஒலிப் பதிவு செய்யலாம்"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"ஆடியோவைப் பதிவு செய்ய <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> பயன்பாட்டை அனுமதிக்கவா?"</string>
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index 377982a..0712cbc 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -15,7 +15,7 @@
-->
<resources>
<style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
- <style name="Theme.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
+ <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
<style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
<style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
index 04df180..1be47ba 100644
--- a/core/res/res/values-watch/themes.xml
+++ b/core/res/res/values-watch/themes.xml
@@ -15,7 +15,7 @@
-->
<resources>
<!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. -->
- <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
+ <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
<item name="windowContentTransitions">false</item>
<item name="windowActivityTransitions">false</item>
<item name="windowCloseOnTouchOutside">false</item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0b04e08..966a5a5 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -272,9 +272,9 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问您的通讯录吗?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"获取此设备的位置信息"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>获取此设备的位置信息吗?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"要允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”获取此设备的位置信息吗?"</string>
<string name="permgrouprequestdetail_location" msgid="1113400215566814664">"只有当您使用该应用时,该应用才有权获取位置信息。"</string>
- <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"一律允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>获取此设备的位置信息吗?"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"一律允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”获取此设备的位置信息吗?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"即使您并未使用该应用,该应用也将始终有权获取位置信息。"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"日历"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"访问您的日历"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index bd4ea2a..bd36ed6 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -514,7 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"允許應用程式修改你的相片收藏。"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"讀取你的媒體收藏的位置資訊"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取你的媒體收藏的位置資訊。"</string>
- <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"找不到生物特徵辨識硬體"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"無法使用生物特徵辨識硬體"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"僅偵測到部分指紋,請再試一次。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋,請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器有髒汙。請清潔感應器,然後再試一次。"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3fed8a3..65b8807 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3573,6 +3573,11 @@
{@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="notificationTimeout" format="integer" />
+ <!-- The minimum timeout in milliseconds that UI controls need to remain on the screen.
+ This setting can be changed at runtime by calling
+ {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
+ android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
+ <attr name="minimumUiTimeout" format="integer" />
<!-- Additional flags as specified in
{@link android.accessibilityservice.AccessibilityServiceInfo}.
This setting can be changed at runtime by calling
@@ -3601,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/ids.xml b/core/res/res/values/ids.xml
index bf7e068..8bca211 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -172,4 +172,7 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_HIDE_TOOLTIP}. -->
<item type="id" name="accessibilityActionHideTooltip" />
+
+ <!-- Accessibility action to notify a window there is an outside touch. -->
+ <item type="id" name="accessibilityActionOutsideTouch" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fa31dce..cc99a4e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,11 +2909,14 @@
<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">
@@ -2942,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..64620f3 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 -->
@@ -4334,6 +4355,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/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
index 339f9c7..e6e0de3 100644
--- a/core/res/res/values/styles_permission_controller.xml
+++ b/core/res/res/values/styles_permission_controller.xml
@@ -15,8 +15,8 @@
limitations under the License.
-->
-<!-- styles for the permission grant dialog. -->
<resources>
+ <!-- styles for the permission grant dialog. -->
<style name="PermissionGrantDialog">
<item name="background">?attr/windowBackground</item>
<item name="elevation">?attr/windowElevation</item>
@@ -95,4 +95,37 @@
<item name="layout_marginEnd">16dp</item>
<item name="layout_marginBottom">4dp</item>
</style>
+
+ <!-- styles for the permission review screen. -->
+ <style name="PermissionReviewDescription">
+ <item name="layout_marginTop">20dp</item>
+ <item name="layout_marginStart">24dp</item>
+ <item name="layout_marginBottom">16dp</item>
+ <item name="layout_marginEnd">24dp</item>
+ </style>
+
+ <style name="PermissionReviewTitleIcon">
+ <item name="layout_marginTop">4dp</item>
+ <item name="layout_width">36dp</item>
+ <item name="layout_height">36dp</item>
+ <item name="scaleType">fitCenter</item>
+ </style>
+
+ <style name="PermissionReviewTitleMessage"
+ parent="@style/TextAppearance.DeviceDefault">
+ <item name="paddingStart">22dp</item>
+ <item name="textSize">20sp</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="PermissionReviewSettings">
+ <item name="layout_marginStart">8dp</item>
+ <item name="layout_marginEnd">8dp</item>
+ </style>
+
+ <style name="PermissionReviewButtonBar">
+ <item name="layout_marginStart">24dp</item>
+ <item name="layout_marginEnd">16dp</item>
+ <item name="layout_marginBottom">4dp</item>
+ </style>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 92cca72..7b8eced 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -218,6 +218,7 @@
<java-symbol type="id" name="selection_end_handle" />
<java-symbol type="id" name="insertion_handle" />
<java-symbol type="id" name="accessibilityActionClickOnClickableSpan" />
+ <java-symbol type="id" name="accessibilityActionOutsideTouch" />
<java-symbol type="id" name="camera" />
<java-symbol type="id" name="mic" />
<java-symbol type="id" name="overlay" />
@@ -2099,7 +2100,7 @@
<java-symbol type="string" name="vpn_lockdown_error" />
<java-symbol type="string" name="vpn_lockdown_config" />
<java-symbol type="string" name="wallpaper_binding_label" />
- <java-symbol type="style" name="Theme.Dialog.AppError" />
+ <java-symbol type="style" name="Theme.DeviceDefault.Dialog.AppError" />
<java-symbol type="style" name="Theme.Leanback.Dialog.Alert" />
<java-symbol type="style" name="Theme.Toast" />
<java-symbol type="xml" name="storage_list" />
@@ -3082,6 +3083,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/values/themes.xml b/core/res/res/values/themes.xml
index 3937af5..a7530ce 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -869,13 +869,6 @@
<!-- System themes -->
<eat-comment />
- <!-- Theme for the dialog shown when an app crashes or ANRs. -->
- <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog.Alert">
- <item name="windowContentTransitions">false</item>
- <item name="windowActivityTransitions">false</item>
- <item name="windowCloseOnTouchOutside">false</item>
- </style>
-
<!-- Special theme for the recent apps dialog, to allow customization
with overlays. -->
<style name="Theme.Dialog.RecentApplications" parent="Theme.DeviceDefault.Light.Dialog">
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 14e5082..92096ab 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -719,6 +719,13 @@
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
</style>
+ <!-- Theme for the dialog shown when an app crashes or ANRs. -->
+ <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog.Alert">
+ <item name="windowContentTransitions">false</item>
+ <item name="windowActivityTransitions">false</item>
+ <item name="windowCloseOnTouchOutside">false</item>
+ </style>
+
<style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
<!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
index 3bc93cd..369cee3 100644
--- a/core/res/res/values/themes_permission_controller.xml
+++ b/core/res/res/values/themes_permission_controller.xml
@@ -15,8 +15,8 @@
limitations under the License.
-->
-<!-- themes for the permission grant dialog. -->
<resources>
+ <!-- themes for the permission grant dialog. -->
<style name="Theme.DeviceDefault.PermissionGrantApp"
parent="@style/Theme.DeviceDefault.Light.Panel">
<item name="windowIsFloating">false</item>
@@ -32,4 +32,13 @@
<item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
<item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
</style>
+
+ <!-- themes for the permission review dialog. -->
+ <style name="Theme.DeviceDefault.PermissionReviewApp"
+ parent="@style/Theme.DeviceDefault.Settings">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ <item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
+ <item name="buttonBarStyle">@style/PermissionReviewButtonBar</item>
+ </style>
</resources>
diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
new file mode 100644
index 0000000..7218b3a2
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import static android.content.pm.PackageBuilder.builder;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidHidlUpdater}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+ private static final String OTHER_LIBRARY = "other.library";
+
+ @Test
+ public void targeted_at_O() {
+ PackageBuilder before = builder()
+ .targetSdkVersion(Build.VERSION_CODES.O);
+
+ // Should add both HIDL libraries
+ PackageBuilder after = builder()
+ .targetSdkVersion(Build.VERSION_CODES.O)
+ .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_not_empty_usesLibraries() {
+ PackageBuilder before = builder()
+ .targetSdkVersion(Build.VERSION_CODES.O)
+ .requiredLibraries(OTHER_LIBRARY);
+
+ // The hidl jars should be added at the start of the list because it
+ // is not on the bootclasspath and the package targets pre-P.
+ PackageBuilder after = builder()
+ .targetSdkVersion(Build.VERSION_CODES.O)
+ .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE, OTHER_LIBRARY);
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void targeted_at_O_in_usesLibraries() {
+ PackageBuilder before = builder()
+ .targetSdkVersion(Build.VERSION_CODES.O)
+ .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
+
+ // No change is required because although the HIDL libraries has been removed from
+ // the bootclasspath the package explicitly requests it.
+ checkBackwardsCompatibility(before, before);
+ }
+
+ @Test
+ public void in_usesLibraries() {
+ PackageBuilder before = builder().requiredLibraries(ANDROID_HIDL_BASE);
+
+ // No change is required because the package explicitly requests the HIDL libraries
+ // and is targeted at the current version so does not need backwards compatibility.
+ checkBackwardsCompatibility(before, before);
+ }
+
+ @Test
+ public void in_usesOptionalLibraries() {
+ PackageBuilder before = builder().optionalLibraries(ANDROID_HIDL_BASE);
+
+ // No change is required because the package explicitly requests the HIDL libraries
+ // and is targeted at the current version so does not need backwards compatibility.
+ checkBackwardsCompatibility(before, before);
+ }
+
+ private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
+ checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new);
+ }
+}
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/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 9c9f11b..20fe162 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -17,6 +17,22 @@
package android.os;
import static android.os.FileUtils.roundStorageSize;
+import static android.os.FileUtils.translateModePfdToPosix;
+import static android.os.FileUtils.translateModePosixToPfd;
+import static android.os.FileUtils.translateModePosixToString;
+import static android.os.FileUtils.translateModeStringToPosix;
+import static android.os.ParcelFileDescriptor.MODE_APPEND;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.O_APPEND;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDONLY;
+import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_TRUNC;
+import static android.system.OsConstants.O_WRONLY;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
@@ -476,6 +492,32 @@
assertEquals(G64, roundStorageSize(G32 + 1));
}
+ @Test
+ public void testTranslateMode() throws Exception {
+ assertTranslate("r", O_RDONLY, MODE_READ_ONLY);
+
+ assertTranslate("rw", O_RDWR | O_CREAT,
+ MODE_READ_WRITE | MODE_CREATE);
+ assertTranslate("rwt", O_RDWR | O_CREAT | O_TRUNC,
+ MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+ assertTranslate("rwa", O_RDWR | O_CREAT | O_APPEND,
+ MODE_READ_WRITE | MODE_CREATE | MODE_APPEND);
+
+ assertTranslate("w", O_WRONLY | O_CREAT,
+ MODE_WRITE_ONLY | MODE_CREATE | MODE_CREATE);
+ assertTranslate("wt", O_WRONLY | O_CREAT | O_TRUNC,
+ MODE_WRITE_ONLY | MODE_CREATE | MODE_TRUNCATE);
+ assertTranslate("wa", O_WRONLY | O_CREAT | O_APPEND,
+ MODE_WRITE_ONLY | MODE_CREATE | MODE_APPEND);
+ }
+
+ private static void assertTranslate(String string, int posix, int pfd) {
+ assertEquals(posix, translateModeStringToPosix(string));
+ assertEquals(string, translateModePosixToString(posix));
+ assertEquals(pfd, translateModePosixToPfd(posix));
+ assertEquals(posix, translateModePfdToPosix(pfd));
+ }
+
private static void assertNameEquals(String expected, File actual) {
assertEquals(expected, actual.getName());
}
diff --git a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
index 920988b..9e44554 100644
--- a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
+++ b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
@@ -24,7 +24,6 @@
import android.provider.Settings;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
/**
* Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's
@@ -76,9 +75,11 @@
}
private void setPersistentVrModeEnabled(boolean enable) throws Throwable {
- mVrManager.setPersistentVrModeEnabled(enable);
- // Allow the system time to send out callbacks for persistent VR mode.
- Thread.sleep(200);
+ if (mVrManager != null) {
+ mVrManager.setPersistentVrModeEnabled(enable);
+ // Allow the system time to send out callbacks for persistent VR mode.
+ Thread.sleep(200);
+ }
}
@SmallTest
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..565a3ec 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -342,6 +342,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/core/tests/webkit/Android.mk b/core/tests/webkit/Android.mk
deleted file mode 100644
index 45f6957..0000000
--- a/core/tests/webkit/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, unit_tests_src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test
-
-LOCAL_PACKAGE_NAME := WebViewLoadingTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_REQUIRED_MODULES := \
- WebViewLoadingOnDiskTestApk \
- WebViewLoadingFromApkTestApk
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/webkit/AndroidManifest.xml b/core/tests/webkit/AndroidManifest.xml
deleted file mode 100644
index 42accdf..0000000
--- a/core/tests/webkit/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.webkit.tests"
- android:sharedUserId="android.uid.system">
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.webkit.tests"
- android:label="Frameworks WebView Loader Tests" />
-
-</manifest>
diff --git a/core/tests/webkit/AndroidTest.xml b/core/tests/webkit/AndroidTest.xml
deleted file mode 100644
index 4c50b7d..0000000
--- a/core/tests/webkit/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Runs Frameworks WebView Loading Tests.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-instrumentation" />
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="test-file-name" value="WebViewLoadingTests.apk" />
- <option name="test-file-name" value="WebViewLoadingOnDiskTestApk.apk" />
- <option name="test-file-name" value="WebViewLoadingFromApkTestApk.apk" />
- <option name="cleanup-apks" value="true" />
- <option name="alt-dir" value="out" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.webkit.tests" />
- </test>
-</configuration>
diff --git a/core/tests/webkit/OWNERS b/core/tests/webkit/OWNERS
deleted file mode 100644
index 00e540a..0000000
--- a/core/tests/webkit/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-changwan@google.com
-tobiasjs@google.com
-torne@google.com
diff --git a/core/tests/webkit/apk_with_native_libs/Android.mk b/core/tests/webkit/apk_with_native_libs/Android.mk
deleted file mode 100644
index e18a7e0..0000000
--- a/core/tests/webkit/apk_with_native_libs/Android.mk
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-MY_PATH := $(LOCAL_PATH)
-
-# Set shared variables
-MY_MODULE_TAGS := optional
-MY_JNI_SHARED_LIBRARIES := libwebviewtest_jni
-MY_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-MY_SRC_FILES := $(call all-java-files-under, src)
-MY_CFLAGS := -Wall -Werror
-MY_SDK_VERSION := system_current
-MY_PROGUARD_ENABLED := disabled
-MY_MULTILIB := both
-
-# Recurse down the file tree.
-include $(call all-subdir-makefiles)
-
-
-
-# Builds an apk containing native libraries that will be unzipped on the device.
-include $(CLEAR_VARS)
-
-LOCAL_PATH := $(MY_PATH)
-LOCAL_PACKAGE_NAME := WebViewLoadingOnDiskTestApk
-LOCAL_MANIFEST_FILE := ondisk/AndroidManifest.xml
-
-LOCAL_MODULE_TAGS := $(MY_MODULE_TAGS)
-LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
-LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
-LOCAL_SRC_FILES := $(MY_SRC_FILES)
-LOCAL_CFLAGS := $(MY_CFLAGS)
-LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
-LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
-LOCAL_MULTILIB := $(MY_MULTILIB)
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
-
-
-# Builds an apk containing uncompressed native libraries that have to be
-# accessed through the APK itself on the device.
-include $(CLEAR_VARS)
-
-LOCAL_PATH := $(MY_PATH)
-LOCAL_PACKAGE_NAME := WebViewLoadingFromApkTestApk
-LOCAL_MANIFEST_FILE := inapk/AndroidManifest.xml
-
-LOCAL_MODULE_TAGS := $(MY_MODULE_TAGS)
-LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
-LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
-LOCAL_SRC_FILES := $(MY_SRC_FILES)
-LOCAL_CFLAGS := $(MY_CFLAGS)
-LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
-LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
-LOCAL_MULTILIB := $(MY_MULTILIB)
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml b/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml
deleted file mode 100644
index 868b238..0000000
--- a/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.webviewloading_test_from_apk"
- android:versionCode="1"
- android:versionName="0.0.0.1">
-
- <application android:label="WebView Loading Test APK"
- android:multiArch="true"
- android:extractNativeLibs="false">
- <meta-data android:name="com.android.webview.WebViewLibrary"
- android:value="libwebviewtest_jni.so" />
- </application>
-</manifest>
diff --git a/core/tests/webkit/apk_with_native_libs/jni/Android.mk b/core/tests/webkit/apk_with_native_libs/jni/Android.mk
deleted file mode 100644
index fd5b5eb..0000000
--- a/core/tests/webkit/apk_with_native_libs/jni/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libwebviewtest_jni
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := WebViewTestJniOnLoad.cpp
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
deleted file mode 100644
index 0ced4ee..0000000
--- a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <jni.h>
-
-jint JNI_OnLoad(JavaVM * /*vm*/, void * /*reserved*/) {
- return JNI_VERSION_1_4;
-}
diff --git a/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml b/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml
deleted file mode 100644
index ffffeb8..0000000
--- a/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.webviewloading_test_on_disk"
- android:versionCode="1"
- android:versionName="0.0.0.1">
-
- <application android:label="WebView Loading Test APK"
- android:multiArch="true">
- <meta-data android:name="com.android.webview.WebViewLibrary"
- android:value="libwebviewtest_jni.so" />
- </application>
-</manifest>
diff --git a/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java b/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java
deleted file mode 100644
index 0efa4b4..0000000
--- a/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.webview.chromium;
-
-/**
- * An empty class for testing purposes.
- */
-public class WebViewLoadingTestClass {
-}
diff --git a/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java b/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java
deleted file mode 100644
index e2f2d37..0000000
--- a/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.Log;
-
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.InstrumentationRegistry;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Unit tests for {@link WebViewLibraryLoader}.
- * Use the following command to run these tests:
- * make WebViewLoadingTests \
- * && adb install -r -d \
- * ${ANDROID_PRODUCT_OUT}/data/app/WebViewLoadingTests/WebViewLoadingTests.apk \
- * && adb shell am instrument -e class 'android.webkit.WebViewLibraryLoaderTest' -w \
- * 'com.android.webkit.tests/android.support.test.runner.AndroidJUnitRunner'
- */
-@RunWith(AndroidJUnit4.class)
-public final class WebViewLibraryLoaderTest {
- private static final String WEBVIEW_LIBS_ON_DISK_TEST_APK =
- "com.android.webviewloading_test_on_disk";
- private static final String WEBVIEW_LIBS_IN_APK_TEST_APK =
- "com.android.webviewloading_test_from_apk";
- private static final String WEBVIEW_LOADING_TEST_NATIVE_LIB = "libwebviewtest_jni.so";
-
- private PackageInfo webviewOnDiskPackageInfo;
- private PackageInfo webviewFromApkPackageInfo;
-
- @Before public void setUp() throws PackageManager.NameNotFoundException {
- PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
- webviewOnDiskPackageInfo =
- pm.getPackageInfo(WEBVIEW_LIBS_ON_DISK_TEST_APK, PackageManager.GET_META_DATA);
- webviewFromApkPackageInfo =
- pm.getPackageInfo(WEBVIEW_LIBS_IN_APK_TEST_APK, PackageManager.GET_META_DATA);
- }
-
- private static boolean is64BitDevice() {
- return Build.SUPPORTED_64_BIT_ABIS.length > 0;
- }
-
- // We test the getWebViewNativeLibraryDirectory method here because it handled several different
- // cases/combinations and it seems unnecessary to create one test-apk for each such combination
- // and arch.
-
- /**
- * Ensure we fetch the correct native library directories in the multi-arch case where
- * the primary ABI is 64-bit.
- */
- @SmallTest
- @Test public void testGetWebViewLibDirMultiArchPrimary64bit() {
- final String nativeLib = "nativeLib";
- final String secondaryNativeLib = "secondaryNativeLib";
- PackageInfo packageInfo = new PackageInfo();
- ApplicationInfo ai = new ApplicationInfoBuilder().
- // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP
- setPrimaryCpuAbi("arm64-v8a").
- setNativeLibraryDir(nativeLib).
- setSecondaryCpuAbi("armeabi").
- setSecondaryNativeLibraryDir(secondaryNativeLib).
- create();
- packageInfo.applicationInfo = ai;
- String actual32Lib =
- WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, false /* is64bit */);
- String actual64Lib =
- WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, true /* is64bit */);
- assertEquals(nativeLib, actual64Lib);
- assertEquals(secondaryNativeLib, actual32Lib);
- }
-
- /**
- * Ensure we fetch the correct native library directory in the 64-bit single-arch case.
- */
- @SmallTest
- @Test public void testGetWebViewLibDirSingleArch64bit() {
- final String nativeLib = "nativeLib";
- PackageInfo packageInfo = new PackageInfo();
- ApplicationInfo ai = new ApplicationInfoBuilder().
- // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP
- setPrimaryCpuAbi("arm64-v8a").
- setNativeLibraryDir(nativeLib).
- create();
- packageInfo.applicationInfo = ai;
- String actual64Lib =
- WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, true /* is64bit */);
- assertEquals(nativeLib, actual64Lib);
- }
-
- /**
- * Ensure we fetch the correct native library directory in the 32-bit single-arch case.
- */
- @SmallTest
- @Test public void testGetWebViewLibDirSingleArch32bit() {
- final String nativeLib = "nativeLib";
- PackageInfo packageInfo = new PackageInfo();
- ApplicationInfo ai = new ApplicationInfoBuilder().
- // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP
- setPrimaryCpuAbi("armeabi-v7a").
- setNativeLibraryDir(nativeLib).
- create();
- packageInfo.applicationInfo = ai;
- String actual32Lib =
- WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, false /* is64bit */);
- assertEquals(nativeLib, actual32Lib);
- }
-
- /**
- * Ensure we fetch the correct 32-bit library path from an APK with 32-bit and 64-bit
- * libraries unzipped onto disk.
- */
- @MediumTest
- @Test public void testGetWebViewLibraryPathOnDisk32Bit()
- throws WebViewFactory.MissingWebViewPackageException {
- WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewOnDiskPackageInfo, false /* is64bit */);
- String expectedLibaryDirectory = is64BitDevice() ?
- webviewOnDiskPackageInfo.applicationInfo.secondaryNativeLibraryDir :
- webviewOnDiskPackageInfo.applicationInfo.nativeLibraryDir;
- String lib32Path = expectedLibaryDirectory + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB;
- assertEquals("Fetched incorrect 32-bit path from WebView library.",
- lib32Path, actualNativeLib.path);
- }
-
- /**
- * Ensure we fetch the correct 64-bit library path from an APK with 32-bit and 64-bit
- * libraries unzipped onto disk.
- */
- @MediumTest
- @Test public void testGetWebViewLibraryPathOnDisk64Bit()
- throws WebViewFactory.MissingWebViewPackageException {
- // A 32-bit device will not unpack 64-bit libraries.
- if (!is64BitDevice()) return;
-
- WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewOnDiskPackageInfo, true /* is64bit */);
- String lib64Path = webviewOnDiskPackageInfo.applicationInfo.nativeLibraryDir
- + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB;
- assertEquals("Fetched incorrect 64-bit path from WebView library.",
- lib64Path, actualNativeLib.path);
- }
-
- /**
- * Check the size of the 32-bit library fetched from an APK with both 32-bit and 64-bit
- * libraries unzipped onto disk.
- */
- @MediumTest
- @Test public void testGetWebView32BitLibrarySizeOnDiskIsNonZero()
- throws WebViewFactory.MissingWebViewPackageException {
- WebViewLibraryLoader.WebViewNativeLibrary actual32BitNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewOnDiskPackageInfo, false /* is64bit */);
- assertTrue(actual32BitNativeLib.size > 0);
- }
-
- /**
- * Check the size of the 64-bit library fetched from an APK with both 32-bit and 64-bit
- * libraries unzipped onto disk.
- */
- @MediumTest
- @Test public void testGetWebView64BitLibrarySizeOnDiskIsNonZero()
- throws WebViewFactory.MissingWebViewPackageException {
- // A 32-bit device will not unpack 64-bit libraries.
- if (!is64BitDevice()) return;
- WebViewLibraryLoader.WebViewNativeLibrary actual64BitNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewOnDiskPackageInfo, true /* is64bit */);
- assertTrue(actual64BitNativeLib.size > 0);
- }
-
- /**
- * Ensure we fetch the correct 32-bit library path from an APK with both 32-bit and 64-bit
- * libraries stored uncompressed in the APK.
- */
- @MediumTest
- @Test public void testGetWebView32BitLibraryPathFromApk()
- throws WebViewFactory.MissingWebViewPackageException, IOException {
- WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewFromApkPackageInfo, false /* is64bit */);
- // The device might have ignored the app's request to not extract native libs, so first
- // check whether the library paths match those of extracted libraries.
- String expectedLibaryDirectory = is64BitDevice() ?
- webviewFromApkPackageInfo.applicationInfo.secondaryNativeLibraryDir :
- webviewFromApkPackageInfo.applicationInfo.nativeLibraryDir;
- String lib32Path = expectedLibaryDirectory + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB;
- if (lib32Path.equals(actualNativeLib.path)) {
- // If the libraries were extracted to disk, ensure that they're actually there.
- assertTrue("The given WebView library doesn't exist.",
- new File(actualNativeLib.path).exists());
- } else { // The libraries were not extracted to disk.
- assertIsValidZipEntryPath(actualNativeLib.path,
- webviewFromApkPackageInfo.applicationInfo.sourceDir);
- }
- }
-
- /**
- * Ensure we fetch the correct 32-bit library path from an APK with both 32-bit and 64-bit
- * libraries stored uncompressed in the APK.
- */
- @MediumTest
- @Test public void testGetWebView64BitLibraryPathFromApk()
- throws WebViewFactory.MissingWebViewPackageException, IOException {
- // A 32-bit device will not unpack 64-bit libraries.
- if (!is64BitDevice()) return;
-
- WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewFromApkPackageInfo, true /* is64bit */);
- assertIsValidZipEntryPath(actualNativeLib.path,
- webviewFromApkPackageInfo.applicationInfo.sourceDir);
- }
-
- private static void assertIsValidZipEntryPath(String path, String zipFilePath)
- throws IOException {
- assertTrue("The path to a zip entry must start with the path to the zip file itself."
- + "Expected zip path: " + zipFilePath + ", actual zip entry: " + path,
- path.startsWith(zipFilePath + "!/"));
- String[] pathSplit = path.split("!/");
- assertEquals("A zip file path should have two parts, the zip path, and the zip entry path.",
- 2, pathSplit.length);
- ZipFile zipFile = new ZipFile(pathSplit[0]);
- assertNotNull("Path doesn't point to a valid zip entry: " + path,
- zipFile.getEntry(pathSplit[1]));
- }
-
-
- /**
- * Check the size of the 32-bit library fetched from an APK with both 32-bit and 64-bit
- * libraries stored uncompressed in the APK.
- */
- @MediumTest
- @Test public void testGetWebView32BitLibrarySizeFromApkIsNonZero()
- throws WebViewFactory.MissingWebViewPackageException {
- WebViewLibraryLoader.WebViewNativeLibrary actual32BitNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewFromApkPackageInfo, false /* is64bit */);
- assertTrue(actual32BitNativeLib.size > 0);
- }
-
- /**
- * Check the size of the 64-bit library fetched from an APK with both 32-bit and 64-bit
- * libraries stored uncompressed in the APK.
- */
- @MediumTest
- @Test public void testGetWebView64BitLibrarySizeFromApkIsNonZero()
- throws WebViewFactory.MissingWebViewPackageException {
- // A 32-bit device will not unpack 64-bit libraries.
- if (!is64BitDevice()) return;
-
- WebViewLibraryLoader.WebViewNativeLibrary actual64BitNativeLib =
- WebViewLibraryLoader.getWebViewNativeLibrary(
- webviewFromApkPackageInfo, true /* is64bit */);
- assertTrue(actual64BitNativeLib.size > 0);
- }
-
- private static class ApplicationInfoBuilder {
- ApplicationInfo ai;
-
- public ApplicationInfoBuilder setPrimaryCpuAbi(String primaryCpuAbi) {
- ai.primaryCpuAbi = primaryCpuAbi;
- return this;
- }
-
- public ApplicationInfoBuilder setSecondaryCpuAbi(String secondaryCpuAbi) {
- ai.secondaryCpuAbi = secondaryCpuAbi;
- return this;
- }
-
- public ApplicationInfoBuilder setNativeLibraryDir(String nativeLibraryDir) {
- ai.nativeLibraryDir = nativeLibraryDir;
- return this;
- }
-
- public ApplicationInfoBuilder setSecondaryNativeLibraryDir(
- String secondaryNativeLibraryDir) {
- ai.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
- return this;
- }
-
- public ApplicationInfoBuilder setMetaData(Bundle metaData) {
- ai.metaData = metaData;
- return this;
- }
-
- public ApplicationInfoBuilder() {
- ai = new android.content.pm.ApplicationInfo();
- }
-
- public ApplicationInfo create() {
- return ai;
- }
- }
-}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6f52fbd..73c10d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -188,6 +188,12 @@
<library name="android.test.runner"
file="/system/framework/android.test.runner.impl.jar" />
+ <!-- In BOOT_JARS historically, and now added to legacy applications. -->
+ <library name="android.hidl.base-V1.0-java"
+ file="/system/framework/android.hidl.base-V1.0-java.jar" />
+ <library name="android.hidl.manager-V1.0-java"
+ file="/system/framework/android.hidl.manager-V1.0-java.jar" />
+
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save package="com.android.providers.downloads" />
@@ -204,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/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/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index d84a207..949c9445 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -60,7 +60,7 @@
}
T& editItemAt(size_t index) {
- CHECK(index < size()) << "ByteBucketArray.getOrCreate(index=" << index
+ CHECK(index < size()) << "ByteBucketArray.editItemAt(index=" << index
<< ") with size=" << size();
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
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/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index a43f58cd..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/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/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/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..78b64b2 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();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 4f30f98..4c78539 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"
@@ -77,7 +77,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)...);
}
@@ -155,7 +155,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..3b1ebd6 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -27,6 +27,8 @@
#include "utils/GLUtils.h"
#include "utils/TraceUtils.h"
+#include <GLES3/gl3.h>
+
#include <GrBackendSurface.h>
#include <SkBlendMode.h>
#include <SkImageInfo.h>
@@ -62,21 +64,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 +93,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
@@ -147,8 +150,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 +170,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..fbdf313 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -34,8 +34,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,
@@ -46,6 +45,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);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 988981d..7f8abb8 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(),
@@ -321,19 +316,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 +348,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 +486,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..b78dea1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -42,15 +42,14 @@
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; }
@@ -59,7 +58,7 @@
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 +111,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..3c281e78 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 {
@@ -118,9 +120,16 @@
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 {
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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 727cef3..ea6a851 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -151,7 +151,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 +417,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 +556,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..8448788 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);
}
/**
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/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 7258a0a..a5dcc72 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();
@@ -246,6 +246,9 @@
mGrContext->releaseResourcesAndAbandonContext();
}
mGrContext = std::move(context);
+ if (mGrContext) {
+ 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..4881172 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"
@@ -399,8 +398,6 @@
free_features_extensions_structs(features);
- DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
-
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
}
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..c35f512 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>
@@ -179,12 +178,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 +195,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 +220,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..5e5d134 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,7 @@
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);
}
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/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 a2bf549..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"
@@ -1014,7 +1014,11 @@
PlayerMessage response;
request.ParseFromArray(pData, pDataLen);
- media_player->invoke(request, &response);
+ process_media_player_call( env, thiz, media_player->invoke(request, &response),
+ "java.lang.RuntimeException", NULL );
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
int size = response.ByteSize();
jbyte* temp = new jbyte[size];
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 adb371d..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,13 +59,15 @@
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);
return JNI_TRUE;
}
-jboolean DoCreateRelroFile(const char* lib, const char* relro) {
+jboolean DoCreateRelroFile(JNIEnv* env, const char* lib, const char* relro,
+ jobject clazzLoader) {
// Try to unlink the old file, since if this is being called, the old one is
// obsolete.
if (unlink(relro) != 0 && errno != ENOENT) {
@@ -82,11 +85,19 @@
ALOGE("Failed to create temporary file %s: %s", relro_tmp, strerror(errno));
return JNI_FALSE;
}
+ android_namespace_t* ns =
+ android::FindNamespaceByClassLoader(env, clazzLoader);
+ if (ns == NULL) {
+ ALOGE("Failed to find classloader namespace");
+ return JNI_FALSE;
+ }
android_dlextinfo extinfo;
- extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO;
+ extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO |
+ ANDROID_DLEXT_USE_NAMESPACE;
extinfo.reserved_addr = gReservedAddress;
extinfo.reserved_size = gReservedSize;
extinfo.relro_fd = tmp_fd;
+ extinfo.library_namespace = ns;
void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo);
int close_result = close(tmp_fd);
if (handle == NULL) {
@@ -143,13 +154,14 @@
return DoReserveAddressSpace(size);
}
-jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro) {
+jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro,
+ jobject clazzLoader) {
jboolean ret = JNI_FALSE;
const char* lib_utf8 = env->GetStringUTFChars(lib, NULL);
if (lib_utf8 != NULL) {
const char* relro_utf8 = env->GetStringUTFChars(relro, NULL);
if (relro_utf8 != NULL) {
- ret = DoCreateRelroFile(lib_utf8, relro_utf8);
+ ret = DoCreateRelroFile(env, lib_utf8, relro_utf8, clazzLoader);
env->ReleaseStringUTFChars(relro, relro_utf8);
}
env->ReleaseStringUTFChars(lib, lib_utf8);
@@ -179,7 +191,7 @@
{ "nativeReserveAddressSpace", "(J)Z",
reinterpret_cast<void*>(ReserveAddressSpace) },
{ "nativeCreateRelroFile",
- "(Ljava/lang/String;Ljava/lang/String;)Z",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Z",
reinterpret_cast<void*>(CreateRelroFile) },
{ "nativeLoadWithRelroFile",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)I",
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
index 6473f0d..0467bff 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
@@ -177,7 +177,7 @@
}
}
textView.getViewTreeObserver().removeOnPreDrawListener(this);
- return false;
+ return true;
}
});
textView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/CarrierDefaultApp/Android.mk b/packages/CarrierDefaultApp/Android.mk
index 5068b3b..df88afd 100644
--- a/packages/CarrierDefaultApp/Android.mk
+++ b/packages/CarrierDefaultApp/Android.mk
@@ -9,8 +9,6 @@
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
-LOCAL_STATIC_JAVA_LIBRARIES := services.net
-
include $(BUILD_PACKAGE)
# This finds and builds the test apk as well, so a single make does both.
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index b1933373..4f67350 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -32,7 +32,6 @@
import android.net.Proxy;
import android.net.TrafficStats;
import android.net.Uri;
-import android.net.dns.ResolvUtil;
import android.net.http.SslError;
import android.os.Bundle;
import android.telephony.CarrierConfigManager;
@@ -159,9 +158,9 @@
private void setNetwork(Network network) {
if (network != null) {
+ network = network.getPrivateDnsBypassingCopy();
mCm.bindProcessToNetwork(network);
- mCm.setProcessDefaultNetworkForHostResolution(
- ResolvUtil.getNetworkWithUseLocalNameserversFlag(network));
+ mCm.setProcessDefaultNetworkForHostResolution(network);
}
mNetwork = network;
}
@@ -242,7 +241,6 @@
private void testForCaptivePortal() {
mTestingThread = new Thread(new Runnable() {
public void run() {
- final Network network = ResolvUtil.makeNetworkWithPrivateDnsBypass(mNetwork);
// Give time for captive portal to open.
try {
Thread.sleep(1000);
@@ -253,7 +251,7 @@
int httpResponseCode = 500;
int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
try {
- urlConnection = (HttpURLConnection) network.openConnection(
+ urlConnection = (HttpURLConnection) mNetwork.openConnection(
new URL(mCm.getCaptivePortalServerUrl()));
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
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-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index ab2892f..d29bd65 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -135,7 +135,7 @@
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"테더링"</string>
<string name="tether_settings_title_all" msgid="8356136101061143841">"테더링 및 휴대용 핫스팟"</string>
<string name="managed_user_title" msgid="8109605045406748842">"모든 직장 앱"</string>
- <string name="user_guest" msgid="8475274842845401871">"손님"</string>
+ <string name="user_guest" msgid="8475274842845401871">"게스트"</string>
<string name="unknown" msgid="1592123443519355854">"알 수 없음"</string>
<string name="running_process_item_user_label" msgid="3129887865552025943">"사용자: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="313159469856372621">"일부 기본값이 설정됨"</string>
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/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 649900b..b9f7323 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -78,12 +78,6 @@
private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
- /**
- * When we connect to multiple profiles, we only want to display a single
- * error even if they all fail. This tracks that state.
- */
- private boolean mIsConnectingErrorPossible;
-
public long getHiSyncId() {
return mHiSyncId;
}
@@ -230,9 +224,6 @@
return;
}
- // Reset the only-show-one-error-dialog tracking variable
- mIsConnectingErrorPossible = true;
-
int preferredProfiles = 0;
for (LocalBluetoothProfile profile : mProfiles) {
if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
@@ -253,8 +244,6 @@
if (!ensurePaired()) {
return;
}
- // Reset the only-show-one-error-dialog tracking variable
- mIsConnectingErrorPossible = true;
for (LocalBluetoothProfile profile : mProfiles) {
if (profile.isAutoConnectable()) {
@@ -271,8 +260,6 @@
*/
public void connectProfile(LocalBluetoothProfile profile) {
mConnectAttempted = SystemClock.elapsedRealtime();
- // Reset the only-show-one-error-dialog tracking variable
- mIsConnectingErrorPossible = true;
connectInt(profile);
// Refresh the UI based on profile.connect() call
refresh();
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/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/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index c6ea480..bd21b83 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1617,6 +1617,12 @@
dumpSetting(s, p,
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
SecureSettingsProto.Accessibility.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED,
+ SecureSettingsProto.Accessibility.MINIMUM_UI_TIMEOUT_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS,
+ SecureSettingsProto.Accessibility.MINIMUM_UI_TIMEOUT_MS);
p.end(accessibilityToken);
dumpSetting(s, p,
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/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 4fc190d..ec35b3d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -16,6 +16,8 @@
package com.android.shell;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.shell.BugreportPrefs.STATE_HIDE;
@@ -235,6 +237,7 @@
private static final Bundle sNotificationBundle = new Bundle();
private boolean mIsWatch;
+ private boolean mIsTv;
private int mLastProgressPercent;
@@ -255,6 +258,9 @@
final Configuration conf = mContext.getResources().getConfiguration();
mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) ==
Configuration.UI_MODE_TYPE_WATCH;
+ PackageManager packageManager = getPackageManager();
+ mIsTv = packageManager.hasSystemFeature(FEATURE_LEANBACK)
+ || packageManager.hasSystemFeature(FEATURE_TELEVISION);
NotificationManager nm = NotificationManager.from(mContext);
nm.createNotificationChannel(
new NotificationChannel(NOTIFICATION_CHANNEL_ID,
@@ -500,8 +506,8 @@
.setProgress(info.max, info.progress, false)
.setOngoing(true);
- // Wear bugreport doesn't need the bug info dialog, screenshot and cancel action.
- if (!mIsWatch) {
+ // Wear and ATV bugreport doesn't need the bug info dialog, screenshot and cancel action.
+ if (!(mIsWatch || mIsTv)) {
final Action cancelAction = new Action.Builder(null, mContext.getString(
com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
index 0734e0d..4fbc226 100644
--- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
+++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
@@ -20,6 +20,7 @@
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.net.Uri;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
@@ -68,6 +69,18 @@
}
@Override
+ public Cursor queryChildDocuments(
+ String parentDocumentId, String[] projection, String sortOrder)
+ throws FileNotFoundException {
+ final Cursor c = super.queryChildDocuments(parentDocumentId, projection, sortOrder);
+ final Bundle extras = new Bundle();
+ extras.putCharSequence(DocumentsContract.EXTRA_INFO,
+ getContext().getText(R.string.bugreport_confirm));
+ c.setExtras(extras);
+ return c;
+ }
+
+ @Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
if (DOC_ID_ROOT.equals(documentId)) {
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/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/operator_name.xml b/packages/SystemUI/res/layout/operator_name.xml
index c4f75e9..015e30a 100644
--- a/packages/SystemUI/res/layout/operator_name.xml
+++ b/packages/SystemUI/res/layout/operator_name.xml
@@ -27,5 +27,6 @@
android:maxLength="20"
android:gravity="center_vertical|start"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true" />
+ android:singleLine="true"
+ android:paddingEnd="5dp" />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 45d2185..2c5120d 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -25,10 +25,10 @@
<color name="notification_legacy_background_color">@*android:color/notification_material_background_color</color>
<!-- The color of the material notification background when dimmed -->
- <color name="notification_material_background_dimmed_color">#aa212121</color>
+ <color name="notification_material_background_dimmed_color">#aa000000</color>
<!-- The color of the dividing line between grouped notifications while . -->
- <color name="notification_divider_color">#000</color>
+ <color name="notification_divider_color">#212121</color>
<!-- The background color of the notification shade -->
<color name="notification_shade_background_color">#181818</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 26eadb5..c168d4e 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>
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/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ca1b489..123fca7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -391,6 +391,9 @@
position = mPages.size() - 1 - position;
}
ViewGroup view = mPages.get(position);
+ if (view.getParent() != null) {
+ container.removeView(view);
+ }
container.addView(view);
updateListening();
return view;
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/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 67db68d..216ed68 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
@@ -1141,9 +1141,13 @@
}
private void updateNotificationColor() {
+ Configuration currentConfig = getResources().getConfiguration();
+ boolean nightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+
mNotificationColor = ContrastColorUtil.resolveContrastColor(mContext,
getStatusBarNotification().getNotification().color,
- getBackgroundColorWithoutTint());
+ getBackgroundColorWithoutTint(), nightMode);
mNotificationColorAmbient = ContrastColorUtil.resolveAmbientColor(mContext,
getStatusBarNotification().getNotification().color);
}
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 2fab58b..da98565 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
@@ -115,6 +115,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 +136,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;
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/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..6b6566c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -415,10 +415,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);
}
/**
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 aa000fd..759e8ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -732,10 +732,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(
@@ -1142,11 +1144,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();
}
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 200964c..57c7e28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -136,6 +136,7 @@
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
+ onThemeChanged();
}
public void setDozeScreenBrightness(int value) {
@@ -484,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/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 2cbb78a..3744105 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -356,10 +356,11 @@
if (supplementalIconId != 0) {
Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
supplementalIcon.mutate().setTint(color);
- listItem.setSupplementalIcon(supplementalIcon, true,
- supplementalIconOnClickListener);
+ listItem.setSupplementalIcon(supplementalIcon, true);
+ listItem.setSupplementalIconListener(supplementalIconOnClickListener);
} else {
listItem.setSupplementalEmptyIcon(true);
+ listItem.setSupplementalIconListener(null);
}
mVolumeLineItems.add(listItem);
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/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..6a3c8a85
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.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.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 = new NotificationGroupManager();
+ private int mId = 0;
+
+ @Mock HeadsUpManager mHeadsUpManager;
+
+ @Before
+ public void setup() {
+ 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(childEntry.row.isHeadsUp()).thenReturn(true);
+ when(mHeadsUpManager.contains(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 testSuppressedSummaryHeadsUpTransfersToChild() {
+ NotificationData.Entry summaryEntry = createSummaryNotification();
+ when(summaryEntry.row.isHeadsUp()).thenReturn(true);
+ when(mHeadsUpManager.contains(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);
+ when(summaryEntry.row.isHeadsUp()).thenReturn(true);
+ NotificationData.Entry childEntry =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry2 =
+ createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ // Trigger a transfer of heads up state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+ when(summaryEntry.row.isHeadsUp()).thenReturn(false);
+ when(childEntry.row.isHeadsUp()).thenReturn(true);
+
+ // 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.contains(summaryEntry.key));
+ assertFalse(mHeadsUpManager.contains(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/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/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 3732ebc..21cfc04 100644
--- a/packages/VpnDialogs/res/values-es-rUS/strings.xml
+++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
- <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que permite controlar el tráfico de la red. Acéptala solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando la VPN está activa."</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando se activa la VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"La VPN está conectada."</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b2e06f9..9bee8db 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -21,6 +21,8 @@
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
@@ -1689,6 +1691,24 @@
this, userState));
}
+ private void scheduleSetAllClientsMinimumUiTimeout(UserState userState) {
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::sendMinimumUiTimeoutChanged,
+ this, userState.mUserClients, userState.mMinimumUiTimeout));
+ }
+
+ private void sendMinimumUiTimeoutChanged(
+ RemoteCallbackList<IAccessibilityManagerClient> userClients, int uiTimeout) {
+ notifyClientsOfServicesMinimumUiTimeoutChange(mGlobalClients, uiTimeout);
+ notifyClientsOfServicesMinimumUiTimeoutChange(userClients, uiTimeout);
+ }
+
+ private void notifyClientsOfServicesMinimumUiTimeoutChange(
+ RemoteCallbackList<IAccessibilityManagerClient> clients, int uiTimeout) {
+ clients.broadcast(ignoreRemoteException(
+ client -> client.setMinimumUiTimeout(uiTimeout)));
+ }
+
private void updateInputFilter(UserState userState) {
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return;
@@ -1822,6 +1842,7 @@
scheduleUpdateClientsIfNeededLocked(userState);
updateRelevantEventsLocked(userState);
updateAccessibilityButtonTargetsLocked(userState);
+ updateMinimumUiTimeoutLocked(userState);
}
private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -1940,6 +1961,7 @@
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
somethingChanged |= readAccessibilityButtonSettingsLocked(userState);
+ somethingChanged |= readUserMinimumUiTimeoutSettingsLocked(userState);
return somethingChanged;
}
@@ -2084,6 +2106,22 @@
return true;
}
+ private boolean readUserMinimumUiTimeoutSettingsLocked(UserState userState) {
+ final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, 0,
+ userState.mUserId) == 1;
+ final int timeout = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, 0,
+ userState.mUserId);
+ if (enabled != userState.mUserMinimumUiTimeoutEnabled
+ || timeout != userState.mUserMinimumUiTimeout) {
+ userState.mUserMinimumUiTimeoutEnabled = enabled;
+ userState.mUserMinimumUiTimeout = timeout;
+ return true;
+ }
+ return false;
+ }
+
/**
* Check if the service that will be enabled by the shortcut is installed. If it isn't,
* clear the value and the associated setting so a sideloaded service can't spoof the
@@ -2250,6 +2288,27 @@
}
}
+ private void updateMinimumUiTimeoutLocked(UserState userState) {
+ int newUiTimeout = 0;
+ if (userState.mUserMinimumUiTimeoutEnabled) {
+ newUiTimeout = userState.mUserMinimumUiTimeout;
+ } else {
+ final List<AccessibilityServiceConnection> services = userState.mBoundServices;
+ final int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ final int serviceUiTimeout = services.get(i).getServiceInfo()
+ .getMinimumUiTimeoutMillis();
+ if (newUiTimeout < serviceUiTimeout) {
+ newUiTimeout = serviceUiTimeout;
+ }
+ }
+ }
+ if (newUiTimeout != userState.mMinimumUiTimeout) {
+ userState.mMinimumUiTimeout = newUiTimeout;
+ scheduleSetAllClientsMinimumUiTimeout(userState);
+ }
+ }
+
@GuardedBy("mLock")
@Override
public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
@@ -2388,6 +2447,20 @@
return mFingerprintGestureDispatcher.onFingerprintGesture(gestureKeyCode);
}
+ /**
+ * Get the minimum timeout for changes to the UI needed by this user. Controls should remain
+ * on the screen for at least this long to give users time to react.
+ *
+ * @return The minimum timeout for the current user in milliseconds.
+ */
+ @Override
+ public int getMinimumUiTimeout() {
+ synchronized(mLock) {
+ final UserState userState = getCurrentUserStateLocked();
+ return userState.mMinimumUiTimeout;
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
@@ -2405,6 +2478,7 @@
pw.append(", navBarMagnificationEnabled="
+ userState.mIsNavBarMagnificationEnabled);
pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
+ pw.append(", minimumUiTimeout=" + userState.mMinimumUiTimeout);
if (mUiAutomationManager.isUiAutomationRunningLocked()) {
pw.append(", ");
mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
@@ -2549,6 +2623,38 @@
return -1;
}
+ private void notifyOutsideTouchIfNeeded(int targetWindowId, int action, Bundle arguments,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int fetchFlags,
+ int interrogatingPid, long interrogatingTid) {
+ if (action != ACTION_CLICK && action != ACTION_LONG_CLICK) {
+ return;
+ }
+
+ final List<Integer> outsideWindowsIds;
+ final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>();
+ synchronized (mLock) {
+ outsideWindowsIds = mSecurityPolicy.getWatchOutsideTouchWindowId(targetWindowId);
+ for (int i = 0; i < outsideWindowsIds.size(); i++) {
+ connectionList.add(getConnectionLocked(outsideWindowsIds.get(i)));
+ }
+ }
+ for (int i = 0; i < connectionList.size(); i++) {
+ final RemoteAccessibilityConnection connection = connectionList.get(i);
+ if (connection != null) {
+ try {
+ connection.mConnection.performAccessibilityAction(
+ AccessibilityNodeInfo.ROOT_ITEM_ID,
+ R.id.accessibilityActionOutsideTouch, arguments, interactionId,
+ callback, fetchFlags, interrogatingPid, interrogatingTid);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re);
+ }
+ }
+ }
+ }
+ }
+
@Override
public void ensureWindowsAvailableTimed() {
synchronized (mLock) {
@@ -2628,6 +2734,8 @@
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
+ notifyOutsideTouchIfNeeded(resolvedWindowId, action, arguments, interactionId, callback,
+ fetchFlags, interrogatingPid, interrogatingTid);
if (activityToken != null) {
LocalServices.getService(ActivityTaskManagerInternal.class)
.setFocusedActivity(activityToken);
@@ -2955,6 +3063,7 @@
public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
private boolean mTouchInteractionInProgress;
+ private boolean mHasWatchOutsideTouchWindow;
private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) {
final int eventType = event.getEventType();
@@ -3112,6 +3221,7 @@
mWindowInfoById.valueAt(i).recycle();
}
mWindowInfoById.clear();
+ mHasWatchOutsideTouchWindow = false;
mFocusedWindowId = INVALID_WINDOW_ID;
if (!mTouchInteractionInProgress) {
@@ -3156,6 +3266,9 @@
activeWindowGone = false;
}
}
+ if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) {
+ mHasWatchOutsideTouchWindow = true;
+ }
mWindows.add(window);
mA11yWindowInfoById.put(windowId, window);
mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo));
@@ -3574,6 +3687,22 @@
return mWindowInfoById.get(windowId);
}
+ private List<Integer> getWatchOutsideTouchWindowId(int targetWindowId) {
+ if (mWindowInfoById != null && mHasWatchOutsideTouchWindow) {
+ final List<Integer> outsideWindowsId = new ArrayList<>();
+ final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId);
+ for (int i = 0; i < mWindowInfoById.size(); i++) {
+ WindowInfo window = mWindowInfoById.valueAt(i);
+ if (window.layer < targetWindow.layer
+ && window.hasFlagWatchOutsideTouch) {
+ outsideWindowsId.add(mWindowInfoById.keyAt(i));
+ }
+ }
+ return outsideWindowsId;
+ }
+ return Collections.emptyList();
+ }
+
private AccessibilityWindowInfo getPictureInPictureWindow() {
if (mWindows != null) {
final int windowCount = mWindows.size();
@@ -3660,6 +3789,7 @@
public ComponentName mServiceToEnableWithShortcut;
public int mLastSentClientState = -1;
+ public int mMinimumUiTimeout = 0;
private int mSoftKeyboardShowMode = 0;
@@ -3674,6 +3804,8 @@
public boolean mIsPerformGesturesEnabled;
public boolean mIsFilterKeyEventsEnabled;
public boolean mAccessibilityFocusOnlyInActiveWindow;
+ public boolean mUserMinimumUiTimeoutEnabled;
+ public int mUserMinimumUiTimeout;
public boolean mBindInstantServiceAllowed;
@@ -3713,6 +3845,9 @@
// Clear event management state.
mLastSentClientState = -1;
+ // clear minimum ui timeout
+ mMinimumUiTimeout = 0;
+
// Clear state persisted in settings.
mEnabledServices.clear();
mTouchExplorationGrantedServices.clear();
@@ -3722,6 +3857,8 @@
mServiceAssignedToAccessibilityButton = null;
mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
+ mUserMinimumUiTimeoutEnabled = false;
+ mUserMinimumUiTimeout = 0;
}
public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -3948,6 +4085,12 @@
private final Uri mAccessibilityButtonComponentIdUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+ private final Uri mUserMinimumUiTimeoutEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED);
+
+ private final Uri mUserMinimumUiTimeoutUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS);
+
public AccessibilityContentObserver(Handler handler) {
super(handler);
}
@@ -3982,6 +4125,10 @@
mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mUserMinimumUiTimeoutEnabledUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mUserMinimumUiTimeoutUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -4032,6 +4179,11 @@
if (readAccessibilityButtonSettingsLocked(userState)) {
onUserStateChangedLocked(userState);
}
+ } else if (mUserMinimumUiTimeoutEnabledUri.equals(uri)
+ || mUserMinimumUiTimeoutUri.equals(uri)) {
+ if (readUserMinimumUiTimeoutSettingsLocked(userState)) {
+ updateMinimumUiTimeoutLocked(userState);
+ }
}
}
}
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/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/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index c26ac17..1b97926 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -698,8 +698,6 @@
@GuardedBy("mQueueLock")
private ArrayList<FullBackupEntry> mFullBackupQueue;
- private BackupPolicyEnforcer mBackupPolicyEnforcer;
-
// Utility: build a new random integer token. The low bits are the ordinal of the
// operation for near-time uniqueness, and the upper bits are random for app-
// side unpredictability.
@@ -899,8 +897,6 @@
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
-
- mBackupPolicyEnforcer = new BackupPolicyEnforcer(context);
}
private void initPackageTracking() {
@@ -2827,10 +2823,6 @@
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
- if (!enable && mBackupPolicyEnforcer.getMandatoryBackupTransport() != null) {
- Slog.w(TAG, "Cannot disable backups when the mandatory backups policy is active.");
- return;
- }
Slog.i(TAG, "Backup enabled => " + enable);
@@ -3085,12 +3077,6 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransport");
- if (!isAllowedByMandatoryBackupTransportPolicy(transportName)) {
- // Don't change the transport if it is not allowed.
- Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
- return mTransportManager.getCurrentTransportName();
- }
-
final long oldId = Binder.clearCallingIdentity();
try {
String previousTransportName = mTransportManager.selectTransport(transportName);
@@ -3105,20 +3091,10 @@
@Override
public void selectBackupTransportAsync(
- ComponentName transportComponent, @Nullable ISelectBackupTransportCallback listener) {
+ ComponentName transportComponent, ISelectBackupTransportCallback listener) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
- if (!isAllowedByMandatoryBackupTransportPolicy(transportComponent)) {
- try {
- if (listener != null) {
- Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
- listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
- }
- return;
- }
+
final long oldId = Binder.clearCallingIdentity();
try {
String transportString = transportComponent.flattenToShortString();
@@ -3140,12 +3116,10 @@
}
try {
- if (listener != null) {
- if (transportName != null) {
- listener.onSuccess(transportName);
- } else {
- listener.onFailure(result);
- }
+ if (transportName != null) {
+ listener.onSuccess(transportName);
+ } else {
+ listener.onFailure(result);
}
} catch (RemoteException e) {
Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
@@ -3156,38 +3130,6 @@
}
}
- /**
- * Returns if the specified transport can be set as the current transport without violating the
- * mandatory backup transport policy.
- */
- private boolean isAllowedByMandatoryBackupTransportPolicy(String transportName) {
- ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
- if (mandatoryBackupTransport == null) {
- return true;
- }
- final String mandatoryBackupTransportName;
- try {
- mandatoryBackupTransportName =
- mTransportManager.getTransportName(mandatoryBackupTransport);
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "mandatory backup transport not registered!");
- return false;
- }
- return TextUtils.equals(mandatoryBackupTransportName, transportName);
- }
-
- /**
- * Returns if the specified transport can be set as the current transport without violating the
- * mandatory backup transport policy.
- */
- private boolean isAllowedByMandatoryBackupTransportPolicy(ComponentName transport) {
- ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
- if (mandatoryBackupTransport == null) {
- return true;
- }
- return mandatoryBackupTransport.equals(transport);
- }
-
private void updateStateForTransport(String newTransportName) {
// Publish the name change
Settings.Secure.putString(mContext.getContentResolver(),
diff --git a/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java b/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
deleted file mode 100644
index 158084a..0000000
--- a/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.android.server.backup;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * A helper class to decouple this service from {@link DevicePolicyManager} in order to improve
- * testability.
- */
-@VisibleForTesting
-public class BackupPolicyEnforcer {
- private DevicePolicyManager mDevicePolicyManager;
-
- public BackupPolicyEnforcer(Context context) {
- mDevicePolicyManager =
- (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
- }
-
- public ComponentName getMandatoryBackupTransport() {
- return mDevicePolicyManager.getMandatoryBackupTransport();
- }
-}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 499c03d..ad2f82c 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -81,6 +81,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseLongArray;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -3637,6 +3638,8 @@
if (DEBUG_BATCH) {
Slog.v(TAG, "Time changed notification from kernel; rebatching");
}
+ // StatsLog requires currentTimeMillis(), which == nowRTC to within usecs.
+ StatsLog.write(StatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
removeImpl(mTimeTickSender);
removeImpl(mDateChangeSender);
rebatchAllAlarms();
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/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index 4016d29..e21a3d7 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -26,7 +26,6 @@
import static android.os.HardwarePropertiesManager.TEMPERATURE_THROTTLING_BELOW_VR_MIN;
import android.Manifest;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
@@ -34,8 +33,8 @@
import android.os.Binder;
import android.os.CpuUsageInfo;
import android.os.IHardwarePropertiesManager;
-import android.os.Process;
import android.os.UserHandle;
+
import com.android.internal.util.DumpUtils;
import com.android.server.vr.VrManagerInternal;
@@ -166,11 +165,11 @@
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (!dpm.isDeviceOwnerApp(callingPackage)
- && !vrService.isCurrentVrListener(callingPackage, userId)
&& mContext.checkCallingOrSelfPermission(Manifest.permission.DEVICE_POWER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("The caller is not a device owner, bound VrListenerService"
- + ", or holding the DEVICE_POWER permission.");
+ != PackageManager.PERMISSION_GRANTED
+ && (vrService == null || !vrService.isCurrentVrListener(callingPackage, userId))) {
+ throw new SecurityException("The caller is neither a device owner"
+ + ", nor holding the DEVICE_POWER permission, nor the current VrListener.");
}
}
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 01e8152..380f6a7 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -612,7 +612,7 @@
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- mResourceId,
+ uid,
mConfig.getSourceAddress(),
mConfig.getDestinationAddress(),
spi,
@@ -679,7 +679,7 @@
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
+ uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -821,13 +821,13 @@
for (int selAddrFamily : ADDRESS_FAMILIES) {
netd.ipSecDeleteSecurityPolicy(
- 0,
+ uid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
mOkey,
0xffffffff);
netd.ipSecDeleteSecurityPolicy(
- 0,
+ uid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
mIkey,
@@ -1083,7 +1083,8 @@
}
checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ int callingUid = Binder.getCallingUid();
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
final int resourceId = mNextResourceId++;
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -1096,7 +1097,7 @@
spi =
mSrvConfig
.getNetdInstance()
- .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
+ .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
userRecord.mSpiRecords.put(
resourceId,
@@ -1264,7 +1265,8 @@
// TODO: Check that underlying network exists, and IP addresses not assigned to a different
// network (b/72316676).
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ int callerUid = Binder.getCallingUid();
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(callerUid);
if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
@@ -1285,7 +1287,7 @@
for (int selAddrFamily : ADDRESS_FAMILIES) {
// Always send down correct local/remote addresses for template.
netd.ipSecAddSecurityPolicy(
- 0, // Use 0 for reqId
+ callerUid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
localAddr,
@@ -1294,7 +1296,7 @@
okey,
0xffffffff);
netd.ipSecAddSecurityPolicy(
- 0, // Use 0 for reqId
+ callerUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
remoteAddr,
@@ -1532,7 +1534,7 @@
mSrvConfig
.getNetdInstance()
.ipSecAddSecurityAssociation(
- resourceId,
+ Binder.getCallingUid(),
c.getMode(),
c.getSourceAddress(),
c.getDestinationAddress(),
@@ -1623,13 +1625,14 @@
@Override
public synchronized void applyTransportModeTransform(
ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ int callingUid = Binder.getCallingUid();
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
checkDirection(direction);
// Get transform record; if no transform is found, will throw IllegalArgumentException
TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
// TODO: make this a function.
- if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
+ if (info.pid != getCallingPid() || info.uid != callingUid) {
throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
}
@@ -1643,7 +1646,7 @@
.getNetdInstance()
.ipSecApplyTransportModeTransform(
socket.getFileDescriptor(),
- resourceId,
+ callingUid,
direction,
c.getSourceAddress(),
c.getDestinationAddress(),
@@ -1675,7 +1678,8 @@
enforceTunnelPermissions(callingPackage);
checkDirection(direction);
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ int callingUid = Binder.getCallingUid();
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
// Get transform record; if no transform is found, will throw IllegalArgumentException
TransformRecord transformInfo =
@@ -1717,7 +1721,7 @@
mSrvConfig
.getNetdInstance()
.ipSecUpdateSecurityPolicy(
- 0, // Use 0 for reqId
+ callingUid,
selAddrFamily,
direction,
tunnelInterfaceInfo.getLocalAddress(),
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index ab7bf28..ee01d86 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -189,6 +189,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 +202,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/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index daf870d..1d163ee 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1565,10 +1565,11 @@
try {
// TODO: support quota shared across interfaces
- mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
+ mNetdService.bandwidthSetInterfaceQuota(iface, quotaBytes);
+
mActiveQuotas.put(iface, quotaBytes);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
synchronized (mTetheringStatsProviders) {
@@ -1599,9 +1600,9 @@
try {
// TODO: support quota shared across interfaces
- mConnector.execute("bandwidth", "removeiquota", iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.bandwidthRemoveInterfaceQuota(iface);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
synchronized (mTetheringStatsProviders) {
@@ -1633,10 +1634,10 @@
try {
// TODO: support alert shared across interfaces
- mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
+ mNetdService.bandwidthSetInterfaceAlert(iface, alertBytes);
mActiveAlerts.put(iface, alertBytes);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
}
@@ -1653,10 +1654,10 @@
try {
// TODO: support alert shared across interfaces
- mConnector.execute("bandwidth", "removeinterfacealert", iface);
+ mNetdService.bandwidthRemoveInterfaceAlert(iface);
mActiveAlerts.remove(iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
}
@@ -1666,18 +1667,15 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("bandwidth", "setglobalalert", alertBytes);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.bandwidthSetGlobalAlert(alertBytes);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
private void setUidOnMeteredNetworkList(int uid, boolean blacklist, boolean enable) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final String chain = blacklist ? "naughtyapps" : "niceapps";
- final String suffix = enable ? "add" : "remove";
-
synchronized (mQuotaLock) {
boolean oldEnable;
SparseBooleanArray quotaList;
@@ -1692,7 +1690,19 @@
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth");
try {
- mConnector.execute("bandwidth", suffix + chain, uid);
+ if (blacklist) {
+ if (enable) {
+ mNetdService.bandwidthAddNaughtyApp(uid);
+ } else {
+ mNetdService.bandwidthRemoveNaughtyApp(uid);
+ }
+ } else {
+ if (enable) {
+ mNetdService.bandwidthAddNiceApp(uid);
+ } else {
+ mNetdService.bandwidthRemoveNiceApp(uid);
+ }
+ }
synchronized (mRulesLock) {
if (enable) {
quotaList.put(uid, true);
@@ -1700,8 +1710,8 @@
quotaList.delete(uid);
}
}
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
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 c721901..7533db1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11941,7 +11941,9 @@
printed = true;
}
pw.print(" "); UserHandle.formatUid(pw, reg.uid);
- pw.print(" "); pw.print(reg.pkg); pw.print(":");
+ pw.print(" "); pw.print(reg.pkg);
+ final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
+ pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) {
pw.print(" IDLE");
}
@@ -17776,6 +17778,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) {
@@ -18043,6 +18058,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) {
@@ -19042,6 +19061,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;
@@ -19266,6 +19287,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(
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index eb41fe7..bcb2b9b 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;
@@ -1901,14 +1902,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/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
index 347a357..cd4d6a3 100644
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -33,7 +33,7 @@
private boolean mConsuming = true;
public BaseErrorDialog(Context context) {
- super(context, com.android.internal.R.style.Theme_Dialog_AppError);
+ super(context, com.android.internal.R.style.Theme_DeviceDefault_Dialog_AppError);
context.assertRuntimeOverlayThemable();
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
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/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 36e7cba..6b09f1b 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -39,12 +39,17 @@
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;
// Callback mechanism received from the client
// (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
private IBiometricPromptReceiver mDialogReceiverFromClient;
@@ -88,15 +93,13 @@
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) {
+ IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
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());
}
@@ -115,8 +118,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 +146,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);
}
@@ -220,7 +221,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 */);
@@ -268,7 +269,7 @@
if (mBundle != null) {
try {
mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
- mAuthenticator.getType());
+ getBiometricType());
} 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..d1371d1 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();
@@ -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();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a181b61..73c4223 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -108,6 +108,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 +219,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) {
super(context, getMetrics(), daemon, halDeviceId, token, listener,
targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
- statusBarService, authenticator);
+ statusBarService);
}
@Override
@@ -715,6 +717,7 @@
pmap.put(mCurrentUserId, stats);
}
mPerformanceStats = stats;
+ mIsCrypto = (opId != 0);
startAuthentication(client, opPackageName);
});
@@ -847,7 +850,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 +1112,4 @@
LockoutResetMonitor monitor) {
mLockoutMonitors.remove(monitor);
}
-}
\ No newline at end of file
+}
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..660710e 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,33 @@
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) {
+ super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+ restricted,
+ owner, bundle, dialogReceiver, statusBarService);
+ }
+
+ @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,10 +156,10 @@
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);
authenticateInternal(client, opId, opPackageName);
}
@@ -143,11 +171,11 @@
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);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -338,8 +366,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 +390,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 +475,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 +615,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 +887,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..9f4fff8 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,33 @@
}
}
+ 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) {
+ super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+ restricted,
+ owner, bundle, dialogReceiver, statusBarService);
+ }
+
+ @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,10 +179,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);
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);
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/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d16c277..a8f7259 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -68,6 +68,7 @@
import android.hardware.usb.UsbManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -112,10 +113,8 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.LocalServices;
-import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
-import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
@@ -149,7 +148,7 @@
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
private static final Class[] messageClasses = {
- Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class
+ Tethering.class, TetherMasterSM.class, IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
@@ -159,21 +158,21 @@
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
private static class TetherState {
- public final TetherInterfaceStateMachine stateMachine;
+ public final IpServer ipServer;
public int lastState;
public int lastError;
- public TetherState(TetherInterfaceStateMachine sm) {
- stateMachine = sm;
+ public TetherState(IpServer ipServer) {
+ this.ipServer = ipServer;
// Assume all state machines start out available and with no errors.
- lastState = IControlsTethering.STATE_AVAILABLE;
+ lastState = IpServer.STATE_AVAILABLE;
lastError = TETHER_ERROR_NO_ERROR;
}
public boolean isCurrentlyServing() {
switch (lastState) {
- case IControlsTethering.STATE_TETHERED:
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_TETHERED:
+ case IpServer.STATE_LOCAL_ONLY:
return true;
default:
return false;
@@ -198,7 +197,7 @@
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
// TODO: Figure out how to merge this and other downstream-tracking objects
// into a single coherent structure.
- private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
+ private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
@@ -604,7 +603,7 @@
}
public int tether(String iface) {
- return tether(iface, IControlsTethering.STATE_TETHERED);
+ return tether(iface, IpServer.STATE_TETHERED);
}
private int tether(String iface, int requestedState) {
@@ -617,7 +616,7 @@
}
// Ignore the error status of the interface. If the interface is available,
// the errors are referring to past tethering attempts anyway.
- if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
+ if (tetherState.lastState != IpServer.STATE_AVAILABLE) {
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE;
}
@@ -626,8 +625,7 @@
// return an error.
//
// TODO: reexamine the threading and messaging model.
- tetherState.stateMachine.sendMessage(
- TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState);
+ tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState);
return TETHER_ERROR_NO_ERROR;
}
}
@@ -644,8 +642,7 @@
Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE;
}
- tetherState.stateMachine.sendMessage(
- TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
return TETHER_ERROR_NO_ERROR;
}
}
@@ -689,11 +686,11 @@
String iface = mTetherStates.keyAt(i);
if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
erroredList.add(iface);
- } else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
+ } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
availableList.add(iface);
- } else if (tetherState.lastState == IControlsTethering.STATE_LOCAL_ONLY) {
+ } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
localOnlyList.add(iface);
- } else if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
+ } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
usbTethered = true;
} else if (cfg.isWifi(iface)) {
@@ -882,10 +879,10 @@
synchronized (Tethering.this.mPublicSync) {
if (!usbConnected && mRndisEnabled) {
// Turn off tethering if it was enabled and there is a disconnect.
- tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE, TETHERING_USB);
+ tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
} else if (usbConfigured && rndisEnabled) {
// Tether if rndis is enabled and usb is configured.
- tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED, TETHERING_USB);
+ tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
}
mRndisEnabled = usbConfigured && rndisEnabled;
}
@@ -959,15 +956,15 @@
if (!TextUtils.isEmpty(ifname)) {
final TetherState ts = mTetherStates.get(ifname);
if (ts != null) {
- ts.stateMachine.unwanted();
+ ts.ipServer.unwanted();
return;
}
}
for (int i = 0; i < mTetherStates.size(); i++) {
- TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine;
- if (tism.interfaceType() == TETHERING_WIFI) {
- tism.unwanted();
+ final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
+ if (ipServer.interfaceType() == TETHERING_WIFI) {
+ ipServer.unwanted();
return;
}
}
@@ -978,15 +975,15 @@
}
private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
- // Map wifiIpMode values to IControlsTethering serving states, inferring
+ // Map wifiIpMode values to IpServer.Callback serving states, inferring
// from mWifiTetherRequested as a final "best guess".
final int ipServingMode;
switch (wifiIpMode) {
case IFACE_IP_MODE_TETHERED:
- ipServingMode = IControlsTethering.STATE_TETHERED;
+ ipServingMode = IpServer.STATE_TETHERED;
break;
case IFACE_IP_MODE_LOCAL_ONLY:
- ipServingMode = IControlsTethering.STATE_LOCAL_ONLY;
+ ipServingMode = IpServer.STATE_LOCAL_ONLY;
break;
default:
mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);
@@ -1041,12 +1038,12 @@
private void changeInterfaceState(String ifname, int requestedState) {
final int result;
switch (requestedState) {
- case IControlsTethering.STATE_UNAVAILABLE:
- case IControlsTethering.STATE_AVAILABLE:
+ case IpServer.STATE_UNAVAILABLE:
+ case IpServer.STATE_AVAILABLE:
result = untether(ifname);
break;
- case IControlsTethering.STATE_TETHERED:
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_TETHERED:
+ case IpServer.STATE_LOCAL_ONLY:
result = tether(ifname, requestedState);
break;
default:
@@ -1104,7 +1101,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
+ if (tetherState.lastState == IpServer.STATE_TETHERED) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -1117,7 +1114,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
+ if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -1177,7 +1174,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState != IControlsTethering.STATE_TETHERED) {
+ if (tetherState.lastState != IpServer.STATE_TETHERED) {
continue; // Skip interfaces that aren't tethered.
}
String iface = mTetherStates.keyAt(i);
@@ -1231,7 +1228,7 @@
// Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList
// so that the garbage collector does not clean up the state machine before it has a chance
// to tear itself down.
- private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+ private final ArrayList<IpServer> mNotifyList;
private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
private final OffloadWrapper mOffload;
@@ -1268,17 +1265,19 @@
public boolean processMessage(Message message) {
logMessage(this, message.what);
switch (message.what) {
- case EVENT_IFACE_SERVING_STATE_ACTIVE:
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ case EVENT_IFACE_SERVING_STATE_ACTIVE: {
+ final IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
transitionTo(mTetherModeAliveState);
break;
- case EVENT_IFACE_SERVING_STATE_INACTIVE:
- who = (TetherInterfaceStateMachine) message.obj;
+ }
+ case EVENT_IFACE_SERVING_STATE_INACTIVE: {
+ final IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
break;
+ }
case EVENT_IFACE_UPDATE_LINKPROPERTIES:
// Silently ignore these for now.
break;
@@ -1410,8 +1409,8 @@
protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
mCurrentUpstreamIfaceSet = ifaces;
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
+ for (IpServer ipServer : mNotifyList) {
+ ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces);
}
}
@@ -1420,13 +1419,13 @@
mOffload.updateUpstreamNetworkState(ns);
}
- private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
+ private void handleInterfaceServingStateActive(int mode, IpServer who) {
if (mNotifyList.indexOf(who) < 0) {
mNotifyList.add(who);
mIPv6TetheringCoordinator.addActiveDownstream(who, mode);
}
- if (mode == IControlsTethering.STATE_TETHERED) {
+ if (mode == IpServer.STATE_TETHERED) {
// No need to notify OffloadController just yet as there are no
// "offload-able" prefixes to pass along. This will handled
// when the TISM informs Tethering of its LinkProperties.
@@ -1441,10 +1440,10 @@
final WifiManager mgr = getWifiManager();
final String iface = who.interfaceName();
switch (mode) {
- case IControlsTethering.STATE_TETHERED:
+ case IpServer.STATE_TETHERED:
mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED);
break;
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_LOCAL_ONLY:
mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY);
break;
default:
@@ -1454,7 +1453,7 @@
}
}
- private void handleInterfaceServingStateInactive(TetherInterfaceStateMachine who) {
+ private void handleInterfaceServingStateInactive(IpServer who) {
mNotifyList.remove(who);
mIPv6TetheringCoordinator.removeActiveDownstream(who);
mOffload.excludeDownstreamInterface(who.interfaceName());
@@ -1563,10 +1562,10 @@
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE: {
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
- who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+ who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
mCurrentUpstreamIfaceSet);
// If there has been a change and an upstream is now
// desired, kick off the selection process.
@@ -1577,7 +1576,7 @@
break;
}
case EVENT_IFACE_SERVING_STATE_INACTIVE: {
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
@@ -1591,7 +1590,7 @@
if (DBG) {
Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
" live requests:");
- for (TetherInterfaceStateMachine o : mNotifyList) {
+ for (IpServer o : mNotifyList) {
Log.d(TAG, " " + o);
}
}
@@ -1605,7 +1604,7 @@
}
case EVENT_IFACE_UPDATE_LINKPROPERTIES: {
final LinkProperties newLp = (LinkProperties) message.obj;
- if (message.arg1 == IControlsTethering.STATE_TETHERED) {
+ if (message.arg1 == IpServer.STATE_TETHERED) {
mOffload.updateDownstreamLinkProperties(newLp);
} else {
mOffload.excludeDownstreamInterface(newLp.getInterfaceName());
@@ -1650,7 +1649,7 @@
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
- TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+ IpServer who = (IpServer) message.obj;
who.sendMessage(mErrorNotification);
break;
case CMD_CLEAR_ERROR:
@@ -1665,8 +1664,8 @@
void notify(int msgType) {
mErrorNotification = msgType;
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- sm.sendMessage(msgType);
+ for (IpServer ipServer : mNotifyList) {
+ ipServer.sendMessage(msgType);
}
}
@@ -1676,7 +1675,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingEnabled");
- notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR);
+ notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR);
}
}
@@ -1684,7 +1683,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingDisabled");
- notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR);
+ notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR);
}
}
@@ -1692,7 +1691,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in startTethering");
- notify(TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR);
+ notify(IpServer.CMD_START_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -1703,7 +1702,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in stopTethering");
- notify(TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR);
+ notify(IpServer.CMD_STOP_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -1714,7 +1713,7 @@
@Override
public void enter() {
Log.e(TAG, "Error in setDnsForwarders");
- notify(TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR);
+ notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR);
try {
mNMService.stopTethering();
} catch (Exception e) {}
@@ -1771,15 +1770,15 @@
// Maybe add prefixes or addresses for downstreams, depending on
// the IP serving mode of each.
- for (TetherInterfaceStateMachine tism : mNotifyList) {
- final LinkProperties lp = tism.linkProperties();
+ for (IpServer ipServer : mNotifyList) {
+ final LinkProperties lp = ipServer.linkProperties();
- switch (tism.servingMode()) {
- case IControlsTethering.STATE_UNAVAILABLE:
- case IControlsTethering.STATE_AVAILABLE:
+ switch (ipServer.servingMode()) {
+ case IpServer.STATE_UNAVAILABLE:
+ case IpServer.STATE_AVAILABLE:
// No usable LinkProperties in these states.
continue;
- case IControlsTethering.STATE_TETHERED:
+ case IpServer.STATE_TETHERED:
// Only add IPv4 /32 and IPv6 /128 prefixes. The
// directly-connected prefixes will be sent as
// downstream "offload-able" prefixes.
@@ -1789,7 +1788,7 @@
localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip));
}
break;
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_LOCAL_ONLY:
// Add prefixes covering all local IPs.
localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp));
break;
@@ -1826,16 +1825,16 @@
pw.print(iface + " - ");
switch (tetherState.lastState) {
- case IControlsTethering.STATE_UNAVAILABLE:
+ case IpServer.STATE_UNAVAILABLE:
pw.print("UnavailableState");
break;
- case IControlsTethering.STATE_AVAILABLE:
+ case IpServer.STATE_AVAILABLE:
pw.print("AvailableState");
break;
- case IControlsTethering.STATE_TETHERED:
+ case IpServer.STATE_TETHERED:
pw.print("TetheredState");
break;
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_LOCAL_ONLY:
pw.print("LocalHotspotState");
break;
default:
@@ -1873,28 +1872,26 @@
return false;
}
- private IControlsTethering makeControlCallback(String ifname) {
- return new IControlsTethering() {
+ private IpServer.Callback makeControlCallback() {
+ return new IpServer.Callback() {
@Override
- public void updateInterfaceState(
- TetherInterfaceStateMachine who, int state, int lastError) {
- notifyInterfaceStateChange(ifname, who, state, lastError);
+ public void updateInterfaceState(IpServer who, int state, int lastError) {
+ notifyInterfaceStateChange(who, state, lastError);
}
@Override
- public void updateLinkProperties(
- TetherInterfaceStateMachine who, LinkProperties newLp) {
- notifyLinkPropertiesChanged(ifname, who, newLp);
+ public void updateLinkProperties(IpServer who, LinkProperties newLp) {
+ notifyLinkPropertiesChanged(who, newLp);
}
};
}
// TODO: Move into TetherMasterSM.
- private void notifyInterfaceStateChange(
- String iface, TetherInterfaceStateMachine who, int state, int error) {
+ private void notifyInterfaceStateChange(IpServer who, int state, int error) {
+ final String iface = who.interfaceName();
synchronized (mPublicSync) {
final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.stateMachine.equals(who)) {
+ if (tetherState != null && tetherState.ipServer.equals(who)) {
tetherState.lastState = state;
tetherState.lastError = error;
} else {
@@ -1908,7 +1905,7 @@
// Notify that we're tethering (or not) this interface.
// This is how data saver for instance knows if the user explicitly
// turned on tethering (thus keeping us from being in data saver mode).
- mPolicyManager.onTetheringChanged(iface, state == IControlsTethering.STATE_TETHERED);
+ mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED);
} catch (RemoteException e) {
// Not really very much we can do here.
}
@@ -1921,12 +1918,12 @@
}
int which;
switch (state) {
- case IControlsTethering.STATE_UNAVAILABLE:
- case IControlsTethering.STATE_AVAILABLE:
+ case IpServer.STATE_UNAVAILABLE:
+ case IpServer.STATE_AVAILABLE:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
break;
- case IControlsTethering.STATE_TETHERED:
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case IpServer.STATE_TETHERED:
+ case IpServer.STATE_LOCAL_ONLY:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE;
break;
default:
@@ -1937,12 +1934,12 @@
sendTetherStateChangedBroadcast();
}
- private void notifyLinkPropertiesChanged(String iface, TetherInterfaceStateMachine who,
- LinkProperties newLp) {
+ private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) {
+ final String iface = who.interfaceName();
final int state;
synchronized (mPublicSync) {
final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.stateMachine.equals(who)) {
+ if (tetherState != null && tetherState.ipServer.equals(who)) {
state = tetherState.lastState;
} else {
mLog.log("got notification from stale iface " + iface);
@@ -1952,7 +1949,7 @@
mLog.log(String.format(
"OBSERVED LinkProperties update iface=%s state=%s lp=%s",
- iface, IControlsTethering.getStateString(state), newLp));
+ iface, IpServer.getStateString(state), newLp));
final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
mTetherMasterSM.sendMessage(which, state, 0, newLp);
}
@@ -1976,11 +1973,11 @@
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
- new TetherInterfaceStateMachine(
- iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
- makeControlCallback(iface), mConfig.enableLegacyDhcpServer, mDeps));
+ new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
+ makeControlCallback(), mConfig.enableLegacyDhcpServer,
+ mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
- tetherState.stateMachine.start();
+ tetherState.ipServer.start();
}
private void stopTrackingInterfaceLocked(final String iface) {
@@ -1989,36 +1986,11 @@
mLog.log("attempting to remove unknown iface (" + iface + "), ignoring");
return;
}
- tetherState.stateMachine.stop();
+ tetherState.ipServer.stop();
mLog.log("removing TetheringInterfaceStateMachine for: " + iface);
mTetherStates.remove(iface);
}
- private static String getIPv4DefaultRouteInterface(NetworkState ns) {
- if (ns == null) return null;
- return getInterfaceForDestination(ns.linkProperties, Inet4Address.ANY);
- }
-
- private static String getIPv6DefaultRouteInterface(NetworkState ns) {
- if (ns == null) return null;
- // An upstream network's IPv6 capability is currently only useful if it
- // can be 64share'd downstream (RFC 7278). For now, that means mobile
- // upstream networks only.
- if (ns.networkCapabilities == null ||
- !ns.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
- return null;
- }
-
- return getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY);
- }
-
- private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
- final RouteInfo ri = (lp != null)
- ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
- : null;
- return (ri != null) ? ri.getInterface() : null;
- }
-
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
deleted file mode 100644
index 2b81347..0000000
--- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity.tethering;
-
-import android.net.LinkProperties;
-
-/**
- * @hide
- *
- * Interface with methods necessary to notify that a given interface is ready for tethering.
- *
- * Rename to something more representative, e.g. IpServingControlCallback.
- *
- * All methods MUST be called on the TetherMasterSM main Looper's thread.
- */
-public class IControlsTethering {
- public static final int STATE_UNAVAILABLE = 0;
- public static final int STATE_AVAILABLE = 1;
- public static final int STATE_TETHERED = 2;
- public static final int STATE_LOCAL_ONLY = 3;
-
- public static String getStateString(int state) {
- switch (state) {
- case STATE_UNAVAILABLE: return "UNAVAILABLE";
- case STATE_AVAILABLE: return "AVAILABLE";
- case STATE_TETHERED: return "TETHERED";
- case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
- }
- return "UNKNOWN: " + state;
- }
-
- /**
- * Notify that |who| has changed its tethering state.
- *
- * TODO: Remove the need for the |who| argument.
- *
- * @param who corresponding instance of a TetherInterfaceStateMachine
- * @param state one of IControlsTethering.STATE_*
- * @param lastError one of ConnectivityManager.TETHER_ERROR_*
- */
- public void updateInterfaceState(TetherInterfaceStateMachine who, int state, int lastError) {}
-
- /**
- * Notify that |who| has new LinkProperties.
- *
- * TODO: Remove the need for the |who| argument.
- *
- * @param who corresponding instance of a TetherInterfaceStateMachine
- * @param newLp the new LinkProperties to report
- */
- public void updateLinkProperties(TetherInterfaceStateMachine who, LinkProperties newLp) {}
-}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index ba67c94..1000148 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity.tethering;
import android.net.ConnectivityManager;
+import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -50,19 +51,19 @@
private static final boolean VDBG = false;
private static class Downstream {
- public final TetherInterfaceStateMachine tism;
- public final int mode; // IControlsTethering.STATE_*
+ public final IpServer ipServer;
+ public final int mode; // IpServer.STATE_*
// Used to append to a ULA /48, constructing a ULA /64 for local use.
public final short subnetId;
- Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId) {
- this.tism = tism;
+ Downstream(IpServer ipServer, int mode, short subnetId) {
+ this.ipServer = ipServer;
this.mode = mode;
this.subnetId = subnetId;
}
}
- private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+ private final ArrayList<IpServer> mNotifyList;
private final SharedLog mLog;
// NOTE: mActiveDownstreams is a list and not a hash data structure because
// we keep active downstreams in arrival order. This is done so /64s can
@@ -74,8 +75,7 @@
private short mNextSubnetId;
private NetworkState mUpstreamNetworkState;
- public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
- SharedLog log) {
+ public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
mNotifyList = notifyList;
mLog = log.forSubComponent(TAG);
mActiveDownstreams = new LinkedList<>();
@@ -83,7 +83,7 @@
mNextSubnetId = 0;
}
- public void addActiveDownstream(TetherInterfaceStateMachine downstream, int mode) {
+ public void addActiveDownstream(IpServer downstream, int mode) {
if (findDownstream(downstream) == null) {
// Adding a new downstream appends it to the list. Adding a
// downstream a second time without first removing it has no effect.
@@ -98,7 +98,7 @@
}
}
- public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
+ public void removeActiveDownstream(IpServer downstream) {
stopIPv6TetheringOn(downstream);
if (mActiveDownstreams.remove(findDownstream(downstream))) {
updateIPv6TetheringInterfaces();
@@ -133,8 +133,8 @@
}
private void stopIPv6TetheringOnAllInterfaces() {
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- stopIPv6TetheringOn(sm);
+ for (IpServer ipServer : mNotifyList) {
+ stopIPv6TetheringOn(ipServer);
}
}
@@ -156,28 +156,28 @@
}
private void updateIPv6TetheringInterfaces() {
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
- sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+ for (IpServer ipServer : mNotifyList) {
+ final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
+ ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
break;
}
}
- private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
- if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
+ private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
+ if (ipServer.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
// TODO: Figure out IPv6 support on PAN interfaces.
return null;
}
- final Downstream ds = findDownstream(sm);
+ final Downstream ds = findDownstream(ipServer);
if (ds == null) return null;
- if (ds.mode == IControlsTethering.STATE_LOCAL_ONLY) {
+ if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
// Build a Unique Locally-assigned Prefix configuration.
return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
}
- // This downstream is in IControlsTethering.STATE_TETHERED mode.
+ // This downstream is in IpServer.STATE_TETHERED mode.
if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
return null;
}
@@ -188,7 +188,7 @@
// IPv6 toward the oldest (first requested) active downstream.
final Downstream currentActive = mActiveDownstreams.peek();
- if (currentActive != null && currentActive.tism == sm) {
+ if (currentActive != null && currentActive.ipServer == ipServer) {
final LinkProperties lp = getIPv6OnlyLinkProperties(
mUpstreamNetworkState.linkProperties);
if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
@@ -199,9 +199,9 @@
return null;
}
- Downstream findDownstream(TetherInterfaceStateMachine tism) {
+ Downstream findDownstream(IpServer ipServer) {
for (Downstream ds : mActiveDownstreams) {
- if (ds.tism == tism) return ds;
+ if (ds.ipServer == ipServer) return ds;
}
return null;
}
@@ -304,7 +304,7 @@
ns.linkProperties);
}
- private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+ private static void stopIPv6TetheringOn(IpServer ipServer) {
+ ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index caa867c..8b40069 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -21,6 +21,7 @@
import android.net.NetworkRequest;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
+import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetdService;
@@ -49,20 +50,12 @@
}
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ ArrayList<IpServer> notifyList, SharedLog log) {
return new IPv6TetheringCoordinator(notifyList, log);
}
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
- return new RouterAdvertisementDaemon(ifParams);
- }
-
- public InterfaceParams getInterfaceParams(String ifName) {
- return InterfaceParams.getByName(ifName);
- }
-
- public INetd getNetdService() {
- return NetdService.getInstance();
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies();
}
public boolean isTetheringSupported() {
@@ -72,9 +65,4 @@
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
-
- public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, DhcpServingParams params,
- SharedLog log) {
- return new DhcpServer(looper, iface, params, log);
- }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 49f33e0..b2287ac 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.inputmethod;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -129,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;
@@ -152,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;
@@ -572,6 +571,11 @@
IBinder mCurToken;
/**
+ * The displayId of current active input method.
+ */
+ int mCurTokenDisplayId = INVALID_DISPLAY;
+
+ /**
* If non-null, this is the input method service we are currently connected
* to.
*/
@@ -1866,7 +1870,9 @@
mCurAttribute = attribute;
// Check if the input method is changing.
- if (mCurId != null && mCurId.equals(mCurMethodId)) {
+ final int displayId = mWindowManagerInternal.getDisplayIdForWindow(
+ mCurFocusedWindow);
+ if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -1930,14 +1936,20 @@
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
+ final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow);
+ mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY;
+
if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
mLastBindTime = SystemClock.uptimeMillis();
mHaveConnection = true;
mCurId = info.getId();
mCurToken = new Binder();
try {
- if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);
- mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD, DEFAULT_DISPLAY);
+ if (DEBUG) {
+ Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
+ + mCurTokenDisplayId);
+ }
+ mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD, mCurTokenDisplayId);
} catch (RemoteException e) {
}
return new InputBindResult(
@@ -1964,8 +1976,9 @@
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_INITIALIZE_IME, mCurMethod, mCurToken));
+ // Dispatch display id for InputMethodService to update context display.
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
+ MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken));
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
@@ -2011,15 +2024,19 @@
if (mCurToken != null) {
try {
- if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken);
+ if (DEBUG) {
+ Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+ + mCurTokenDisplayId);
+ }
if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) {
// The current IME is shown. Hence an IME switch (transition) is happening.
mWindowManagerInternal.saveLastInputMethodWindowForTransition();
}
- mIWindowManager.removeWindowToken(mCurToken, DEFAULT_DISPLAY);
+ mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
} catch (RemoteException e) {
}
mCurToken = null;
+ mCurTokenDisplayId = INVALID_DISPLAY;
}
mCurId = null;
@@ -2785,6 +2802,15 @@
// soft input window if it is shown.
if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
+
+ // If focused display changed, we should unbind current method
+ // to make app window in previous display relayout after Ime
+ // window token removed.
+ final int newFocusDisplayId =
+ mWindowManagerInternal.getDisplayIdForWindow(windowToken);
+ if (newFocusDisplayId != mCurTokenDisplayId) {
+ unbindCurrentMethodLocked(false);
+ }
}
} else if (isTextEditor && doAutoShow && (softInputMode &
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
@@ -3151,7 +3177,7 @@
*/
@Override
public int getInputMethodWindowVisibleHeight() {
- return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
+ return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
}
@BinderThread
@@ -3352,9 +3378,12 @@
case MSG_INITIALIZE_IME:
args = (SomeArgs)msg.obj;
try {
- if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
+ if (DEBUG) {
+ Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
+ + msg.arg1);
+ }
final IBinder token = (IBinder) args.arg2;
- ((IInputMethod) args.arg1).initializeInternal(token,
+ ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
new InputMethodPrivilegedOperationsImpl(this, token));
} catch (RemoteException e) {
}
@@ -4516,6 +4545,7 @@
p.println(" mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + mVisibleBound);
p.println(" mCurToken=" + mCurToken);
+ p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
p.println(" mCurIntent=" + mCurIntent);
method = mCurMethod;
p.println(" mCurMethod=" + mCurMethod);
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 98%
rename from core/java/com/android/internal/inputmethod/InputMethodUtils.java
rename to services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 1410ff9..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;
@@ -1294,7 +1298,6 @@
}
}
- @VisibleForTesting
public static boolean isSoftInputModeStateVisibleAllowed(
int targetSdkVersion, int controlFlags) {
if (targetSdkVersion < Build.VERSION_CODES.P) {
@@ -1309,4 +1312,5 @@
}
return true;
}
+
}
diff --git a/services/core/java/com/android/server/inputmethod/LocaleUtils.java b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
new file mode 100644
index 0000000..4958ece
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.icu.util.ULocale;
+import android.os.LocaleList;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+final class LocaleUtils {
+ public interface LocaleExtractor<T> {
+ @Nullable
+ Locale get(@Nullable T source);
+ }
+
+ /**
+ * Calculates a matching score for the single desired locale.
+ *
+ * @see LocaleUtils#filterByLanguage(List, LocaleExtractor, LocaleList, ArrayList)
+ *
+ * @param supported The locale supported by IME subtype.
+ * @param desired The locale preferred by user.
+ * @return A score based on the locale matching for the default subtype enabling.
+ */
+ @IntRange(from=1, to=3)
+ private static byte calculateMatchingSubScore(@NonNull final ULocale supported,
+ @NonNull final ULocale desired) {
+ // Assuming supported/desired is fully expanded.
+ if (supported.equals(desired)) {
+ return 3; // Exact match.
+ }
+
+ // Skip language matching since it was already done in calculateMatchingScore.
+
+ final String supportedScript = supported.getScript();
+ if (supportedScript.isEmpty() || !supportedScript.equals(desired.getScript())) {
+ // TODO: Need subscript matching. For example, Hanb should match with Bopo.
+ return 1;
+ }
+
+ final String supportedCountry = supported.getCountry();
+ if (supportedCountry.isEmpty() || !supportedCountry.equals(desired.getCountry())) {
+ return 2;
+ }
+
+ // Ignore others e.g. variants, extensions.
+ return 3;
+ }
+
+ private static final class ScoreEntry implements Comparable<ScoreEntry> {
+ public int mIndex = -1;
+ @NonNull public final byte[] mScore; // matching score of the i-th system languages.
+
+ ScoreEntry(@NonNull byte[] score, int index) {
+ mScore = new byte[score.length];
+ set(score, index);
+ }
+
+ private void set(@NonNull byte[] score, int index) {
+ for (int i = 0; i < mScore.length; ++i) {
+ mScore[i] = score[i];
+ }
+ mIndex = index;
+ }
+
+ /**
+ * Update score and index if the given score is better than this.
+ */
+ public void updateIfBetter(@NonNull byte[] score, int index) {
+ if (compare(mScore, score) == -1) { // mScore < score
+ set(score, index);
+ }
+ }
+
+ /**
+ * Provides comaprison for bytes[].
+ *
+ * <p> Comparison does as follows. If the first value of {@code left} is larger than the
+ * first value of {@code right}, {@code left} is large than {@code right}. If the first
+ * value of {@code left} is less than the first value of {@code right}, {@code left} is less
+ * than {@code right}. If the first value of {@code left} and the first value of
+ * {@code right} is equal, do the same comparison to the next value. Finally if all values
+ * in {@code left} and {@code right} are equal, {@code left} and {@code right} is equal.</p>
+ *
+ * @param left The length must be equal to {@code right}.
+ * @param right The length must be equal to {@code left}.
+ * @return 1 if {@code left} is larger than {@code right}. -1 if {@code left} is less than
+ * {@code right}. 0 if {@code left} and {@code right} is equal.
+ */
+ @IntRange(from=-1, to=1)
+ private static int compare(@NonNull byte[] left, @NonNull byte[] right) {
+ for (int i = 0; i < left.length; ++i) {
+ if (left[i] > right[i]) {
+ return 1;
+ } else if (left[i] < right[i]) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public int compareTo(final ScoreEntry other) {
+ return -1 * compare(mScore, other.mScore); // Order by descending order.
+ }
+ }
+
+ /**
+ * Filters the given items based on language preferences.
+ *
+ * <p>For each language found in {@code preferredLocales}, this method tries to copy at most
+ * one best-match item from {@code source} to {@code dest}. For example, if
+ * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLocales},
+ * this method tries to copy at most one English locale, at most one Japanese, and at most one
+ * French locale from {@code source} to {@code dest}. Here the best matching English locale
+ * will be searched from {@code source} based on matching score. For the score design, see
+ * {@link LocaleUtils#calculateMatchingSubScore(ULocale, ULocale)}</p>
+ *
+ * @param sources Source items to be filtered.
+ * @param extractor Type converter from the source items to {@link Locale} object.
+ * @param preferredLocales Ordered list of locales with which the input items will be
+ * filtered.
+ * @param dest Destination into which the filtered items will be added.
+ * @param <T> Type of the data items.
+ */
+ public static <T> void filterByLanguage(
+ @NonNull List<T> sources,
+ @NonNull LocaleExtractor<T> extractor,
+ @NonNull LocaleList preferredLocales,
+ @NonNull ArrayList<T> dest) {
+ if (preferredLocales.isEmpty()) {
+ return;
+ }
+
+ final int numPreferredLocales = preferredLocales.size();
+ final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
+ final byte[] score = new byte[numPreferredLocales];
+ final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
+
+ final int sourceSize = sources.size();
+ for (int i = 0; i < sourceSize; ++i) {
+ final Locale locale = extractor.get(sources.get(i));
+ if (locale == null) {
+ continue;
+ }
+
+ boolean canSkip = true;
+ for (int j = 0; j < numPreferredLocales; ++j) {
+ final Locale preferredLocale = preferredLocales.get(j);
+ if (!TextUtils.equals(locale.getLanguage(), preferredLocale.getLanguage())) {
+ score[j] = 0;
+ continue;
+ }
+ if (preferredULocaleCache[j] == null) {
+ preferredULocaleCache[j] = ULocale.addLikelySubtags(
+ ULocale.forLocale(preferredLocale));
+ }
+ score[j] = calculateMatchingSubScore(
+ preferredULocaleCache[j],
+ ULocale.addLikelySubtags(ULocale.forLocale(locale)));
+ if (canSkip && score[j] != 0) {
+ canSkip = false;
+ }
+ }
+ if (canSkip) {
+ continue;
+ }
+
+ final String lang = locale.getLanguage();
+ final ScoreEntry bestScore = scoreboard.get(lang);
+ if (bestScore == null) {
+ scoreboard.put(lang, new ScoreEntry(score, i));
+ } else {
+ bestScore.updateIfBetter(score, i);
+ }
+ }
+
+ final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
+ Arrays.sort(result);
+ for (final ScoreEntry entry : result) {
+ dest.add(sources.get(entry.mIndex));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index b4bc7f5..452b699 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -46,9 +46,9 @@
static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
private static final int MAX_LOG_SIZE =
- ActivityManager.isLowRamDeviceStatic() ? 20 : 50;
+ ActivityManager.isLowRamDeviceStatic() ? 100 : 400;
private static final int MAX_NETWORK_BLOCKED_LOG_SIZE =
- ActivityManager.isLowRamDeviceStatic() ? 50 : 100;
+ ActivityManager.isLowRamDeviceStatic() ? 100 : 400;
private static final int EVENT_TYPE_GENERIC = 0;
private static final int EVENT_NETWORK_BLOCKED = 1;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 76f9695..48e09d7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -796,7 +796,7 @@
try {
mActivityManager.registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 340ae0a..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,11 +691,13 @@
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)) {
+ if (isPackageOrComponentAllowed(component.toString(), userId)
+ || isPackageOrComponentAllowed(component.getPackageName(), userId)) {
registerServiceLocked(component, userId);
} else {
Slog.d(TAG, component + " no longer has permission to be bound");
@@ -833,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++) {
@@ -860,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");
@@ -932,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;
}
/**
@@ -1017,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) {
@@ -1039,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)) {
@@ -1056,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) {
@@ -1231,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);
@@ -1279,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 */
@@ -1304,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 03b7652..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;
@@ -345,7 +350,7 @@
private String mSoundNotificationKey;
private String mVibrateNotificationKey;
- private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
+ private final SparseArray<ArraySet<ComponentName>> mListenersDisablingEffects =
new SparseArray<>();
private List<ComponentName> mEffectsSuppressors = new ArrayList<>();
private int mListenerHints; // right now, all hints are global
@@ -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);
@@ -1784,10 +1789,10 @@
private ArrayList<ComponentName> getSuppressors() {
ArrayList<ComponentName> names = new ArrayList<ComponentName>();
for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
- ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
+ ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i);
- for (ManagedServiceInfo info : serviceInfoList) {
- names.add(info.component);
+ for (ComponentName info : serviceInfoList) {
+ names.add(info);
}
}
@@ -1803,11 +1808,10 @@
for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
final int hint = mListenersDisablingEffects.keyAt(i);
- final ArraySet<ManagedServiceInfo> listeners =
- mListenersDisablingEffects.valueAt(i);
+ final ArraySet<ComponentName> listeners = mListenersDisablingEffects.valueAt(i);
if (hints == 0 || (hint & hints) == hint) {
- removed = removed || listeners.remove(info);
+ removed |= listeners.remove(info.component);
}
}
@@ -1830,18 +1834,18 @@
private void addDisabledHint(ManagedServiceInfo info, int hint) {
if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
- mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
+ mListenersDisablingEffects.put(hint, new ArraySet<>());
}
- ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
- hintListeners.add(info);
+ ArraySet<ComponentName> hintListeners = mListenersDisablingEffects.get(hint);
+ hintListeners.add(info.component);
}
private int calculateHints() {
int hints = 0;
for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
int hint = mListenersDisablingEffects.keyAt(i);
- ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
+ ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i);
if (!serviceInfoList.isEmpty()) {
hints |= hint;
@@ -2955,6 +2959,21 @@
}
@Override
+ public void clearRequestedListenerHints(INotificationListener token) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationLock) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ removeDisabledHints(info);
+ updateListenerHintsLocked();
+ updateEffectsSuppressorLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void requestHintsFromListener(INotificationListener token, int hints) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -3860,11 +3879,12 @@
proto.write(
ListenersDisablingEffectsProto.HINT, mListenersDisablingEffects.keyAt(i));
- final ArraySet<ManagedServiceInfo> listeners =
+ final ArraySet<ComponentName> listeners =
mListenersDisablingEffects.valueAt(i);
for (int j = 0; j < listeners.size(); j++) {
- final ManagedServiceInfo listener = listeners.valueAt(i);
- listener.writeToProto(proto, ListenersDisablingEffectsProto.LISTENERS, null);
+ final ComponentName componentName = listeners.valueAt(i);
+ componentName.writeToProto(proto,
+ ListenersDisablingEffectsProto.LISTENER_COMPONENTS);
}
proto.end(effectsToken);
@@ -4003,15 +4023,14 @@
if (i > 0) pw.print(';');
pw.print("hint[" + hint + "]:");
- final ArraySet<ManagedServiceInfo> listeners =
- mListenersDisablingEffects.valueAt(i);
+ final ArraySet<ComponentName> listeners = mListenersDisablingEffects.valueAt(i);
final int listenerSize = listeners.size();
for (int j = 0; j < listenerSize; j++) {
if (i > 0) pw.print(',');
- final ManagedServiceInfo listener = listeners.valueAt(i);
+ final ComponentName listener = listeners.valueAt(i);
if (listener != null) {
- pw.print(listener.component);
+ pw.print(listener);
}
}
}
@@ -4070,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;
}
@@ -4099,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);
}
};
@@ -4381,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) {
@@ -4670,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();
@@ -6253,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();
@@ -6661,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) {
@@ -6786,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";
@@ -6876,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.
@@ -7310,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/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 593e7cd..13ff6e8 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -107,7 +107,7 @@
}
// pkg|uid => PackagePreferences
- private final ArrayMap<String, PackagePreferences> mPackagePreferencess = new ArrayMap<>();
+ private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
// pkg => PackagePreferences
private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
@@ -142,109 +142,117 @@
if (type != XmlPullParser.START_TAG) return;
String tag = parser.getName();
if (!TAG_RANKING.equals(tag)) return;
- // Clobber groups and channels with the xml, but don't delete other data that wasn't present
- // at the time of serialization.
- mRestoredWithoutUids.clear();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- tag = parser.getName();
- if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
- return;
- }
- if (type == XmlPullParser.START_TAG) {
- if (TAG_PACKAGE.equals(tag)) {
- int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
- String name = parser.getAttributeValue(null, ATT_NAME);
- if (!TextUtils.isEmpty(name)) {
- if (forRestore) {
- try {
- //TODO: http://b/22388012
- uid = mPm.getPackageUidAsUser(name,
- UserHandle.USER_SYSTEM);
- } catch (PackageManager.NameNotFoundException e) {
- // noop
- }
- }
+ synchronized (mPackagePreferences) {
+ // Clobber groups and channels with the xml, but don't delete other data that wasn't present
- PackagePreferences r = getOrCreatePackagePreferences(name, uid,
- XmlUtils.readIntAttribute(
- parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
- XmlUtils.readIntAttribute(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
- XmlUtils.readIntAttribute(
- parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
- XmlUtils.readBooleanAttribute(
- parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
- r.importance = XmlUtils.readIntAttribute(
- parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
- r.priority = XmlUtils.readIntAttribute(
- parser, ATT_PRIORITY, DEFAULT_PRIORITY);
- r.visibility = XmlUtils.readIntAttribute(
- parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
- r.showBadge = XmlUtils.readBooleanAttribute(
- parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
- r.lockedAppFields = XmlUtils.readIntAttribute(parser,
- ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
-
- final int innerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- // Channel groups
- if (TAG_GROUP.equals(tagName)) {
- String id = parser.getAttributeValue(null, ATT_ID);
- CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
- if (!TextUtils.isEmpty(id)) {
- NotificationChannelGroup group
- = new NotificationChannelGroup(id, groupName);
- group.populateFromXml(parser);
- r.groups.put(id, group);
+ // at the time of serialization.
+ mRestoredWithoutUids.clear();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ tag = parser.getName();
+ if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
+ return;
+ }
+ if (type == XmlPullParser.START_TAG) {
+ if (TAG_PACKAGE.equals(tag)) {
+ int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+ String name = parser.getAttributeValue(null, ATT_NAME);
+ if (!TextUtils.isEmpty(name)) {
+ if (forRestore) {
+ try {
+ //TODO: http://b/22388012
+ uid = mPm.getPackageUidAsUser(name,
+ UserHandle.USER_SYSTEM);
+ } catch (PackageManager.NameNotFoundException e) {
+ // noop
}
}
- // Channels
- if (TAG_CHANNEL.equals(tagName)) {
- String id = parser.getAttributeValue(null, ATT_ID);
- String channelName = parser.getAttributeValue(null, ATT_NAME);
- int channelImportance = XmlUtils.readIntAttribute(
- parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
- if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
- NotificationChannel channel = new NotificationChannel(id,
- channelName, channelImportance);
- if (forRestore) {
- channel.populateFromXmlForRestore(parser, mContext);
- } else {
- channel.populateFromXml(parser);
+
+ PackagePreferences r = getOrCreatePackagePreferences(name, uid,
+ XmlUtils.readIntAttribute(
+ parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
+ XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
+ DEFAULT_PRIORITY),
+ XmlUtils.readIntAttribute(
+ parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
+ XmlUtils.readBooleanAttribute(
+ parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
+ r.importance = XmlUtils.readIntAttribute(
+ parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+ r.priority = XmlUtils.readIntAttribute(
+ parser, ATT_PRIORITY, DEFAULT_PRIORITY);
+ r.visibility = XmlUtils.readIntAttribute(
+ parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+ r.showBadge = XmlUtils.readBooleanAttribute(
+ parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
+ r.lockedAppFields = XmlUtils.readIntAttribute(parser,
+ ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
+
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ // Channel groups
+ if (TAG_GROUP.equals(tagName)) {
+ String id = parser.getAttributeValue(null, ATT_ID);
+ CharSequence groupName = parser.getAttributeValue(null,
+ ATT_NAME);
+ if (!TextUtils.isEmpty(id)) {
+ NotificationChannelGroup group
+ = new NotificationChannelGroup(id, groupName);
+ group.populateFromXml(parser);
+ r.groups.put(id, group);
}
- r.channels.put(id, channel);
}
- }
- // Delegate
- if (TAG_DELEGATE.equals(tagName)) {
- int delegateId =
- XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
- String delegateName =
- XmlUtils.readStringAttribute(parser, ATT_NAME);
- boolean delegateEnabled = XmlUtils.readBooleanAttribute(
- parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
- boolean userAllowed = XmlUtils.readBooleanAttribute(
- parser, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED);
- Delegate d = null;
- if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(delegateName)) {
- d = new Delegate(
- delegateName, delegateId, delegateEnabled, userAllowed);
+ // Channels
+ if (TAG_CHANNEL.equals(tagName)) {
+ String id = parser.getAttributeValue(null, ATT_ID);
+ String channelName = parser.getAttributeValue(null, ATT_NAME);
+ int channelImportance = XmlUtils.readIntAttribute(
+ parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+ if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
+ NotificationChannel channel = new NotificationChannel(id,
+ channelName, channelImportance);
+ if (forRestore) {
+ channel.populateFromXmlForRestore(parser, mContext);
+ } else {
+ channel.populateFromXml(parser);
+ }
+ r.channels.put(id, channel);
+ }
}
- r.delegate = d;
+ // Delegate
+ if (TAG_DELEGATE.equals(tagName)) {
+ int delegateId =
+ XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+ String delegateName =
+ XmlUtils.readStringAttribute(parser, ATT_NAME);
+ boolean delegateEnabled = XmlUtils.readBooleanAttribute(
+ parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
+ boolean userAllowed = XmlUtils.readBooleanAttribute(
+ parser, ATT_USER_ALLOWED,
+ Delegate.DEFAULT_USER_ALLOWED);
+ Delegate d = null;
+ if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
+ delegateName)) {
+ d = new Delegate(
+ delegateName, delegateId, delegateEnabled,
+ userAllowed);
+ }
+ r.delegate = d;
+ }
+
}
- }
-
- try {
- deleteDefaultChannelIfNeeded(r);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
+ try {
+ deleteDefaultChannelIfNeeded(r);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
+ }
}
}
}
@@ -255,8 +263,8 @@
private PackagePreferences getPackagePreferences(String pkg, int uid) {
final String key = packagePreferencesKey(pkg, uid);
- synchronized (mPackagePreferencess) {
- return mPackagePreferencess.get(key);
+ synchronized (mPackagePreferences) {
+ return mPackagePreferences.get(key);
}
}
@@ -268,10 +276,10 @@
private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
int priority, int visibility, boolean showBadge) {
final String key = packagePreferencesKey(pkg, uid);
- synchronized (mPackagePreferencess) {
+ synchronized (mPackagePreferences) {
PackagePreferences
r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
- : mPackagePreferencess.get(key);
+ : mPackagePreferences.get(key);
if (r == null) {
r = new PackagePreferences();
r.pkg = pkg;
@@ -290,7 +298,7 @@
if (r.uid == UNKNOWN_UID) {
mRestoredWithoutUids.put(pkg, r);
} else {
- mPackagePreferencess.put(key, r);
+ mPackagePreferences.put(key, r);
}
}
return r;
@@ -364,10 +372,10 @@
out.startTag(null, TAG_RANKING);
out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
- synchronized (mPackagePreferencess) {
- final int N = mPackagePreferencess.size();
+ synchronized (mPackagePreferences) {
+ final int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
- final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
//TODO: http://b/22388012
if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
continue;
@@ -882,10 +890,10 @@
public int getBlockedAppCount(int userId) {
int count = 0;
- synchronized (mPackagePreferencess) {
- final int N = mPackagePreferencess.size();
+ synchronized (mPackagePreferences) {
+ final int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
- final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
if (userId == UserHandle.getUserId(r.uid)
&& r.importance == IMPORTANCE_NONE) {
count++;
@@ -896,11 +904,11 @@
}
public void updateChannelsBypassingDnd() {
- synchronized (mPackagePreferencess) {
- final int numPackagePreferencess = mPackagePreferencess.size();
+ synchronized (mPackagePreferences) {
+ final int numPackagePreferencess = mPackagePreferences.size();
for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess;
PackagePreferencesIndex++) {
- final PackagePreferences r = mPackagePreferencess.valueAt(PackagePreferencesIndex);
+ final PackagePreferences r = mPackagePreferences.valueAt(PackagePreferencesIndex);
final int numChannels = r.channels.size();
for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
@@ -1064,8 +1072,8 @@
pw.println("per-package config:");
pw.println("PackagePreferencess:");
- synchronized (mPackagePreferencess) {
- dumpPackagePreferencess(pw, prefix, filter, mPackagePreferencess);
+ synchronized (mPackagePreferences) {
+ dumpPackagePreferencess(pw, prefix, filter, mPackagePreferences);
}
pw.println("Restored without uid:");
dumpPackagePreferencess(pw, prefix, filter, mRestoredWithoutUids);
@@ -1073,9 +1081,9 @@
public void dump(ProtoOutputStream proto,
@NonNull NotificationManagerService.DumpFilter filter) {
- synchronized (mPackagePreferencess) {
+ synchronized (mPackagePreferences) {
dumpPackagePreferencess(proto, RankingHelperProto.RECORDS, filter,
- mPackagePreferencess);
+ mPackagePreferences);
}
dumpPackagePreferencess(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
mRestoredWithoutUids);
@@ -1160,10 +1168,10 @@
} catch (JSONException e) {
// pass
}
- synchronized (mPackagePreferencess) {
- final int N = mPackagePreferencess.size();
+ synchronized (mPackagePreferences) {
+ final int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
- final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
if (filter == null || filter.matches(r.pkg)) {
JSONObject PackagePreferences = new JSONObject();
try {
@@ -1240,11 +1248,11 @@
}
public Map<Integer, String> getPackageBans() {
- synchronized (mPackagePreferencess) {
- final int N = mPackagePreferencess.size();
+ synchronized (mPackagePreferences) {
+ final int N = mPackagePreferences.size();
ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
- final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
if (r.importance == IMPORTANCE_NONE) {
packageBans.put(r.uid, r.pkg);
}
@@ -1284,9 +1292,9 @@
private Map<String, Integer> getPackageChannels() {
ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
- synchronized (mPackagePreferencess) {
- for (int i = 0; i < mPackagePreferencess.size(); i++) {
- final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ synchronized (mPackagePreferences) {
+ for (int i = 0; i < mPackagePreferences.size(); i++) {
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
int channelCount = 0;
for (int j = 0; j < r.channels.size(); j++) {
if (!r.channels.valueAt(j).isDeleted()) {
@@ -1300,22 +1308,22 @@
}
public void onUserRemoved(int userId) {
- synchronized (mPackagePreferencess) {
- int N = mPackagePreferencess.size();
+ synchronized (mPackagePreferences) {
+ int N = mPackagePreferences.size();
for (int i = N - 1; i >= 0; i--) {
- PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i);
+ PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
- mPackagePreferencess.removeAt(i);
+ mPackagePreferences.removeAt(i);
}
}
}
}
protected void onLocaleChanged(Context context, int userId) {
- synchronized (mPackagePreferencess) {
- int N = mPackagePreferencess.size();
+ synchronized (mPackagePreferences) {
+ int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
- PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i);
+ PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
if (PackagePreferences.channels.containsKey(
NotificationChannel.DEFAULT_CHANNEL_ID)) {
@@ -1341,8 +1349,8 @@
for (int i = 0; i < size; i++) {
final String pkg = pkgList[i];
final int uid = uidList[i];
- synchronized (mPackagePreferencess) {
- mPackagePreferencess.remove(packagePreferencesKey(pkg, uid));
+ synchronized (mPackagePreferences) {
+ mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
}
mRestoredWithoutUids.remove(pkg);
updated = true;
@@ -1355,8 +1363,8 @@
try {
r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
mRestoredWithoutUids.remove(pkg);
- synchronized (mPackagePreferencess) {
- mPackagePreferencess.put(packagePreferencesKey(r.pkg, r.uid), r);
+ synchronized (mPackagePreferences) {
+ mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
}
updated = true;
} catch (PackageManager.NameNotFoundException e) {
@@ -1365,11 +1373,13 @@
}
// Package upgrade
try {
- PackagePreferences fullPackagePreferences = getPackagePreferences(pkg,
- mPm.getPackageUidAsUser(pkg, changeUserId));
- if (fullPackagePreferences != null) {
- createDefaultChannelIfNeeded(fullPackagePreferences);
- deleteDefaultChannelIfNeeded(fullPackagePreferences);
+ synchronized (mPackagePreferences) {
+ PackagePreferences fullPackagePreferences = getPackagePreferences(pkg,
+ mPm.getPackageUidAsUser(pkg, changeUserId));
+ if (fullPackagePreferences != null) {
+ createDefaultChannelIfNeeded(fullPackagePreferences);
+ deleteDefaultChannelIfNeeded(fullPackagePreferences);
+ }
}
} catch (PackageManager.NameNotFoundException e) {
}
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 6ac72d3..4dd2bf2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -501,9 +501,14 @@
}
protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
- return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
- NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
- rule.creationTime);
+ if (rule.zenPolicy != null) {
+ return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, rule.zenPolicy,
+ rule.enabled, rule.creationTime);
+ } else {
+ return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
+ NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
+ rule.creationTime);
+ }
}
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
@@ -667,6 +672,9 @@
}
}
+ /**
+ * @return user-specified default notification policy for priority only do not disturb
+ */
public Policy getNotificationPolicy() {
return getNotificationPolicy(mConfig);
}
@@ -675,6 +683,9 @@
return config == null ? null : config.toNotificationPolicy();
}
+ /**
+ * Sets the global notification policy used for priority only do not disturb
+ */
public void setNotificationPolicy(Policy policy) {
if (policy == null || mConfig == null) return;
synchronized (mConfig) {
@@ -770,9 +781,7 @@
ComponentName triggeringComponent, boolean setRingerMode) {
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- if (!evaluateZenMode(reason, setRingerMode)) {
- applyRestrictions(); // evaluateZenMode will also apply restrictions if changed
- }
+ evaluateZenMode(reason, setRingerMode);
mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
}
@@ -798,7 +807,7 @@
}
@VisibleForTesting
- protected boolean evaluateZenMode(String reason, boolean setRingerMode) {
+ protected void evaluateZenMode(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
final int zenBefore = mZenMode;
final int zen = computeZenMode();
@@ -813,7 +822,6 @@
if (zen != zenBefore) {
mHandler.postDispatchOnZenModeChanged();
}
- return true;
}
private void updateRingerModeAffectedStreams() {
@@ -822,7 +830,9 @@
}
}
+
private int computeZenMode() {
+ // TODO: use mConfig.zenPolicy
if (mConfig == null) return Global.ZEN_MODE_OFF;
synchronized (mConfig) {
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index c738701..f1b03d1 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -558,8 +558,8 @@
@Override
public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable,
int userId) throws RemoteException {
- enforceChangeOverlayPackagesPermission("setEnabled");
- userId = handleIncomingUser(userId, "setEnabled");
+ enforceChangeOverlayPackagesPermission("setEnabledExclusive");
+ userId = handleIncomingUser(userId, "setEnabledExclusive");
if (packageName == null || !enable) {
return false;
}
@@ -578,8 +578,8 @@
@Override
public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId)
throws RemoteException {
- enforceChangeOverlayPackagesPermission("setEnabled");
- userId = handleIncomingUser(userId, "setEnabled");
+ enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory");
+ userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory");
if (packageName == null) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 7d762d9..5bf323a 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 != 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/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d305032..8f2833f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -59,6 +59,7 @@
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.dex.DexMetadataHelper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
@@ -99,7 +100,6 @@
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
-import android.content.pm.dex.DexMetadataHelper;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -122,7 +122,6 @@
private static final boolean LOGD = true;
private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
- private static final int MSG_EARLY_BIND = 0;
private static final int MSG_COMMIT = 1;
private static final int MSG_ON_PACKAGE_INSTALLED = 2;
@@ -168,7 +167,6 @@
final int userId;
final SessionParams params;
final long createdMillis;
- final int defaultContainerGid;
/** Staging location where client data is written. */
final File stageDir;
@@ -285,9 +283,6 @@
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
- case MSG_EARLY_BIND:
- earlyBindToDefContainer();
- break;
case MSG_COMMIT:
synchronized (mLock) {
try {
@@ -323,10 +318,6 @@
}
};
- private void earlyBindToDefContainer() {
- mPm.earlyBindToDefContainer();
- }
-
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
@@ -413,19 +404,6 @@
}
}
}
-
- final long identity = Binder.clearCallingIdentity();
- try {
- final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
- defaultContainerGid = UserHandle.getSharedAppGid(uid);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- // attempt to bind to the DefContainer as early as possible
- if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_EARLY_BIND));
- }
}
public SessionInfo generateInfo() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cacdccb..91af0ec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -88,8 +88,6 @@
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
-import static android.system.OsConstants.O_CREAT;
-import static android.system.OsConstants.O_RDWR;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
@@ -113,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;
@@ -137,7 +136,6 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
-import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AppsQueryHelper;
@@ -207,14 +205,12 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
-import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
import android.os.PatternMatcher;
import android.os.PersistableBundle;
import android.os.Process;
@@ -273,12 +269,10 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
import com.android.internal.telephony.CarrierAppUtils;
@@ -310,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;
@@ -551,12 +546,6 @@
public static final String PLATFORM_PACKAGE_NAME = "android";
- public static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
-
- public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
- DEFAULT_CONTAINER_PACKAGE,
- "com.android.defcontainer.DefaultContainerService");
-
private static final String KILL_APP_REASON_GIDS_CHANGED =
"permission grant or revoke changed gids";
@@ -729,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);
@@ -1238,18 +1229,9 @@
}
final PendingPackageBroadcasts mPendingBroadcasts = new PendingPackageBroadcasts();
- // Service Connection to remote media container service to copy
- // package uri's from external media onto secure containers
- // or internal storage.
- private IMediaContainerService mContainerService = null;
-
static final int SEND_PENDING_BROADCAST = 1;
- static final int MCS_BOUND = 3;
static final int INIT_COPY = 5;
- static final int MCS_UNBIND = 6;
static final int POST_INSTALL = 9;
- static final int MCS_RECONNECT = 10;
- static final int MCS_GIVE_UP = 11;
static final int WRITE_SETTINGS = 13;
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
@@ -1258,7 +1240,6 @@
static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_PACKAGE_LIST = 19;
static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
- static final int DEF_CONTAINER_BIND = 21;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -1273,21 +1254,6 @@
// Stores a list of users whose package restrictions file needs to be updated
private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
- final private DefaultContainerConnection mDefContainerConn =
- new DefaultContainerConnection();
- class DefaultContainerConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
- final IMediaContainerService imcs = IMediaContainerService.Stub
- .asInterface(Binder.allowBlocking(service));
- mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
- }
-
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
- }
- }
-
// Recordkeeping of restore-after-install operations that are currently in flight
// between the Package Manager and the Backup Manager
static class PostInstallData {
@@ -1346,31 +1312,6 @@
private final CompilerStats mCompilerStats = new CompilerStats();
class PackageHandler extends Handler {
- private boolean mBound = false;
- final ArrayList<HandlerParams> mPendingInstalls =
- new ArrayList<>();
-
- private boolean connectToService() {
- if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService");
- Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- if (mContext.bindServiceAsUser(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- mBound = true;
- return true;
- }
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- return false;
- }
-
- private void disconnectService() {
- mContainerService = null;
- mBound = false;
- Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- mContext.unbindService(mDefContainerConn);
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- }
PackageHandler(Looper looper) {
super(looper);
@@ -1386,167 +1327,18 @@
void doHandleMessage(Message msg) {
switch (msg.what) {
- case DEF_CONTAINER_BIND:
- if (!mBound) {
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "earlyBindingMCS",
- System.identityHashCode(mHandler));
- if (!connectToService()) {
- Slog.e(TAG, "Failed to bind to media container service");
- }
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "earlyBindingMCS",
- System.identityHashCode(mHandler));
- }
- break;
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
- int idx = mPendingInstalls.size();
- if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
- // If a bind was already initiated we dont really
- // need to do anything. The pending install
- // will be processed later on.
- if (!mBound) {
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
- System.identityHashCode(mHandler));
- // If this is the only one pending we might
- // have to bind to the service again.
- if (!connectToService()) {
- Slog.e(TAG, "Failed to bind to media container service");
- params.serviceError();
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
- System.identityHashCode(mHandler));
- if (params.traceMethod != null) {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
- params.traceCookie);
- }
- return;
- } else {
- // Once we bind to the service, the first
- // pending request will be processed.
- mPendingInstalls.add(idx, params);
- }
- } else {
- mPendingInstalls.add(idx, params);
- // Already bound to the service. Just make
- // sure we trigger off processing the first request.
- if (idx == 0) {
- mHandler.sendEmptyMessage(MCS_BOUND);
- }
+ if (params != null) {
+ if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(params));
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
+ params.startCopy();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
break;
}
- case MCS_BOUND: {
- if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
- if (msg.obj != null) {
- mContainerService = (IMediaContainerService) msg.obj;
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
- System.identityHashCode(mHandler));
- }
- if (mContainerService == null) {
- if (!mBound) {
- // Something seriously wrong since we are not bound and we are not
- // waiting for connection. Bail out.
- Slog.e(TAG, "Cannot bind to media container service");
- for (HandlerParams params : mPendingInstalls) {
- // Indicate service bind error
- params.serviceError();
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(params));
- if (params.traceMethod != null) {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
- params.traceMethod, params.traceCookie);
- }
- }
- mPendingInstalls.clear();
- } else {
- Slog.w(TAG, "Waiting to connect to media container service");
- }
- } else if (mPendingInstalls.size() > 0) {
- HandlerParams params = mPendingInstalls.get(0);
- if (params != null) {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(params));
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
- if (params.startCopy()) {
- // We are done... look for more work or to
- // go idle.
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Checking for more work or unbind...");
- // Delete pending install
- if (mPendingInstalls.size() > 0) {
- mPendingInstalls.remove(0);
- }
- if (mPendingInstalls.size() == 0) {
- if (mBound) {
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Posting delayed MCS_UNBIND");
- removeMessages(MCS_UNBIND);
- Message ubmsg = obtainMessage(MCS_UNBIND);
- // Unbind after a little delay, to avoid
- // continual thrashing.
- sendMessageDelayed(ubmsg, 10000);
- }
- } else {
- // There are more pending requests in queue.
- // Just post MCS_BOUND message to trigger processing
- // of next pending install.
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Posting MCS_BOUND for next work");
- mHandler.sendEmptyMessage(MCS_BOUND);
- }
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- } else {
- // Should never happen ideally.
- Slog.w(TAG, "Empty queue");
- }
- break;
- }
- case MCS_RECONNECT: {
- if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
- if (mPendingInstalls.size() > 0) {
- if (mBound) {
- disconnectService();
- }
- if (!connectToService()) {
- Slog.e(TAG, "Failed to bind to media container service");
- for (HandlerParams params : mPendingInstalls) {
- // Indicate service bind error
- params.serviceError();
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(params));
- }
- mPendingInstalls.clear();
- }
- }
- break;
- }
- case MCS_UNBIND: {
- // If there is no actual work left, then time to unbind.
- if (DEBUG_INSTALL) Slog.i(TAG, "mcs_unbind");
-
- if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {
- if (mBound) {
- if (DEBUG_INSTALL) Slog.i(TAG, "calling disconnectService()");
-
- disconnectService();
- }
- } else if (mPendingInstalls.size() > 0) {
- // There are more pending requests in queue.
- // Just post MCS_BOUND message to trigger processing
- // of next pending install.
- mHandler.sendEmptyMessage(MCS_BOUND);
- }
-
- break;
- }
- case MCS_GIVE_UP: {
- if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
- HandlerParams params = mPendingInstalls.remove(0);
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(params));
- break;
- }
case SEND_PENDING_BROADCAST: {
String packages[];
ArrayList<String> components[];
@@ -1685,11 +1477,7 @@
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, user);
- try {
- ret = args.copyApk(mContainerService, true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not contact the ContainerService");
- }
+ ret = args.copyApk();
} else {
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_REJECT, user);
@@ -1699,7 +1487,6 @@
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
processPendingInstall(args, ret);
- mHandler.sendEmptyMessage(MCS_UNBIND);
}
break;
}
@@ -1724,14 +1511,9 @@
int ret;
if (state.isInstallAllowed()) {
- ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
broadcastPackageVerified(verificationId, originUri,
response.code, state.getInstallArgs().getUser());
- try {
- ret = args.copyApk(mContainerService, true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not contact the ContainerService");
- }
+ ret = args.copyApk();
} else {
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
}
@@ -1740,7 +1522,6 @@
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
processPendingInstall(args, ret);
- mHandler.sendEmptyMessage(MCS_UNBIND);
}
break;
@@ -8805,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);
+ }
}
}
}
@@ -10028,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.
*/
@@ -10037,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;
}
}
@@ -10118,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;
}
@@ -10267,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);
}
@@ -10578,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;
@@ -10622,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,
@@ -10654,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;
}
@@ -10838,7 +10649,8 @@
pkgSetting.volumeUuid = volumeUuid;
}
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath);
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */);
}
/**
@@ -11053,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.");
@@ -11062,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");
}
@@ -12151,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;
@@ -12159,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);
}
}
}
@@ -12430,14 +12244,6 @@
return installReason;
}
- /**
- * Attempts to bind to the default container service explicitly instead of doing so lazily on
- * install commit.
- */
- void earlyBindToDefContainer() {
- mHandler.sendMessage(mHandler.obtainMessage(DEF_CONTAINER_BIND));
- }
-
void installStage(String packageName, File stagedDir,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
@@ -13784,14 +13590,6 @@
}
private abstract class HandlerParams {
- private static final int MAX_RETRIES = 4;
-
- /**
- * Number of times startCopy() has been attempted and had a non-fatal
- * error.
- */
- private int mRetries = 0;
-
/** User handle for the user requesting the information or installation. */
private final UserHandle mUser;
String traceMethod;
@@ -13815,37 +13613,13 @@
return this;
}
- final boolean startCopy() {
- boolean res;
- try {
- if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
-
- if (++mRetries > MAX_RETRIES) {
- Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
- mHandler.sendEmptyMessage(MCS_GIVE_UP);
- handleServiceError();
- return false;
- } else {
- handleStartCopy();
- res = true;
- }
- } catch (RemoteException e) {
- if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
- mHandler.sendEmptyMessage(MCS_RECONNECT);
- res = false;
- }
- handleReturnCode();
- return res;
- }
-
- final void serviceError() {
- if (DEBUG_INSTALL) Slog.i(TAG, "serviceError");
- handleServiceError();
+ final void startCopy() {
+ if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
+ handleStartCopy();
handleReturnCode();
}
- abstract void handleStartCopy() throws RemoteException;
- abstract void handleServiceError();
+ abstract void handleStartCopy();
abstract void handleReturnCode();
}
@@ -14088,7 +13862,7 @@
* policy if needed and then create install arguments based
* on the install location.
*/
- public void handleStartCopy() throws RemoteException {
+ public void handleStartCopy() {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
@@ -14114,8 +13888,8 @@
Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
- pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
- packageAbiOverride);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
if (DEBUG_INSTANT && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
@@ -14132,15 +13906,16 @@
final long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());
- final long sizeBytes = mContainerService.calculateInstalledSize(
+ final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
origin.resolvedPath, packageAbiOverride);
-
- try {
- mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
- pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
- installFlags, packageAbiOverride);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
+ if (sizeBytes >= 0) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
}
/*
@@ -14351,7 +14126,7 @@
* No package verification is enabled, so immediately start
* the remote call to initiate copy using temporary file.
*/
- ret = args.copyApk(mContainerService, true);
+ ret = args.copyApk();
}
}
@@ -14360,19 +14135,10 @@
@Override
void handleReturnCode() {
- // If mArgs is null, then MCS couldn't be reached. When it
- // reconnects, it will try again to install. At that point, this
- // will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
-
- @Override
- void handleServiceError() {
- mArgs = createInstallArgs(this);
- mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- }
}
private InstallArgs createInstallArgs(InstallParams params) {
@@ -14439,7 +14205,7 @@
this.installReason = installReason;
}
- abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
+ abstract int copyApk();
abstract int doPreInstall(int status);
/**
@@ -14547,16 +14313,16 @@
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
- int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
+ int copyApk() {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
- return doCopyApk(imcs, temp);
+ return doCopyApk();
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
+ private int doCopyApk() {
if (origin.staged) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
@@ -14575,25 +14341,8 @@
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
- final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
- @Override
- public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
- if (!FileUtils.isValidExtFilename(name)) {
- throw new IllegalArgumentException("Invalid filename: " + name);
- }
- try {
- final File file = new File(codeFile, name);
- final FileDescriptor fd = Os.open(file.getAbsolutePath(),
- O_RDWR | O_CREAT, 0644);
- Os.chmod(file.getAbsolutePath(), 0644);
- return new ParcelFileDescriptor(fd);
- } catch (ErrnoException e) {
- throw new RemoteException("Failed to open: " + e.getMessage());
- }
- }
- };
-
- int ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
+ int ret = PackageManagerServiceUtils.copyPackage(
+ origin.file.getAbsolutePath(), codeFile);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
@@ -14754,7 +14503,7 @@
params.installReason);
}
- int copyApk(IMediaContainerService imcs, boolean temp) {
+ int copyApk() {
if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
+ move.fromUuid + " to " + move.toUuid);
synchronized (mInstaller) {
@@ -14941,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)) {
@@ -15010,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
@@ -15682,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;
@@ -15780,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
@@ -15799,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
@@ -15819,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);
@@ -15830,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);
}
@@ -15841,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;
}
}
@@ -15861,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;
}
}
@@ -15902,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");
}
}
@@ -15917,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) {
@@ -15947,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.
@@ -15958,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) {
@@ -15972,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;
}
}
}
@@ -16018,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 {
@@ -16038,8 +15829,7 @@
}
}
} catch (PackageManagerException e) {
- res.setError(e.error, e.getMessage());
- return;
+ throw new PrepareFailure(e.error, e.getMessage());
}
}
@@ -16050,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);
@@ -16076,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 {
@@ -16084,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
@@ -16104,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 "
@@ -16138,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;
}
}
@@ -16173,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.
@@ -16193,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()) {
@@ -16208,7 +15997,6 @@
apkPath = pkg.baseCodePath;
}
}
-
if (apkPath != null) {
final VerityUtils.SetupResult result =
VerityUtils.generateApkVeritySetupData(apkPath);
@@ -16220,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.
@@ -16244,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();
}
}
}
@@ -17156,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;
@@ -22267,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) {
@@ -22378,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;
@@ -22386,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:
@@ -22423,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);
}
@@ -22448,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/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 390c0cc..36948fc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -18,23 +18,21 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDWR;
+
import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-
-import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.util.FastPrintWriter;
-import com.android.server.EventLogTags;
-import com.android.server.pm.dex.DexManager;
-import com.android.server.pm.dex.PackageDexUsage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
@@ -53,18 +51,24 @@
import android.system.Os;
import android.util.ArraySet;
import android.util.Log;
-import android.util.PackageUtils;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
+import com.android.server.EventLogTags;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.PackageDexUsage;
+
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
-import libcore.io.Streams;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
@@ -73,8 +77,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
@@ -710,4 +712,120 @@
final File[] compressedFiles = getCompressedFiles(codePath);
return compressedFiles != null && compressedFiles.length > 0;
}
+
+ /**
+ * Parse given package and return minimal details.
+ */
+ public static PackageInfoLite getMinimalPackageInfo(Context context, String packagePath,
+ int flags, String abiOverride) {
+ final PackageInfoLite ret = new PackageInfoLite();
+ if (packagePath == null) {
+ Slog.i(TAG, "Invalid package file " + packagePath);
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ return ret;
+ }
+
+ final File packageFile = new File(packagePath);
+ final PackageParser.PackageLite pkg;
+ final long sizeBytes;
+ try {
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
+ sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
+ } catch (PackageParserException | IOException e) {
+ Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
+
+ if (!packageFile.exists()) {
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
+ } else {
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ }
+
+ return ret;
+ }
+
+ final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
+ pkg.packageName, pkg.installLocation, sizeBytes, flags);
+
+ ret.packageName = pkg.packageName;
+ ret.splitNames = pkg.splitNames;
+ ret.versionCode = pkg.versionCode;
+ ret.versionCodeMajor = pkg.versionCodeMajor;
+ ret.baseRevisionCode = pkg.baseRevisionCode;
+ ret.splitRevisionCodes = pkg.splitRevisionCodes;
+ ret.installLocation = pkg.installLocation;
+ ret.verifiers = pkg.verifiers;
+ ret.recommendedInstallLocation = recommendedInstallLocation;
+ ret.multiArch = pkg.multiArch;
+
+ return ret;
+ }
+
+ /**
+ * Calculate estimated footprint of given package post-installation.
+ *
+ * @return -1 if there's some error calculating the size, otherwise installed size of the
+ * package.
+ */
+ public static long calculateInstalledSize(String packagePath, String abiOverride) {
+ final File packageFile = new File(packagePath);
+ final PackageParser.PackageLite pkg;
+ try {
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
+ return PackageHelper.calculateInstalledSize(pkg, abiOverride);
+ } catch (PackageParserException | IOException e) {
+ Slog.w(TAG, "Failed to calculate installed size: " + e);
+ return -1;
+ }
+ }
+
+ /**
+ * Copy package to the target location.
+ *
+ * @param packagePath absolute path to the package to be copied. Can be
+ * a single monolithic APK file or a cluster directory
+ * containing one or more APKs.
+ * @return returns status code according to those in
+ * {@link PackageManager}
+ */
+ public static int copyPackage(String packagePath, File targetDir) {
+ if (packagePath == null) {
+ return PackageManager.INSTALL_FAILED_INVALID_URI;
+ }
+
+ try {
+ final File packageFile = new File(packagePath);
+ final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
+ copyFile(pkg.baseCodePath, targetDir, "base.apk");
+ if (!ArrayUtils.isEmpty(pkg.splitNames)) {
+ for (int i = 0; i < pkg.splitNames.length; i++) {
+ copyFile(pkg.splitCodePaths[i], targetDir,
+ "split_" + pkg.splitNames[i] + ".apk");
+ }
+ }
+ return PackageManager.INSTALL_SUCCEEDED;
+ } catch (PackageParserException | IOException | ErrnoException e) {
+ Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
+
+ private static void copyFile(String sourcePath, File targetDir, String targetName)
+ throws ErrnoException, IOException {
+ if (!FileUtils.isValidExtFilename(targetName)) {
+ throw new IllegalArgumentException("Invalid filename: " + targetName);
+ }
+ Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
+
+ final File targetFile = new File(targetDir, targetName);
+ final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
+ O_RDWR | O_CREAT, 0644);
+ Os.chmod(targetFile.getAbsolutePath(), 0644);
+ FileInputStream source = null;
+ try {
+ source = new FileInputStream(sourcePath);
+ FileUtils.copy(source.getFD(), targetFd);
+ } finally {
+ IoUtils.closeQuietly(source);
+ }
+ }
}
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 5befc1f..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,9 +63,9 @@
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;
-import com.android.server.pm.PackageManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,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;
@@ -95,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";
@@ -109,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);
@@ -220,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,
@@ -292,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;
@@ -308,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);
+ }
}
}
@@ -380,601 +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);
- }
-
- // Container service
- PackageParser.Package containerPackage = getSystemPackage(
- PackageManagerService.DEFAULT_CONTAINER_PACKAGE);
- if (containerPackage != null) {
- grantRuntimePermissions(containerPackage, 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);
}
}
@@ -984,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);
}
}
@@ -1000,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);
}
}
@@ -1019,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(
@@ -1037,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) {
@@ -1064,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.
@@ -1189,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) {
@@ -1205,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;
@@ -1235,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
@@ -1250,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.
@@ -1296,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;
@@ -1315,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);
@@ -1324,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) {
@@ -1365,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++) {
@@ -1376,8 +1186,7 @@
permissions.clear();
}
permissions.add(permissionGrant.name);
- grantRuntimePermissions(pkg, permissions,
- permissionGrant.fixed, userId);
+ grantRuntimePermissions(pkg, permissions, permissionGrant.fixed, userId);
}
}
}
@@ -1482,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);
@@ -1535,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 3e0429f..3b19beb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3543,8 +3543,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);
@@ -4732,8 +4730,8 @@
}
final WindowManager.LayoutParams attrs = win.getAttrs();
final boolean isDefaultDisplay = win.isDefaultDisplay();
- final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
- (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
+ final boolean needsToOffsetInputMethodTarget =
+ (win == mLastInputMethodTargetWindow) && (mLastInputMethodWindow != null);
if (needsToOffsetInputMethodTarget) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
@@ -6037,6 +6035,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/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 6f9d803..5e3fe0a 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(1000); // uid collection not implemented yet
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..c2d8188 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -566,6 +566,7 @@
@Override
public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.showBiometricDialog(bundle, receiver, type);
@@ -576,6 +577,7 @@
@Override
public void onBiometricAuthenticated() {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.onBiometricAuthenticated();
@@ -586,6 +588,7 @@
@Override
public void onBiometricHelp(String message) {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.onBiometricHelp(message);
@@ -596,6 +599,7 @@
@Override
public void onBiometricError(String error) {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.onBiometricError(error);
@@ -606,6 +610,7 @@
@Override
public void hideBiometricDialog() {
+ enforceBiometricDialog();
if (mBar != null) {
try {
mBar.hideBiometricDialog();
@@ -866,6 +871,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 99%
rename from services/core/java/com/android/server/TextServicesManagerService.java
rename to services/core/java/com/android/server/textservices/TextServicesManagerService.java
index 708350d..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.LocaleUtils;
+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;
@@ -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 = LocaleUtils.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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d3e534c..6c7304d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -409,6 +409,14 @@
private InputMonitor mInputMonitor;
+ /** Caches the value whether told display manager that we have content. */
+ private boolean mLastHasContent;
+
+ /**
+ * The input method window for this display.
+ */
+ WindowState mInputMethodWindow;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final AppWindowToken atoken = w.mAppToken;
@@ -2107,18 +2115,16 @@
mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
}
}
- final WindowState inputMethod = mService.mInputMethodWindow;
- if (inputMethod != null && inputMethod.isVisibleLw()) {
+ if (mInputMethodWindow != null && mInputMethodWindow.isVisibleLw()) {
// If the input method is visible and the user is typing, we don't want these touch
// events to be intercepted and used to change focus. This would likely cause a
// disappearance of the input method.
- inputMethod.getTouchableRegion(mTmpRegion);
- if (inputMethod.getDisplayId() == mDisplayId) {
+ mInputMethodWindow.getTouchableRegion(mTmpRegion);
+ if (mInputMethodWindow.getDisplayId() == mDisplayId) {
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
} else {
// IME is on a different display, so we need to update its tap detector.
- // TODO(multidisplay): Remove when IME will always appear on same display.
- inputMethod.getDisplayContent().setTouchExcludeRegion(null /* focusedTask */);
+ setTouchExcludeRegion(null /* focusedTask */);
}
}
for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
@@ -2257,7 +2263,7 @@
}
void adjustForImeIfNeeded() {
- final WindowState imeWin = mService.mInputMethodWindow;
+ final WindowState imeWin = mInputMethodWindow;
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
&& !mDividerControllerLocked.isImeHideRequested();
final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
@@ -2640,12 +2646,21 @@
}
/**
+ * Set input method window for the display.
+ * @param win Set when window added or Null when destroyed.
+ */
+ void setInputMethodWindowLocked(WindowState win) {
+ mInputMethodWindow = win;
+ computeImeTarget(true /* updateImeTarget */);
+ }
+
+ /**
* Determine and return the window that should be the IME target.
* @param updateImeTarget If true the system IME target will be updated to match what we found.
* @return The window that should be used as the IME target or null if there isn't any.
*/
WindowState computeImeTarget(boolean updateImeTarget) {
- if (mService.mInputMethodWindow == null) {
+ if (mInputMethodWindow == null) {
// There isn't an IME so there shouldn't be a target...That was easy!
if (updateImeTarget) {
if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from "
@@ -3080,8 +3095,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 */);
@@ -3594,6 +3610,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();
}
@@ -4243,4 +4271,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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 86b14337..d92818a 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -681,10 +681,11 @@
i--;
WindowState win = mService.mDestroySurface.get(i);
win.mDestroying = false;
- if (mService.mInputMethodWindow == win) {
- mService.setInputMethodWindowLocked(null);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent.mInputMethodWindow == win) {
+ displayContent.setInputMethodWindowLocked(null);
}
- if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
+ if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
wallpaperDestroyed = true;
}
win.destroySurfaceUnchecked();
@@ -1113,4 +1114,18 @@
callback.accept(mChildren.get(i));
}
}
+
+ /**
+ * Get current topmost focused IME window in system.
+ * Will look on all displays in current Z-order.
+ */
+ WindowState getCurrentInputMethodWindow() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mChildren.get(i);
+ if (displayContent.mInputMethodWindow != null) {
+ return displayContent.mInputMethodWindow;
+ }
+ }
+ return null;
+ }
}
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/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 9381fc6..cbe7d9d 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -286,8 +286,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);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index ac496a8..793ce60 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -335,9 +335,9 @@
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
- * Retrieves a height of input method window.
+ * Retrieves a height of input method window for given display.
*/
- public abstract int getInputMethodWindowVisibleHeight();
+ public abstract int getInputMethodWindowVisibleHeight(int displayId);
/**
* Saves last input method window for transition.
@@ -447,4 +447,9 @@
* Returns {@code true} if a process that is identified by {@code client} has IME focus.
*/
public abstract boolean inputMethodClientHasFocus(IInputMethodClient client);
+
+ /**
+ * Return the display Id for given window.
+ */
+ public abstract int getDisplayIdForWindow(IBinder windowToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 679e0d8..ee128c7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -650,8 +650,6 @@
/** If true hold off on modifying the animation layer of mInputMethodTarget */
boolean mInputMethodTargetWaitingAnim;
- WindowState mInputMethodWindow = null;
-
boolean mHardKeyboardAvailable;
WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
SettingsObserver mSettingsObserver;
@@ -1414,7 +1412,7 @@
win.mToken.addWindow(win);
if (type == TYPE_INPUT_METHOD) {
win.mGivenInsetsPending = true;
- setInputMethodWindowLocked(win);
+ displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
displayContent.computeImeTarget(true /* updateImeTarget */);
@@ -1687,8 +1685,9 @@
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Final remove of window: " + win);
- if (mInputMethodWindow == win) {
- setInputMethodWindowLocked(null);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent.mInputMethodWindow == win) {
+ displayContent.setInputMethodWindowLocked(null);
}
final WindowToken token = win.mToken;
@@ -1733,13 +1732,6 @@
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
- void setInputMethodWindowLocked(WindowState win) {
- mInputMethodWindow = win;
- final DisplayContent dc = win != null
- ? win.getDisplayContent() : getDefaultDisplayContentLocked();
- dc.computeImeTarget(true /* updateImeTarget */);
- }
-
private void updateHiddenWhileSuspendedState(ArraySet<String> packages, boolean suspended) {
synchronized (mWindowMap) {
mRoot.updateHiddenWhileSuspendedState(packages, suspended);
@@ -2058,8 +2050,10 @@
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = isDefaultDisplay;
}
- if (win.mAttrs.type == TYPE_INPUT_METHOD && mInputMethodWindow == null) {
- setInputMethodWindowLocked(win);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (win.mAttrs.type == TYPE_INPUT_METHOD
+ && displayContent.mInputMethodWindow == null) {
+ displayContent.setInputMethodWindowLocked(win);
imMayMove = true;
}
win.adjustStartingWindowFlags();
@@ -2222,8 +2216,9 @@
// of a transaction to avoid artifacts.
win.mAnimatingExit = true;
} else {
- if (mInputMethodWindow == win) {
- setInputMethodWindowLocked(null);
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent.mInputMethodWindow == win) {
+ displayContent.setInputMethodWindowLocked(null);
}
boolean stopped = win.mAppToken != null ? win.mAppToken.mAppStopped : true;
// We set mDestroying=true so AppWindowToken#notifyAppStopped in-to destroy surfaces
@@ -2828,7 +2823,7 @@
@Override
public WindowManagerPolicy.WindowState getInputMethodWindowLw() {
- return mInputMethodWindow;
+ return mRoot.getCurrentInputMethodWindow();
}
@Override
@@ -5575,10 +5570,10 @@
// change message pending.
mH.removeMessages(H.REPORT_FOCUS_CHANGE);
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
- // TODO(multidisplay): Focused windows on default display only.
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final DisplayContent displayContent = (newFocus != null) ? newFocus.getDisplayContent()
+ : getDefaultDisplayContentLocked();
boolean imWindowChanged = false;
- if (mInputMethodWindow != null) {
+ if (displayContent.mInputMethodWindow != null) {
final WindowState prevTarget = mInputMethodTarget;
final WindowState newTarget =
@@ -5587,10 +5582,11 @@
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
- final int prevImeAnimLayer = mInputMethodWindow.mWinAnimator.mAnimLayer;
+ final int prevImeAnimLayer =
+ displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer;
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
- imWindowChanged |=
- prevImeAnimLayer != mInputMethodWindow.mWinAnimator.mAnimLayer;
+ imWindowChanged |= prevImeAnimLayer
+ != displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer;
}
}
@@ -5613,7 +5609,7 @@
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
- if (imWindowChanged && oldFocus != mInputMethodWindow) {
+ if (imWindowChanged && oldFocus != displayContent.mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
displayContent.performLayout(true /*initial*/, updateInputWindows);
@@ -6033,8 +6029,15 @@
}
synchronized (mWindowMap) {
final Region r = new Region();
- if (mInputMethodWindow != null) {
- mInputMethodWindow.getTouchableRegion(r);
+ // TODO(b/111080190): this method is only return the recent focused IME touch region,
+ // For Multi-Session IME, will need to add API for given display Id to
+ // get the right IME touch region.
+ for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mRoot.mChildren.get(i);
+ if (displayContent.mInputMethodWindow != null) {
+ displayContent.mInputMethodWindow.getTouchableRegion(r);
+ return r;
+ }
}
return r;
}
@@ -6217,8 +6220,9 @@
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
- if (mInputMethodWindow != null) {
- mInputMethodWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
+ final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
+ if (imeWindow != null) {
+ imeWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
}
proto.write(DISPLAY_FROZEN, mDisplayFrozen);
final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
@@ -6388,8 +6392,9 @@
pw.print(" mLastStatusBarVisibility=0x");
pw.println(Integer.toHexString(mLastStatusBarVisibility));
}
- if (mInputMethodWindow != null) {
- pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow);
+ final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
+ if (imeWindow != null) {
+ pw.print(" mInputMethodWindow="); pw.println(imeWindow);
}
mWindowPlacerLocked.dump(pw, " ");
mRoot.mWallpaperController.dump(pw, " ");
@@ -7287,10 +7292,9 @@
}
@Override
- public int getInputMethodWindowVisibleHeight() {
+ public int getInputMethodWindowVisibleHeight(int displayId) {
synchronized (mWindowMap) {
- // TODO(multi-display): Have caller pass in the display they are interested in.
- final DisplayContent dc = getDefaultDisplayContentLocked();
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
return dc.mDisplayFrames.getInputMethodWindowVisibleHeight();
}
}
@@ -7298,8 +7302,9 @@
@Override
public void saveLastInputMethodWindowForTransition() {
synchronized (mWindowMap) {
- if (mInputMethodWindow != null) {
- mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
+ final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
+ if (imeWindow != null) {
+ mPolicy.setLastInputMethodWindowLw(imeWindow, mInputMethodTarget);
}
}
}
@@ -7409,9 +7414,12 @@
@Override
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
synchronized (mWindowMap) {
- // TODO: multi-display
- if (getDefaultDisplayContentLocked().inputMethodClientHasFocus(client)) {
- return true;
+ // 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)) {
+ return true;
+ }
}
// Okay, how about this... what is the current focus?
@@ -7429,6 +7437,17 @@
}
return false;
}
+
+ @Override
+ public int getDisplayIdForWindow(IBinder windowToken) {
+ synchronized (mWindowMap) {
+ final WindowState window = mWindowMap.get(windowToken);
+ if (window != null) {
+ return window.getDisplayContent().getDisplayId();
+ }
+ return Display.INVALID_DISPLAY;
+ }
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 637c0ea..5272b66 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -845,7 +845,7 @@
mWindowFrames.mContainingFrame.bottom =
mWindowFrames.mContainingFrame.top + frozen.height();
}
- final WindowState imeWin = mService.mInputMethodWindow;
+ final WindowState imeWin = mService.mRoot.getCurrentInputMethodWindow();
// 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
@@ -3734,6 +3734,8 @@
windowInfo.focused = isFocused();
Task task = getTask();
windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
+ windowInfo.hasFlagWatchOutsideTouch =
+ (mAttrs.flags & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
if (mIsChildWindow) {
windowInfo.parentToken = getParentWindow().mClient.asBinder();
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a1132d7..4c71d65 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -114,7 +114,6 @@
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
-import android.app.backup.ISelectBackupTransportCallback;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -262,7 +261,6 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
@@ -895,7 +893,6 @@
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
- private static final String TAG_MANDATORY_BACKUP_TRANSPORT = "mandatory_backup_transport";
private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
private static final String TAG_METERED_DATA_DISABLED_PACKAGES
@@ -1016,10 +1013,6 @@
// Default title of confirm credentials screen
String organizationName = null;
- // The component name of the backup transport which has to be used if backups are mandatory
- // or null if backups are not mandatory.
- ComponentName mandatoryBackupTransport = null;
-
// Message for user switcher
String startUserSessionMessage = null;
String endUserSessionMessage = null;
@@ -1283,11 +1276,6 @@
out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled));
out.endTag(null, TAG_IS_LOGOUT_ENABLED);
}
- if (mandatoryBackupTransport != null) {
- out.startTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
- out.attribute(null, ATTR_VALUE, mandatoryBackupTransport.flattenToString());
- out.endTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
- }
if (startUserSessionMessage != null) {
out.startTag(null, TAG_START_USER_SESSION_MESSAGE);
out.text(startUserSessionMessage);
@@ -1476,9 +1464,6 @@
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
isLogoutEnabled = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
- } else if (TAG_MANDATORY_BACKUP_TRANSPORT.equals(tag)) {
- mandatoryBackupTransport = ComponentName.unflattenFromString(
- parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) {
type = parser.next();
if (type == XmlPullParser.TEXT) {
@@ -2603,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) {
@@ -2620,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()
@@ -2636,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.
@@ -4759,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.
@@ -4778,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;
}
}
@@ -5151,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
@@ -5194,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,
@@ -10538,8 +10573,7 @@
final int userId = UserHandle.getUserId(uid);
Intent intent = null;
if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
- DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction) ||
- DevicePolicyManager.POLICY_MANDATORY_BACKUPS.equals(restriction)) {
+ DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(userId);
final int N = policy.mAdminList.size();
@@ -10548,9 +10582,7 @@
if ((admin.disableCamera &&
DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
(admin.disableScreenCapture && DevicePolicyManager
- .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) ||
- (admin.mandatoryBackupTransport != null && DevicePolicyManager
- .POLICY_MANDATORY_BACKUPS.equals(restriction))) {
+ .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
break;
}
@@ -11960,12 +11992,7 @@
}
Preconditions.checkNotNull(admin);
synchronized (getLockObject()) {
- ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(
- admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- if (!enabled) {
- activeAdmin.mandatoryBackupTransport = null;
- saveSettingsLocked(UserHandle.USER_SYSTEM);
- }
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
}
final long ident = mInjector.binderClearCallingIdentity();
@@ -12000,87 +12027,6 @@
}
@Override
- public boolean setMandatoryBackupTransport(
- ComponentName admin,
- ComponentName backupTransportComponent) {
- if (!mHasFeature) {
- return false;
- }
- Preconditions.checkNotNull(admin);
- enforceDeviceOwner(admin);
-
- final int callingUid = mInjector.binderGetCallingUid();
- final AtomicBoolean success = new AtomicBoolean(false);
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- final ISelectBackupTransportCallback selectBackupTransportCallbackInternal =
- new ISelectBackupTransportCallback.Stub() {
- public void onSuccess(String transportName) {
- saveMandatoryBackupTransport(admin, callingUid, backupTransportComponent);
- success.set(true);
- countDownLatch.countDown();
- }
-
- public void onFailure(int reason) {
- countDownLatch.countDown();
- }
- };
- final long identity = mInjector.binderClearCallingIdentity();
- try {
- IBackupManager ibm = mInjector.getIBackupManager();
- if (ibm != null && backupTransportComponent != null) {
- if (!ibm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
- ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- }
- ibm.selectBackupTransportAsync(
- backupTransportComponent, selectBackupTransportCallbackInternal);
- countDownLatch.await();
- if (success.get()) {
- ibm.setBackupEnabled(true);
- }
- } else if (backupTransportComponent == null) {
- saveMandatoryBackupTransport(admin, callingUid, backupTransportComponent);
- success.set(true);
- }
- } catch (RemoteException e) {
- throw new IllegalStateException("Failed to set mandatory backup transport.", e);
- } catch (InterruptedException e) {
- throw new IllegalStateException("Failed to set mandatory backup transport.", e);
- } finally {
- mInjector.binderRestoreCallingIdentity(identity);
- }
- return success.get();
- }
-
- private void saveMandatoryBackupTransport(
- ComponentName admin, int callingUid, ComponentName backupTransportComponent) {
- synchronized (getLockObject()) {
- ActiveAdmin activeAdmin =
- getActiveAdminWithPolicyForUidLocked(
- admin,
- DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
- callingUid);
- if (!Objects.equals(backupTransportComponent,
- activeAdmin.mandatoryBackupTransport)) {
- activeAdmin.mandatoryBackupTransport =
- backupTransportComponent;
- saveSettingsLocked(UserHandle.USER_SYSTEM);
- }
- }
- }
-
- @Override
- public ComponentName getMandatoryBackupTransport() {
- if (!mHasFeature) {
- return null;
- }
- synchronized (getLockObject()) {
- ActiveAdmin activeAdmin = getDeviceOwnerAdminLocked();
- return activeAdmin == null ? null : activeAdmin.mandatoryBackupTransport;
- }
- }
-
-
- @Override
public boolean bindDeviceAdminServiceAsUser(
@NonNull ComponentName admin, @NonNull IApplicationThread caller,
@Nullable IBinder activtiyToken, @NonNull Intent serviceIntent,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c80b9d8..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;
@@ -785,6 +786,8 @@
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
+ boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime",
+ false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
@@ -795,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();
@@ -928,7 +934,7 @@
traceLog.traceEnd();
}, START_HIDL_SERVICES);
- if (!isWatch) {
+ if (!isWatch && enableVrService) {
traceBeginAndSlog("StartVrManagerService");
mSystemServiceManager.startService(VrManagerService.class);
traceEnd();
@@ -1459,7 +1465,7 @@
traceEnd();
}
- if (!isWatch) {
+ if (!isWatch && !disableNetworkTime) {
traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
if (useNewTimeServices) {
diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java
deleted file mode 100644
index d9d4b96..0000000
--- a/services/net/java/android/net/dns/ResolvUtil.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dns;
-
-import static android.system.OsConstants.AI_ADDRCONFIG;
-
-import android.net.Network;
-import android.net.NetworkUtils;
-import android.system.GaiException;
-import android.system.OsConstants;
-import android.system.StructAddrinfo;
-
-import libcore.io.Libcore;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-
-/**
- * DNS resolution utility class.
- *
- * @hide
- */
-public class ResolvUtil {
- // Non-portable DNS resolution flag.
- private static final long NETID_USE_LOCAL_NAMESERVERS = 0x80000000L;
-
- private ResolvUtil() {}
-
- public static InetAddress[] blockingResolveAllLocally(Network network, String name)
- throws UnknownHostException {
- // Use AI_ADDRCONFIG by default
- return blockingResolveAllLocally(network, name, AI_ADDRCONFIG);
- }
-
- public static InetAddress[] blockingResolveAllLocally(
- Network network, String name, int aiFlags) throws UnknownHostException {
- final StructAddrinfo hints = new StructAddrinfo();
- hints.ai_flags = aiFlags;
- // Other hints identical to the default Inet6AddressImpl implementation
- hints.ai_family = OsConstants.AF_UNSPEC;
- hints.ai_socktype = OsConstants.SOCK_STREAM;
-
- final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network);
-
- try {
- return Libcore.os.android_getaddrinfo(name, hints, (int) networkForResolv.netId);
- } catch (GaiException gai) {
- gai.rethrowAsUnknownHostException(name + ": TLS-bypass resolution failed");
- return null; // keep compiler quiet
- }
- }
-
- public static Network getNetworkWithUseLocalNameserversFlag(Network network) {
- final long netidForResolv = NETID_USE_LOCAL_NAMESERVERS | (long) network.netId;
- return new Network((int) netidForResolv);
- }
-
- public static Network makeNetworkWithPrivateDnsBypass(Network network) {
- return new Network(network) {
- @Override
- public InetAddress[] getAllByName(String host) throws UnknownHostException {
- return blockingResolveAllLocally(network, host);
- }
- };
- }
-}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index ccc092d..0176dd4 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -228,6 +228,9 @@
// Encourages logging of any available arguments, and all call sites
// are necessarily logged identically.
//
+ // NOTE: Log first because passed objects may or may not be thread-safe and
+ // once passed on to the callback they may be modified by another thread.
+ //
// TODO: Find an lighter weight approach.
private class LoggingCallbackWrapper extends Callback {
private static final String PREFIX = "INVOKE ";
@@ -243,63 +246,63 @@
@Override
public void onPreDhcpAction() {
- mCallback.onPreDhcpAction();
log("onPreDhcpAction()");
+ mCallback.onPreDhcpAction();
}
@Override
public void onPostDhcpAction() {
- mCallback.onPostDhcpAction();
log("onPostDhcpAction()");
+ mCallback.onPostDhcpAction();
}
@Override
public void onNewDhcpResults(DhcpResults dhcpResults) {
- mCallback.onNewDhcpResults(dhcpResults);
log("onNewDhcpResults({" + dhcpResults + "})");
+ mCallback.onNewDhcpResults(dhcpResults);
}
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
- mCallback.onProvisioningSuccess(newLp);
log("onProvisioningSuccess({" + newLp + "})");
+ mCallback.onProvisioningSuccess(newLp);
}
@Override
public void onProvisioningFailure(LinkProperties newLp) {
- mCallback.onProvisioningFailure(newLp);
log("onProvisioningFailure({" + newLp + "})");
+ mCallback.onProvisioningFailure(newLp);
}
@Override
public void onLinkPropertiesChange(LinkProperties newLp) {
- mCallback.onLinkPropertiesChange(newLp);
log("onLinkPropertiesChange({" + newLp + "})");
+ mCallback.onLinkPropertiesChange(newLp);
}
@Override
public void onReachabilityLost(String logMsg) {
- mCallback.onReachabilityLost(logMsg);
log("onReachabilityLost(" + logMsg + ")");
+ mCallback.onReachabilityLost(logMsg);
}
@Override
public void onQuit() {
- mCallback.onQuit();
log("onQuit()");
+ mCallback.onQuit();
}
@Override
public void installPacketFilter(byte[] filter) {
- mCallback.installPacketFilter(filter);
log("installPacketFilter(byte[" + filter.length + "])");
+ mCallback.installPacketFilter(filter);
}
@Override
public void startReadPacketFilter() {
- mCallback.startReadPacketFilter();
log("startReadPacketFilter()");
+ mCallback.startReadPacketFilter();
}
@Override
public void setFallbackMulticastFilter(boolean enabled) {
- mCallback.setFallbackMulticastFilter(enabled);
log("setFallbackMulticastFilter(" + enabled + ")");
+ mCallback.setFallbackMulticastFilter(enabled);
}
@Override
public void setNeighborDiscoveryOffload(boolean enable) {
- mCallback.setNeighborDiscoveryOffload(enable);
log("setNeighborDiscoveryOffload(" + enable + ")");
+ mCallback.setNeighborDiscoveryOffload(enable);
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/net/java/android/net/ip/IpServer.java
similarity index 89%
rename from services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
rename to services/net/java/android/net/ip/IpServer.java
index 5accb45..823c0a1 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity.tethering;
+package android.net.ip;
import static android.net.NetworkUtils.numericToInetAddress;
import static android.net.util.NetworkConstants.asByte;
@@ -31,11 +31,10 @@
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
-import android.net.ip.InterfaceController;
-import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
+import android.net.util.NetdService;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -67,7 +66,22 @@
*
* @hide
*/
-public class TetherInterfaceStateMachine extends StateMachine {
+public class IpServer extends StateMachine {
+ public static final int STATE_UNAVAILABLE = 0;
+ public static final int STATE_AVAILABLE = 1;
+ public static final int STATE_TETHERED = 2;
+ public static final int STATE_LOCAL_ONLY = 3;
+
+ public static String getStateString(int state) {
+ switch (state) {
+ case STATE_UNAVAILABLE: return "UNAVAILABLE";
+ case STATE_AVAILABLE: return "AVAILABLE";
+ case STATE_TETHERED: return "TETHERED";
+ case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
+ }
+ return "UNKNOWN: " + state;
+ }
+
private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
private static final byte DOUG_ADAMS = (byte) 42;
@@ -83,15 +97,53 @@
// TODO: have this configurable
private static final int DHCP_LEASE_TIME_SECS = 3600;
- private final static String TAG = "TetherInterfaceSM";
+ private final static String TAG = "IpServer";
private final static boolean DBG = false;
private final static boolean VDBG = false;
private static final Class[] messageClasses = {
- TetherInterfaceStateMachine.class
+ IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
+ public static class Callback {
+ /**
+ * Notify that |who| has changed its tethering state.
+ *
+ * @param who the calling instance of IpServer
+ * @param state one of STATE_*
+ * @param lastError one of ConnectivityManager.TETHER_ERROR_*
+ */
+ public void updateInterfaceState(IpServer who, int state, int lastError) {}
+
+ /**
+ * Notify that |who| has new LinkProperties.
+ *
+ * @param who the calling instance of IpServer
+ * @param newLp the new LinkProperties to report
+ */
+ public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
+ }
+
+ public static class Dependencies {
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+ return new RouterAdvertisementDaemon(ifParams);
+ }
+
+ public InterfaceParams getInterfaceParams(String ifName) {
+ return InterfaceParams.getByName(ifName);
+ }
+
+ public INetd getNetdService() {
+ return NetdService.getInstance();
+ }
+
+ public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ DhcpServingParams params, SharedLog log) {
+ return new DhcpServer(looper, iface, params, log);
+ }
+ }
+
private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
// request from the user that it wants to tether
public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
@@ -123,7 +175,7 @@
private final INetworkManagementService mNMService;
private final INetd mNetd;
private final INetworkStatsService mStatsService;
- private final IControlsTethering mTetherController;
+ private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
private final String mIfaceName;
@@ -131,7 +183,7 @@
private final LinkProperties mLinkProperties;
private final boolean mUsingLegacyDhcp;
- private final TetheringDependencies mDeps;
+ private final Dependencies mDeps;
private int mLastError;
private int mServingMode;
@@ -148,17 +200,16 @@
private DhcpServer mDhcpServer;
private RaParams mLastRaParams;
- public TetherInterfaceStateMachine(
+ public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetworkManagementService nMService, INetworkStatsService statsService,
- IControlsTethering tetherController, boolean usingLegacyDhcp,
- TetheringDependencies deps) {
+ Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
mNetd = deps.getNetdService();
mStatsService = statsService;
- mTetherController = tetherController;
+ mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
@@ -167,7 +218,7 @@
mDeps = deps;
resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- mServingMode = IControlsTethering.STATE_AVAILABLE;
+ mServingMode = STATE_AVAILABLE;
mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState();
@@ -379,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;
@@ -498,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 =
@@ -521,14 +588,12 @@
private void sendInterfaceState(int newInterfaceState) {
mServingMode = newInterfaceState;
- mTetherController.updateInterfaceState(
- TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
+ mCallback.updateInterfaceState(this, newInterfaceState, mLastError);
sendLinkProperties();
}
private void sendLinkProperties() {
- mTetherController.updateLinkProperties(
- TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
+ mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties));
}
private void resetLinkProperties() {
@@ -539,7 +604,7 @@
class InitialState extends State {
@Override
public void enter() {
- sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
+ sendInterfaceState(STATE_AVAILABLE);
}
@Override
@@ -549,10 +614,10 @@
case CMD_TETHER_REQUESTED:
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
switch (message.arg1) {
- case IControlsTethering.STATE_LOCAL_ONLY:
+ case STATE_LOCAL_ONLY:
transitionTo(mLocalHotspotState);
break;
- case IControlsTethering.STATE_TETHERED:
+ case STATE_TETHERED:
transitionTo(mTetheredState);
break;
default:
@@ -649,7 +714,7 @@
// problematic because transitioning during a multi-state jump yields
// a Log.wtf(). Ultimately, there should be only one ServingState,
// and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of TetherInterfaceStateMachine.
+ // functional element outside of IpServer.
class LocalHotspotState extends BaseServingState {
@Override
public void enter() {
@@ -659,7 +724,7 @@
}
if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
- sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY);
+ sendInterfaceState(STATE_LOCAL_ONLY);
}
@Override
@@ -685,7 +750,7 @@
// problematic because transitioning during a multi-state jump yields
// a Log.wtf(). Ultimately, there should be only one ServingState,
// and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of TetherInterfaceStateMachine.
+ // functional element outside of IpServer.
class TetheredState extends BaseServingState {
@Override
public void enter() {
@@ -695,7 +760,7 @@
}
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
- sendInterfaceState(IControlsTethering.STATE_TETHERED);
+ sendInterfaceState(STATE_TETHERED);
}
@Override
@@ -817,7 +882,7 @@
@Override
public void enter() {
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE);
+ sendInterfaceState(STATE_UNAVAILABLE);
}
}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 91a8857..57ebbfc 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -52,7 +52,6 @@
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderPackages;
import com.android.server.testing.shadows.ShadowAppBackupUtils;
-import com.android.server.testing.shadows.ShadowBackupPolicyEnforcer;
import com.android.server.testing.shadows.ShadowBinder;
import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
@@ -73,10 +72,7 @@
import org.robolectric.shadows.ShadowSettings;
@RunWith(FrameworkRobolectricTestRunner.class)
-@Config(
- manifest = Config.NONE,
- sdk = 26,
- shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class})
+@Config(manifest = Config.NONE, sdk = 26, shadows = {ShadowAppBackupUtils.class})
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
public class BackupManagerServiceTest {
@@ -118,15 +114,12 @@
mBaseStateDir = new File(cacheDir, "base_state");
// Corresponds to /cache/backup_stage
mDataDir = new File(cacheDir, "data");
-
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
@After
public void tearDown() throws Exception {
mBackupThread.quit();
ShadowAppBackupUtils.reset();
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
/* Tests for destination string */
@@ -252,7 +245,6 @@
private ComponentName mNewTransportComponent;
private TransportData mNewTransport;
private TransportMock mNewTransportMock;
- private ComponentName mOldTransportComponent;
private TransportData mOldTransport;
private TransportMock mOldTransportMock;
@@ -260,7 +252,6 @@
mNewTransport = backupTransport();
mNewTransportComponent = mNewTransport.getTransportComponent();
mOldTransport = d2dTransport();
- mOldTransportComponent = mOldTransport.getTransportComponent();
List<TransportMock> transportMocks =
setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
mNewTransportMock = transportMocks.get(0);
@@ -314,42 +305,6 @@
}
@Test
- public void testSelectBackupTransportAsync_whenMandatoryTransport() throws Exception {
- setUpForSelectTransport();
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mNewTransportComponent);
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- verify(callback).onSuccess(eq(mNewTransport.transportName));
- verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
- }
-
- @Test
- public void testSelectBackupTransportAsync_whenOtherThanMandatoryTransport() throws Exception {
- setUpForSelectTransport();
- ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mOldTransportComponent);
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
- verify(callback).onFailure(eq(BackupManager.ERROR_BACKUP_NOT_ALLOWED));
- }
-
- @Test
public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
setUpForSelectTransport();
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupPolicyEnforcer.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupPolicyEnforcer.java
deleted file mode 100644
index e76b9d1..0000000
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupPolicyEnforcer.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.android.server.testing.shadows;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-
-import com.android.server.backup.BackupPolicyEnforcer;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-@Implements(BackupPolicyEnforcer.class)
-public class ShadowBackupPolicyEnforcer {
- @Nullable private static ComponentName sMandatoryBackupTransport;
-
- public static void setMandatoryBackupTransport(
- @Nullable ComponentName backupTransportComponent) {
- sMandatoryBackupTransport = backupTransportComponent;
- }
-
- @Implementation
- @Nullable
- public ComponentName getMandatoryBackupTransport() {
- return sMandatoryBackupTransport;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index eb28e1a..1eb88ba 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -100,6 +100,7 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
@@ -365,7 +366,7 @@
return null;
}
}).when(mActivityManager).registerUidObserver(any(), anyInt(),
- eq(ActivityManager.PROCESS_STATE_UNKNOWN), isNull(String.class));
+ eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
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/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 2338744..f6e5601 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -69,7 +69,6 @@
@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();
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 1aa80c8..0345a81 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -64,8 +64,6 @@
@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/servicestests/src/com/android/server/am/ActivityStackTests.java
index 95f8fd1..ab814ee 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -60,8 +60,6 @@
@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/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index d032eb5..749403e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/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;
@@ -106,7 +90,6 @@
@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/servicestests/src/com/android/server/am/ActivityTestsBase.java
index bb8e5c5..9d09f5c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/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;
@@ -56,11 +57,14 @@
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;
@@ -86,6 +90,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 +99,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 +131,11 @@
return atm;
}
+ void setupActivityTaskManagerService() {
+ mService = createActivityTaskManagerService();
+ mSupervisor = mService.mStackSupervisor;
+ }
+
ActivityManagerService createActivityManagerService() {
final TestActivityTaskManagerService atm =
spy(new TestActivityTaskManagerService(mContext));
@@ -134,6 +148,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 +199,7 @@
private boolean mCreateTask;
private ActivityStack mStack;
private int mActivityFlags;
+ private int mLaunchMode;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -198,6 +225,11 @@
return this;
}
+ ActivityBuilder setLaunchMode(int launchMode) {
+ mLaunchMode = launchMode;
+ return this;
+ }
+
ActivityBuilder setStack(ActivityStack stack) {
mStack = stack;
return this;
@@ -233,6 +265,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,
@@ -500,7 +533,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 +549,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/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 5195214..70cfad1 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -870,8 +870,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/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index d56c6a6..aa3046f 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -51,7 +51,6 @@
public class RunningTasksTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityTaskManagerService mService;
private RunningTasks mRunningTasks;
@@ -60,21 +59,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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index d94a5f345..16b127c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -57,7 +57,6 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.PasswordMetrics;
-import android.app.backup.ISelectBackupTransportCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -2251,8 +2250,8 @@
assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
- // Try with POLICY_DISABLE_CAMERA, POLICY_DISABLE_SCREEN_CAPTURE and
- // POLICY_MANDATORY_BACKUPS, which are not user restrictions
+ // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not
+ // user restrictions
// Camera is not disabled
intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
@@ -2276,34 +2275,6 @@
assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
- // Make the backup transport selection succeed
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- ISelectBackupTransportCallback callback =
- (ISelectBackupTransportCallback) invocation.getArguments()[1];
- if (callback != null) {
- callback.onSuccess("");
- }
- return null;
- }
- }).when(getServices().ibackupManager).selectBackupTransportAsync(
- any(ComponentName.class), any(ISelectBackupTransportCallback.class));
-
-
- // Backups are not mandatory
- intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
- assertNull(intent);
-
- // Backups are mandatory
- ComponentName transportComponent = ComponentName.unflattenFromString(
- "android/com.android.internal.backup.LocalTransport");
- dpm.setMandatoryBackupTransport(admin1, transportComponent);
- intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
- assertNotNull(intent);
- assertEquals(DevicePolicyManager.POLICY_MANDATORY_BACKUPS,
- intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
-
// Same checks for different user
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
// Camera should be disabled by device owner
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 99%
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 f731a4a..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;
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 76%
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 7d0e646..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,14 +14,14 @@
* 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;
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;
@@ -35,18 +35,6 @@
private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper = source -> source;
- 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 testFilterByLanguageEmptyLanguageList() throws Exception {
final ArrayList<Locale> availableLocales = new ArrayList<>();
@@ -398,120 +386,4 @@
assertEquals(availableLocales.get(3), dest.get(0));
}
}
-
- @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));
- }
- }
-
- @Test
- public void testConstructLocaleFromString() throws Exception {
- assertEquals(new Locale("en"), LocaleUtils.constructLocaleFromString("en"));
- assertEquals(new Locale("en", "US"), LocaleUtils.constructLocaleFromString("en_US"));
- assertEquals(new Locale("en", "US", "POSIX"),
- LocaleUtils.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"), LocaleUtils.constructLocaleFromString("tl"));
- assertEquals(new Locale("fil", "PH"), LocaleUtils.constructLocaleFromString("tl_PH"));
- assertEquals(new Locale("fil", "PH", "POSIX"),
- LocaleUtils.constructLocaleFromString("tl_PH_POSIX"));
-
- // So far rejecting an invalid/unexpected locale string is out of the scope of this method.
- assertEquals(new Locale("a"), LocaleUtils.constructLocaleFromString("a"));
- assertEquals(new Locale("a b c"), LocaleUtils.constructLocaleFromString("a b c"));
- assertEquals(new Locale("en-US"), LocaleUtils.constructLocaleFromString("en-US"));
- }
}
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/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/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
index 9fa5ba4..ea44279 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -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/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 5db0867..70e4ce4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -125,7 +125,7 @@
synchronized (sWm.mWindowMap) {
mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow");
- sWm.mInputMethodWindow = mImeWindow;
+ mDisplayContent.mInputMethodWindow = mImeWindow;
mImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG,
"mImeDialogWindow");
mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
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/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
new file mode 100644
index 0000000..100f9c6
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distriZenbuted on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.app.NotificationManager.Policy;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ZenModeConfigTest extends UiServiceTestCase {
+
+ @Test
+ public void testPriorityOnlyMutingAllNotifications() {
+ ZenModeConfig config = getMutedNotificationsConfig();
+ assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+
+ config.allowReminders = true;
+ assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ config.allowReminders = false;
+
+ config.areChannelsBypassingDnd = true;
+ assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ config.areChannelsBypassingDnd = false;
+
+ assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ }
+
+ @Test
+ public void testZenPolicyNothingSetToNotificationPolicy() {
+ ZenModeConfig config = getMutedAllConfig();
+ ZenPolicy zenPolicy = new ZenPolicy.Builder().build();
+ assertEquals(config.toNotificationPolicy(), config.toNotificationPolicy(zenPolicy));
+ }
+
+ @Test
+ public void testZenPolicyToNotificationPolicy() {
+ ZenModeConfig config = getMutedAllConfig();
+ config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
+
+ ZenPolicy zenPolicy = new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .allowReminders(true)
+ .allowEvents(true)
+ .showLights(false)
+ .showInAmbientDisplay(false)
+ .build();
+
+ Policy originalPolicy = config.toNotificationPolicy();
+ int priorityCategories = originalPolicy.priorityCategories;
+ int priorityCallSenders = originalPolicy.priorityCallSenders;
+ int priorityMessageSenders = originalPolicy.priorityMessageSenders;
+ int suppressedVisualEffects = originalPolicy.suppressedVisualEffects;
+ priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
+ priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
+ priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+
+ Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
+ priorityMessageSenders, suppressedVisualEffects);
+
+ assertEquals(expectedPolicy, config.toNotificationPolicy(zenPolicy));
+ }
+
+ @Test
+ public void testPriorityOnlyMutingAll() {
+ ZenModeConfig config = getMutedAllConfig();
+ assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+
+ config.allowReminders = true;
+ assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ config.allowReminders = false;
+
+ config.areChannelsBypassingDnd = true;
+ assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ config.areChannelsBypassingDnd = false;
+
+ config.allowAlarms = true;
+ assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ config.allowAlarms = false;
+
+ assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ }
+
+ private ZenModeConfig getMutedNotificationsConfig() {
+ ZenModeConfig config = new ZenModeConfig();
+ // Allow alarms, media, and system
+ config.allowAlarms = true;
+ config.allowMedia = true;
+ config.allowSystem = true;
+
+ // All notification sounds are not allowed
+ config.allowCalls = false;
+ config.allowRepeatCallers = false;
+ config.allowMessages = false;
+ config.allowReminders = false;
+ config.allowEvents = false;
+ config.areChannelsBypassingDnd = false;
+
+ config.suppressedVisualEffects = 0;
+
+ return config;
+ }
+
+ private ZenModeConfig getMutedAllConfig() {
+ ZenModeConfig config = new ZenModeConfig();
+ // No sounds allowed
+ config.allowAlarms = false;
+ config.allowMedia = false;
+ config.allowSystem = false;
+ config.allowCalls = false;
+ config.allowRepeatCallers = false;
+ config.allowMessages = false;
+ config.allowReminders = false;
+ config.allowEvents = false;
+ config.areChannelsBypassingDnd = false;
+
+ config.suppressedVisualEffects = 0;
+ return config;
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
new file mode 100644
index 0000000..7c6b1c1
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distriZenbuted on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.service.notification.ZenPolicy;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ZenPolicyTest extends UiServiceTestCase {
+
+ @Test
+ public void testZenPolicyApplyAllowedToDisallowed() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // reminders are disallowed
+ builder.allowReminders(false);
+ ZenPolicy remindersDisallowed = builder.build();
+ assertEquals(ZenPolicy.STATE_DISALLOW,
+ remindersDisallowed.getPriorityCategoryReminders());
+
+ // reminders are allowed
+ builder.allowReminders(true);
+ ZenPolicy remindersAllowed = builder.build();
+ assertEquals(ZenPolicy.STATE_ALLOW,
+ remindersAllowed.getPriorityCategoryReminders());
+ assertEquals(ZenPolicy.STATE_DISALLOW,
+ remindersDisallowed.getPriorityCategoryReminders());
+
+ // we apply reminders allowed to reminders disallowed
+ // -> reminders should remain disallowed
+ remindersDisallowed.apply(remindersAllowed);
+ assertEquals(ZenPolicy.STATE_DISALLOW,
+ remindersDisallowed.getPriorityCategoryReminders());
+ }
+
+ @Test
+ public void testZenPolicyApplyAllowedToUnset() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // reminders are unset
+ ZenPolicy remindersUnset = builder.build();
+
+ // reminders are allowed
+ builder.allowReminders(true);
+ ZenPolicy remindersAllowed = builder.build();
+
+ // we apply reminders allowed to reminders unset
+ // -> reminders should be allowed
+ remindersUnset.apply(remindersAllowed);
+ assertEquals(ZenPolicy.STATE_ALLOW, remindersUnset.getPriorityCategoryReminders());
+ }
+
+ @Test
+ public void testZenPolicyApplyDisallowedToUnset() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // reminders are unset
+ ZenPolicy remindersUnset = builder.build();
+
+ // reminders are disallowed
+ builder.allowReminders(false);
+ ZenPolicy remindersDisallowed = builder.build();
+
+ // we apply reminders disallowed to reminders unset
+ // -> reminders should remain disallowed
+ remindersUnset.apply(remindersDisallowed);
+ assertEquals(ZenPolicy.STATE_DISALLOW,
+ remindersUnset.getPriorityCategoryReminders());
+ }
+
+ @Test
+ public void testZenPolicyApplyDisallowedToAllowed() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // reminders are allowed
+ builder.allowReminders(true);
+ ZenPolicy remindersAllowed = builder.build();
+
+ // reminders are disallowed
+ builder.allowReminders(false);
+ ZenPolicy remindersDisallowed = builder.build();
+
+ // we apply reminders allowed to reminders disallowed
+ // -> reminders should change to disallowed
+ remindersAllowed.apply(remindersDisallowed);
+ assertEquals(ZenPolicy.STATE_DISALLOW, remindersAllowed.getPriorityCategoryReminders());
+ }
+
+ @Test
+ public void testZenPolicyApplyUnsetToAllowed() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // reminders are allowed
+ builder.allowReminders(true);
+ ZenPolicy remindersAllowed = builder.build();
+
+ // reminders are unset
+ ZenPolicy.Builder builder2 = new ZenPolicy.Builder();
+ ZenPolicy remindersUnset = builder2.build();
+
+ // we apply reminders allowed to reminders unset
+ // -> reminders should remain allowed
+ remindersAllowed.apply(remindersUnset);
+ assertEquals(ZenPolicy.STATE_ALLOW, remindersAllowed.getPriorityCategoryReminders());
+ }
+
+ @Test
+ public void testZenPolicyApplyMoreSevereCallSenders() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // calls from contacts allowed
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS);
+ ZenPolicy contactsAllowed = builder.build();
+
+ // calls from starred contacts allowed
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_STARRED);
+ ZenPolicy starredAllowed = builder.build();
+
+ // we apply starredAllowed to contactsAllowed -> starred contacts allowed (more restrictive)
+ contactsAllowed.apply(starredAllowed);
+ assertEquals(ZenPolicy.STATE_ALLOW, contactsAllowed.getPriorityCategoryCalls());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, contactsAllowed.getPriorityCallSenders());
+ }
+
+ @Test
+ public void testZenPolicyApplyLessSevereCallSenders() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // calls from contacts allowed
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS);
+ ZenPolicy contactsAllowed = builder.build();
+
+ // calls from anyone allowed
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE);
+ ZenPolicy anyoneAllowed = builder.build();
+
+ // we apply anyoneAllowed to contactsAllowed -> contactsAllowed (more restrictive)
+ contactsAllowed.apply(anyoneAllowed);
+ assertEquals(ZenPolicy.STATE_ALLOW, contactsAllowed.getPriorityCategoryCalls());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, contactsAllowed.getPriorityCallSenders());
+ }
+
+ @Test
+ public void testZenPolicyApplyMoreSevereMessageSenders() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ // messsages from contacts allowed
+ builder.allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS);
+ ZenPolicy contactsAllowed = builder.build();
+
+ // messsages from no one allowed
+ builder.allowMessages(ZenPolicy.PEOPLE_TYPE_NONE);
+ ZenPolicy noneAllowed = builder.build();
+
+ // noneAllowed to contactsAllowed -> no messages allowed (more restrictive)
+ contactsAllowed.apply(noneAllowed);
+ assertEquals(ZenPolicy.STATE_DISALLOW, contactsAllowed.getPriorityCategoryMessages());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, contactsAllowed.getPriorityMessageSenders());
+ }
+
+ @Test
+ public void testZenPolicyMessagesInvalid() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowMessages(20); // invalid #, won't change policy
+ ZenPolicy policy = builder.build();
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMessages());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_UNSET, policy.getPriorityMessageSenders());
+ }
+
+ @Test
+ public void testZenPolicyCallsInvalid() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE);
+ builder.allowCalls(20); // invalid #, won't change policy
+ ZenPolicy policy = builder.build();
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders());
+ }
+
+ @Test
+ public void testEmptyZenPolicy() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, -1);
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowReminders() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowReminders(true);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryReminders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowReminders(false);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryReminders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowEvents() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowEvents(true);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryEvents());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowEvents(false);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryEvents());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowMessages() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityMessageSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityMessageSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowMessages(ZenPolicy.PEOPLE_TYPE_STARRED);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityMessageSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowMessages(ZenPolicy.PEOPLE_TYPE_NONE);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMessages());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityMessageSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowMessages(ZenPolicy.PEOPLE_TYPE_UNSET);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, -1);
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowCalls() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityCallSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_STARRED);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityCallSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_NONE);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryCalls());
+ assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityCallSenders());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowCalls(ZenPolicy.PEOPLE_TYPE_UNSET);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, -1);
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowRepeatCallers() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowRepeatCallers(true);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryRepeatCallers());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowRepeatCallers(false);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryRepeatCallers());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowAlarms() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowAlarms(true);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryAlarms());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowAlarms(false);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryAlarms());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowMedia() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowMedia(true);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMedia());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowMedia(false);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMedia());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ @Test
+ public void testAllowSystem() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.allowSystem(true);
+ ZenPolicy policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM);
+ assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategorySystem());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+
+ builder.allowSystem(false);
+ policy = builder.build();
+ assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM);
+ assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategorySystem());
+ assertAllVisualEffectsUnsetExcept(policy, -1);
+ }
+
+ private void assertAllPriorityCategoriesUnsetExcept(ZenPolicy policy, int except) {
+ if (except != ZenPolicy.PRIORITY_CATEGORY_REMINDERS) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryReminders());
+ }
+
+ if (except != ZenPolicy.PRIORITY_CATEGORY_EVENTS) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryEvents());
+ }
+
+ if (except != ZenPolicy.PRIORITY_CATEGORY_MESSAGES) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMessages());
+ }
+
+ if (except != ZenPolicy.PRIORITY_CATEGORY_CALLS) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryCalls());
+ }
+
+ if (except != ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryRepeatCallers());
+ }
+
+ if (except != ZenPolicy.PRIORITY_CATEGORY_ALARMS) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryAlarms());
+ }
+
+ if (except != ZenPolicy.PRIORITY_CATEGORY_MEDIA) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMedia());
+ }
+
+ if (except != ZenPolicy.PRIORITY_CATEGORY_SYSTEM) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategorySystem());
+ }
+ }
+
+ private void assertAllVisualEffectsUnsetExcept(ZenPolicy policy, int except) {
+ if (except != ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectFullScreenIntent());
+ }
+
+ if (except != ZenPolicy.VISUAL_EFFECT_LIGHTS) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectLights());
+ }
+
+ if (except != ZenPolicy.VISUAL_EFFECT_PEEK) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectPeek());
+ }
+
+ if (except != ZenPolicy.VISUAL_EFFECT_STATUS_BAR) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectStatusBar());
+ }
+
+ if (except != ZenPolicy.VISUAL_EFFECT_BADGE) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectBadge());
+ }
+
+ if (except != ZenPolicy.VISUAL_EFFECT_AMBIENT) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectAmbient());
+ }
+
+ if (except != ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST) {
+ assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectNotificationList());
+ }
+ }
+}
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/startop/OWNERS b/startop/OWNERS
new file mode 100644
index 0000000..bfe96d3
--- /dev/null
+++ b/startop/OWNERS
@@ -0,0 +1,5 @@
+# mailing list: startop-eng@google.com
+chriswailes@google.com
+eholk@google.com
+iam@google.com
+sehr@google.com
diff --git a/startop/scripts/app_startup/analyze_metrics.py b/startop/scripts/app_startup/analyze_metrics.py
new file mode 100755
index 0000000..d74d6f6
--- /dev/null
+++ b/startop/scripts/app_startup/analyze_metrics.py
@@ -0,0 +1,457 @@
+#!/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.
+
+"""
+Perform statistical analysis on measurements produced by app_startup_runner.py
+
+Install:
+$> sudo apt-get install python3-scipy
+
+Usage:
+$> ./analyze_metrics.py <filename.csv> [<filename2.csv> ...]
+$> ./analyze_metrics.py --help
+"""
+
+import argparse
+import csv
+import itertools
+import os
+import subprocess
+import sys
+import tempfile
+from typing import Any, List, Dict, Iterable, TextIO, Tuple
+
+from scipy import stats as sc
+import numpy as np
+
+
+# These CSV columns are considered labels. Everything after them in the same row are metrics.
+_LABEL_COLUMNS=['packages', 'readaheads', 'compiler_filters']
+# The metric series with the 'cold' readahead is the baseline.
+# All others (warm, jit, etc) are the potential improvements.
+
+#fixme: this should probably be an option
+_BASELINE=('readaheads', 'cold')
+# ignore this for some statistic calculations
+_IGNORE_PAIR=('readaheads', 'warm')
+_PLOT_SUBKEY='readaheads'
+_PLOT_GROUPKEY='packages'
+_PLOT_DATA_INDEX = 0
+_DELTA=50
+_DELTA2=100
+_PVALUE_THRESHOLD=0.10
+_debug = False # See -d/--debug flag.
+
+def parse_options(argv: List[str] = None):
+ """Parse command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Perform statistical analysis on measurements produced by app_start_runner.py.")
+ parser.add_argument('input_files', metavar='file.csv', nargs='+', help='CSV file produced by app_startup_runner.py')
+
+ parser.add_argument('-d', '--debug', dest='debug', action='store_true', help='Add extra debugging output')
+ parser.add_argument('-os', '--output-samples', dest='output_samples', default='/dev/null', action='store', help='Store CSV for per-sample data')
+ parser.add_argument('-oc', '--output-comparable', dest='output_comparable', default='/dev/null', action='store', help='Output CSV for comparable against baseline')
+ parser.add_argument('-ocs', '--output-comparable-significant', dest='output_comparable_significant', default='/dev/null', action='store', help='Output CSV for comparable against baseline (significant only)')
+ parser.add_argument('-pt', '--pvalue-threshold', dest='pvalue_threshold', type=float, default=_PVALUE_THRESHOLD, action='store')
+ parser.add_argument('-dt', '--delta-threshold', dest='delta_threshold', type=int, default=_DELTA, action='store')
+
+ return parser.parse_args(argv)
+
+def _debug_print(*args, **kwargs):
+ """Print the args to sys.stderr if the --debug/-d flag was passed in."""
+ global _debug
+ if _debug:
+ print(*args, **kwargs, file=sys.stderr)
+
+def _expand_gen_repr(args):
+ new_args_list = []
+ for i in args:
+ # detect iterable objects that do not have their own override of __str__
+ if hasattr(i, '__iter__'):
+ to_str = getattr(i, '__str__')
+ if to_str.__objclass__ == object:
+ # the repr for a generator is just type+address, expand it out instead.
+ new_args_list.append([_expand_gen_repr([j])[0] for j in i])
+ continue
+ # normal case: uses the built-in to-string
+ new_args_list.append(i)
+ return new_args_list
+
+def _debug_print_gen(*args, **kwargs):
+ """Like _debug_print but will turn any iterable args into a list."""
+ if not _debug:
+ return
+
+ new_args_list = _expand_gen_repr(args)
+ _debug_print(*new_args_list, **kwargs)
+
+def read_headers(input_file: TextIO) -> Tuple[List[str], List[str]]:
+ _debug_print("read_headers for file: ", input_file.name)
+ csv_reader = csv.reader(input_file)
+
+ label_num_columns = len(_LABEL_COLUMNS)
+
+ try:
+ header = next(csv_reader)
+ except StopIteration:
+ header = None
+ _debug_print('header', header)
+
+ if not header:
+ return (None, None)
+
+ labels = header[0:label_num_columns]
+ data = header[label_num_columns:]
+
+ return (labels, data)
+
+def read_labels_and_data(input_file: TextIO) -> Iterable[Tuple[List[str], List[int]]]:
+ _debug_print("print_analysis for file: ", input_file.name)
+ csv_reader = csv.reader(input_file)
+
+ # Skip the header because it doesn't contain any data.
+ # To get the header see read_headers function.
+ try:
+ header = next(csv_reader)
+ except StopIteration:
+ header = None
+
+ label_num_columns = len(_LABEL_COLUMNS)
+
+ for row in csv_reader:
+ if len(row) > 0 and row[0][0] == ';':
+ _debug_print("skip comment line", row)
+ continue
+
+ labels = row[0:label_num_columns]
+ data = [int(i) for i in row[label_num_columns:]]
+
+# _debug_print("labels:", labels)
+# _debug_print("data:", data)
+
+ yield (labels, data)
+
+def group_metrics_by_label(it: Iterable[Tuple[List[str], List[int]]]):
+ prev_labels = None
+ data_2d = []
+
+ for label_list, data_list in it:
+ if prev_labels != label_list:
+ if prev_labels:
+# _debug_print("grouped labels:", prev_labels, "data_2d:", data_2d)
+ yield (prev_labels, data_2d)
+ data_2d = []
+
+ data_2d.append(data_list)
+ prev_labels = label_list
+
+ if prev_labels:
+# _debug_print("grouped labels:", prev_labels, "data_2d:", data_2d)
+ yield (prev_labels, data_2d)
+
+def data_to_numpy(it: Iterable[Tuple[List[str], List[List[int]]]]) -> Iterable[Tuple[List[str], Any]]:
+ for label_list, data_2d in it:
+ yield (label_list, np.asarray(data_2d, dtype=int))
+
+def iterate_columns(np_data_2d):
+ for col in range(np_data_2d.shape[1]):
+ col_as_array = np_data_2d[:, col]
+ yield col_as_array
+
+def confidence_interval(np_data_2d, percent=0.95):
+ """
+ Given some data [[a,b,c],[d,e,f,]...]
+
+ We assume the same metric is in the column (e.g. [a,d])
+ and that data in the rows (e.g. [b,e]) are separate metric values.
+
+ We then calculate the CI for each metric individually returning it as a list of tuples.
+ """
+ arr = []
+ for col_2d in iterate_columns(np_data_2d):
+ mean = col_2d.mean()
+ sigma = col_2d.std()
+
+ ci = sc.norm.interval(percent, loc=mean, scale=sigma / np.sqrt(len(col_2d)))
+ arr.append(ci)
+
+ # TODO: This seems to be returning NaN when all the samples have the same exact value
+ # (e.g. stddev=0, which can trivially happen when sample count = 1).
+
+ return arr
+
+def print_analysis(it, label_header: List[str], data_header: List[str], output_samples: str):
+ print(label_header)
+
+ with open(output_samples, "w") as output_file:
+
+ csv_writer = csv.writer(output_file)
+ csv_writer.writerow(label_header + ['mean', 'std', 'confidence_interval_a', 'confidence_interval_b'])
+
+ for label_list, np_data_2d in it:
+ print("**********************")
+ print(label_list)
+ print()
+ print(" ", data_header)
+ # aggregate computation column-wise
+ print("Mean: ", np_data_2d.mean(axis=0))
+ print("Std: ", np_data_2d.std(axis=0))
+ print("CI95%:", confidence_interval(np_data_2d))
+ print("SEM: ", stats_standard_error_one(np_data_2d, axis=0))
+
+ #ci = confidence_interval(np_data_2d)[_PLOT_DATA_INDEX]
+ sem = stats_standard_error_one(np_data_2d, axis=0)[_PLOT_DATA_INDEX]
+ mean = np_data_2d.mean(axis=0)[_PLOT_DATA_INDEX]
+
+ ci = (mean - sem, mean + sem)
+
+ csv_writer.writerow(label_list + [mean, np_data_2d.std(axis=0)[_PLOT_DATA_INDEX], ci[0], ci[1]])
+
+def from_file_group_by_labels(input_file):
+ (label_header, data_header) = read_headers(input_file)
+ label_data_iter = read_labels_and_data(input_file)
+ grouped_iter = group_metrics_by_label(label_data_iter)
+ grouped_numpy_iter = data_to_numpy(grouped_iter)
+
+ return grouped_numpy_iter, label_header, data_header
+
+def list_without_index(list, index):
+ return list[:index] + list[index+1:]
+
+def group_by_without_baseline_key(grouped_numpy_iter, label_header):
+ """
+ Data is considered comparable if the only difference is the baseline key
+ (i.e. the readahead is different but the package, compilation filter, etc, are the same).
+
+ Returns iterator that's grouped by the non-baseline labels to an iterator of
+ (label_list, data_2d).
+ """
+ baseline_index = label_header.index(_BASELINE[0])
+
+ def get_label_without_baseline(tpl):
+ label_list, _ = tpl
+ return list_without_index(label_list, baseline_index)
+ # [['pkgname', 'compfilter', 'warm'], [data]]
+ # [['pkgname', 'compfilter', 'cold'], [data2]]
+ # [['pkgname2', 'compfilter', 'warm'], [data3]]
+ #
+ # ->
+ # ( [['pkgname', 'compfilter', 'warm'], [data]] # ignore baseline label change.
+ # [['pkgname', 'compfilter', 'cold'], [data2]] ), # split here because the pkgname changed.
+ # ( [['pkgname2', 'compfilter', 'warm'], [data3]] )
+ for group_info, it in itertools.groupby(grouped_numpy_iter, key = get_label_without_baseline):
+ yield it
+
+ # TODO: replace this messy manual iteration/grouping with pandas
+
+def iterate_comparable_metrics(without_baseline_iter, label_header):
+ baseline_index = label_header.index(_BASELINE[0])
+ baseline_value = _BASELINE[1]
+
+ _debug_print("iterate comparables")
+
+ def is_baseline_fun(tp):
+ ll, dat = tp
+ return ll[baseline_index] == baseline_value
+
+ # iterating here when everything but the baseline key is the same.
+ for it in without_baseline_iter:
+ it1, it2 = itertools.tee(it)
+
+ # find all the baseline data.
+ baseline_filter_it = filter(is_baseline_fun, it1)
+
+ # find non-baseline data.
+ nonbaseline_filter_it = itertools.filterfalse(is_baseline_fun, it2)
+
+ yield itertools.product(baseline_filter_it, nonbaseline_filter_it)
+
+def stats_standard_error_one(a, axis):
+ a_std = a.std(axis=axis, ddof=0)
+ a_len = a.shape[axis]
+
+ return a_std / np.sqrt(a_len)
+
+def stats_standard_error(a, b, axis):
+ a_std = a.std(axis=axis, ddof=0)
+ b_std = b.std(axis=axis, ddof=0)
+
+ a_len = a.shape[axis]
+ b_len = b.shape[axis]
+
+ temp1 = a_std*a_std/a_len
+ temp2 = b_std*b_std/b_len
+
+ return np.sqrt(temp1 + temp2)
+
+def stats_tvalue(a, b, axis, delta = 0):
+ a_mean = a.mean(axis=axis)
+ b_mean = b.mean(axis=axis)
+
+ return (a_mean - b_mean - delta) / stats_standard_error(a, b, axis)
+
+def stats_pvalue(a, b, axis, delta, left:bool = False):
+ """
+ Single-tailed 2-sample t-test.
+
+ Returns p-value for the null hypothesis: mean(a) - mean(b) >= delta.
+ :param a: numpy 2d array
+ :param b: numpy 2d array
+ :param axis: which axis to do the calculations across
+ :param delta: test value of mean differences
+ :param left: if true then use <= delta instead of >= delta
+ :return: p-value
+ """
+ # implement our own pvalue calculation because the built-in t-test (t,p values)
+ # only offer delta=0 , e.g. m1-m1 ? 0
+ # we are however interested in m1-m2 ? delta
+ t_value = stats_tvalue(a, b, axis, delta)
+
+ # 2-sample degrees of freedom is using the array sizes - 2.
+ dof = a.shape[axis] + b.shape[axis] - 2
+
+ if left:
+ # left tailed test. e.g. m1-m2 <= delta
+ return sc.t.cdf(t_value, dof)
+ else:
+ # right tailed test. e.g. m1-m2 >= delta
+ return sc.t.sf(t_value, dof)
+ # a left+right tailed test is a 2-tail t-test and can be done using ttest_ind for delta=0
+
+def print_comparable_analysis(comparable_metrics_iter, label_header, data_header, output_comparable: str, output_comparable_significant: str):
+ baseline_value = _BASELINE[1]
+ baseline_index = label_header.index(_BASELINE[0])
+
+ old_baseline_label_list = None
+ delta = _DELTA
+ filter_value = _IGNORE_PAIR[1]
+ filter_index = label_header.index(_IGNORE_PAIR[0])
+
+ pvalue_threshold = _PVALUE_THRESHOLD
+ ci_threshold = (1 - _PVALUE_THRESHOLD) * 100.0
+
+ with open(output_comparable, "w") as output_file:
+
+ csv_writer = csv.writer(output_file)
+ csv_writer.writerow(label_header + ['mean', 'mean_diff', 'sem', 'pvalue_2tailed', 'pvalue_gt%d' %(_DELTA), 'pvalue_gt%d' %(_DELTA2)])
+
+ print("------------------------------------------------------------------")
+ print("Comparison against the baseline %s = %s" %(_BASELINE, baseline_value))
+ print("--- Right-tailed t-test checks if the baseline >= current %s by at least %d" %(_BASELINE[0], delta))
+ print()
+
+ global_stats = {'better_than_delta': [], 'better_than_delta_p95': []}
+
+ for nested_it in comparable_metrics_iter:
+ print("************************")
+
+ better_than_delta = []
+ better_than_delta_p95 = []
+
+ saw_baseline_once = False
+
+ for ((baseline_label_list, baseline_np_data_2d), (rest_label_list, rest_np_data_2d)) in nested_it:
+ _debug_print("baseline_label_list:", baseline_label_list)
+ _debug_print("baseline_np_data_2d:", baseline_np_data_2d)
+ _debug_print("rest_label_list:", rest_label_list)
+ _debug_print("rest_np_data_2d:", rest_np_data_2d)
+
+ mean_diff = baseline_np_data_2d.mean(axis=0) - rest_np_data_2d.mean(axis=0)
+ # 2-sample 2-tailed t-test with delta=0
+ # e.g. "Is it true that usually the two sample means are different?"
+ t_statistic, t_pvalue = sc.ttest_ind(baseline_np_data_2d, rest_np_data_2d, axis=0)
+
+ # 2-sample 1-tailed t-test with delta=50
+ # e.g. "Is it true that usually the sample means better than 50ms?"
+ t2 = stats_tvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=delta)
+ p2 = stats_pvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=delta)
+
+ t2_b = stats_tvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=_DELTA2)
+ p2_b = stats_pvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=_DELTA2)
+
+ print("%s vs %s" %(rest_label_list, baseline_value))
+ print(" ", data_header)
+ print("Mean Difference: ", mean_diff)
+ print("T-test (2-tailed) != 0: t=%s, p=%s" %(t_statistic, t_pvalue))
+ print("T-test (right-tailed) >= %d: t=%s, p=%s" %(_DELTA, t2, p2))
+ print("T-test (right-tailed) >= %d: t=%s, p=%s" %(_DELTA2, t2_b, p2_b))
+
+ def write_out_values(label_list, *args):
+ csv_writer.writerow(label_list + [i[_PLOT_DATA_INDEX] for i in args])
+
+ sem = stats_standard_error(baseline_np_data_2d, rest_np_data_2d, axis=0)
+ if saw_baseline_once == False:
+ saw_baseline_once = True
+ base_sem = stats_standard_error_one(baseline_np_data_2d, axis=0)
+ write_out_values(baseline_label_list, baseline_np_data_2d.mean(axis=0), [0], base_sem, [None], [None], [None])
+ write_out_values(rest_label_list, rest_np_data_2d.mean(axis=0), mean_diff, sem, t_pvalue, p2, p2_b)
+
+ # now do the global statistics aggregation
+
+ if rest_label_list[filter_index] == filter_value:
+ continue
+
+ if mean_diff > delta:
+ better_than_delta.append((mean_diff, p2, rest_label_list))
+
+ if p2 <= pvalue_threshold:
+ better_than_delta_p95.append((mean_diff, rest_label_list))
+
+ if better_than_delta:
+ global_stats['better_than_delta'].append(better_than_delta)
+ if better_than_delta_p95:
+ global_stats['better_than_delta_p95'].append(better_than_delta_p95)
+
+ print("------------------------")
+ print("Global statistics:")
+ print("//// Rows with %s=%s are ignored here." %_IGNORE_PAIR)
+ print("- # of results with mean diff better than delta(%d) = %d" %(delta, len(global_stats['better_than_delta'])))
+ print(" > (meandiff, pvalue, labels)")
+ for i in global_stats['better_than_delta']:
+ print(" > %s" %i)
+ print("- # of results with mean diff better than delta(%d) CI%d%% = %d" %(delta, ci_threshold, len(global_stats['better_than_delta_p95'])))
+ print(" > (meandiff, labels)")
+ for i in global_stats['better_than_delta_p95']:
+ print(" > %s" %i)
+
+def main():
+ global _debug
+ global _DELTA
+ global _PVALUE_THRESHOLD
+
+ opts = parse_options()
+ _debug = opts.debug
+ _debug_print("parsed options: ", opts)
+
+ _PVALUE_THRESHOLD = opts.pvalue_threshold or _PVALUE_THRESHOLD
+
+ for file_name in opts.input_files:
+ with open(file_name, 'r') as input_file:
+ (grouped_numpy_iter, label_header, data_header) = from_file_group_by_labels(input_file)
+ print_analysis(grouped_numpy_iter, label_header, data_header, opts.output_samples)
+
+ with open(file_name, 'r') as input_file:
+ (grouped_numpy_iter, label_header, data_header) = from_file_group_by_labels(input_file)
+ without_baseline_iter = group_by_without_baseline_key(grouped_numpy_iter, label_header)
+ #_debug_print_gen(without_baseline_iter)
+
+ comparable_metrics_iter = iterate_comparable_metrics(without_baseline_iter, label_header)
+ print_comparable_analysis(comparable_metrics_iter, label_header, data_header, opts.output_comparable, opts.output_comparable_significant)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/app_startup_runner.py b/startop/scripts/app_startup/app_startup_runner.py
new file mode 100755
index 0000000..780bb4e
--- /dev/null
+++ b/startop/scripts/app_startup/app_startup_runner.py
@@ -0,0 +1,322 @@
+#!/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.
+
+#
+#
+# Measure application start-up time by launching applications under various combinations.
+# See --help for more details.
+#
+#
+# Sample usage:
+# $> ./app_startup_runner.py -p com.google.android.calculator -r warm -r cold -lc 10 -o out.csv
+# $> ./analyze_metrics.py out.csv
+#
+#
+
+import argparse
+import csv
+import itertools
+import os
+import subprocess
+import sys
+import tempfile
+from typing import Any, Callable, Dict, Generic, Iterable, List, NamedTuple, TextIO, Tuple, TypeVar, Optional, Union
+
+# The following command line options participate in the combinatorial generation.
+# All other arguments have a global effect.
+_COMBINATORIAL_OPTIONS=['packages', 'readaheads', 'compiler_filters']
+_TRACING_READAHEADS=['mlock', 'fadvise']
+_FORWARD_OPTIONS={'loop_count': '--count'}
+_RUN_SCRIPT=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'run_app_with_prefetch')
+
+RunCommandArgs = NamedTuple('RunCommandArgs', [('package', str), ('readahead', str), ('compiler_filter', Optional[str])])
+CollectorPackageInfo = NamedTuple('CollectorPackageInfo', [('package', str), ('compiler_filter', str)])
+_COLLECTOR_SCRIPT=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'collector')
+_COLLECTOR_TIMEOUT_MULTIPLIER = 2 # take the regular --timeout and multiply by 2; systrace starts up slowly.
+
+_UNLOCK_SCREEN_SCRIPT=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'unlock_screen')
+
+# This must be the only mutable global variable. All other global variables are constants to avoid magic literals.
+_debug = False # See -d/--debug flag.
+_DEBUG_FORCE = None # Ignore -d/--debug if this is not none.
+
+# Type hinting names.
+T = TypeVar('T')
+NamedTupleMeta = Callable[..., T] # approximation of a (S : NamedTuple<T> where S() == T) metatype.
+
+def parse_options(argv: List[str] = None):
+ """Parse command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Run one or more Android applications under various settings in order to measure startup time.")
+ # 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='append', dest='packages', help='package of the application', required=True)
+ required_named.add_argument('-r', '--readahead', action='append', dest='readaheads', help='which readahead mode to use', choices=('warm', 'cold', 'mlock', 'fadvise'), 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('-c', '--compiler-filter', action='append', dest='compiler_filters', help='which compiler filter to use. if omitted it does not enforce the app\'s compiler filter', choices=('speed', 'speed-profile', 'quicken'))
+ 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')
+ optional_named.add_argument('-o', '--output', dest='output', action='store', help='Write CSV output to file.')
+ optional_named.add_argument('-t', '--timeout', dest='timeout', action='store', type=int, help='Timeout after this many seconds when executing a single run.')
+ optional_named.add_argument('-lc', '--loop-count', dest='loop_count', default=1, type=int, action='store', help='How many times to loop a single run.')
+ optional_named.add_argument('-in', '--inodes', dest='inodes', type=str, action='store', help='Path to inodes file (system/extras/pagecache/pagecache.py -d inodes)')
+
+ return parser.parse_args(argv)
+
+# TODO: refactor this with a common library file with analyze_metrics.py
+def _debug_print(*args, **kwargs):
+ """Print the args to sys.stderr if the --debug/-d flag was passed in."""
+ if _debug:
+ print(*args, **kwargs, file=sys.stderr)
+
+def _expand_gen_repr(args):
+ """Like repr but any generator-like object has its iterator consumed
+ and then called repr on."""
+ new_args_list = []
+ for i in args:
+ # detect iterable objects that do not have their own override of __str__
+ if hasattr(i, '__iter__'):
+ to_str = getattr(i, '__str__')
+ if to_str.__objclass__ == object:
+ # the repr for a generator is just type+address, expand it out instead.
+ new_args_list.append([_expand_gen_repr([j])[0] for j in i])
+ continue
+ # normal case: uses the built-in to-string
+ new_args_list.append(i)
+ return new_args_list
+
+def _debug_print_gen(*args, **kwargs):
+ """Like _debug_print but will turn any iterable args into a list."""
+ if not _debug:
+ return
+
+ new_args_list = _expand_gen_repr(args)
+ _debug_print(*new_args_list, **kwargs)
+
+def _debug_print_nd(*args, **kwargs):
+ """Like _debug_print but will turn any NamedTuple-type args into a string."""
+ if not _debug:
+ return
+
+ new_args_list = []
+ for i in args:
+ if hasattr(i, '_field_types'):
+ new_args_list.append("%s: %s" %(i.__name__, i._field_types))
+ else:
+ new_args_list.append(i)
+
+ _debug_print(*new_args_list, **kwargs)
+
+def dict_lookup_any_key(dictionary: dict, *keys: List[Any]):
+ for k in keys:
+ if k in dictionary:
+ return dictionary[k]
+ raise KeyError("None of the keys %s were in the dictionary" %(keys))
+
+def generate_run_combinations(named_tuple: NamedTupleMeta[T], opts_dict: Dict[str, List[Optional[str]]])\
+ -> Iterable[T]:
+ """
+ Create all possible combinations given the values in opts_dict[named_tuple._fields].
+
+ :type T: type annotation for the named_tuple type.
+ :param named_tuple: named tuple type, whose fields are used to make combinations for
+ :param opts_dict: dictionary of keys to value list. keys correspond to the named_tuple fields.
+ :return: an iterable over named_tuple instances.
+ """
+ combinations_list = []
+ for k in named_tuple._fields:
+ # the key can be either singular or plural , e.g. 'package' or 'packages'
+ val = dict_lookup_any_key(opts_dict, k, k + "s")
+
+ # treat {'x': None} key value pairs as if it was [None]
+ # otherwise itertools.product throws an exception about not being able to iterate None.
+ combinations_list.append(val or [None])
+
+ _debug_print("opts_dict: ", opts_dict)
+ _debug_print_nd("named_tuple: ", named_tuple)
+ _debug_print("combinations_list: ", combinations_list)
+
+ for combo in itertools.product(*combinations_list):
+ yield named_tuple(*combo)
+
+def key_to_cmdline_flag(key: str) -> str:
+ """Convert key into a command line flag, e.g. 'foo-bars' -> '--foo-bar' """
+ if key.endswith("s"):
+ key = key[:-1]
+ return "--" + key.replace("_", "-")
+
+def as_run_command(tpl: NamedTuple) -> List[Union[str, Any]]:
+ """
+ Convert a named tuple into a command-line compatible arguments list.
+
+ Example: ABC(1, 2, 3) -> ['--a', 1, '--b', 2, '--c', 3]
+ """
+ args = []
+ for key, value in tpl._asdict().items():
+ if value is None:
+ continue
+ args.append(key_to_cmdline_flag(key))
+ args.append(value)
+ return args
+
+def generate_group_run_combinations(run_combinations: Iterable[NamedTuple], dst_nt: NamedTupleMeta[T])\
+ -> Iterable[Tuple[T, Iterable[NamedTuple]]]:
+
+ def group_by_keys(src_nt):
+ src_d = src_nt._asdict()
+ # now remove the keys that aren't legal in dst.
+ for illegal_key in set(src_d.keys()) - set(dst_nt._fields):
+ if illegal_key in src_d:
+ del src_d[illegal_key]
+
+ return dst_nt(**src_d)
+
+ for args_list_it in itertools.groupby(run_combinations, group_by_keys):
+ (group_key_value, args_it) = args_list_it
+ yield (group_key_value, args_it)
+
+def parse_run_script_csv_file(csv_file: TextIO) -> List[int]:
+ """Parse a CSV file full of integers into a flat int list."""
+ csv_reader = csv.reader(csv_file)
+ arr = []
+ for row in csv_reader:
+ for i in row:
+ if i:
+ arr.append(int(i))
+ return arr
+
+def make_script_command_with_temp_output(script: str, args: List[str], **kwargs)\
+ -> Tuple[str, TextIO]:
+ """
+ Create a command to run a script given the args.
+ Appends --count <loop_count> --output <tmp-file-name>.
+ Returns a tuple (cmd, tmp_file)
+ """
+ tmp_output_file = tempfile.NamedTemporaryFile(mode='r')
+ cmd = [script] + args
+ for key, value in kwargs.items():
+ cmd += ['--%s' %(key), "%s" %(value)]
+ if _debug:
+ cmd += ['--verbose']
+ cmd = cmd + ["--output", tmp_output_file.name]
+ return cmd, tmp_output_file
+
+def execute_arbitrary_command(cmd: List[str], simulate: bool, timeout: int) -> Tuple[bool, str]:
+ if simulate:
+ print(" ".join(cmd))
+ return (True, "")
+ else:
+ _debug_print("[EXECUTE]", cmd)
+ proc = subprocess.Popen(cmd,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ try:
+ script_output = proc.communicate(timeout=timeout)[0]
+ except subprocess.TimeoutExpired:
+ print("[TIMEDOUT]")
+ proc.kill()
+ script_output = proc.communicate()[0]
+
+ _debug_print("[STDOUT]", script_output)
+ return_code = proc.wait()
+ passed = (return_code == 0)
+ _debug_print("[$?]", return_code)
+ if not passed:
+ print("[FAILED, code:%s]" %(return_code), script_output, file=sys.stderr)
+
+ return (passed, script_output)
+
+def execute_run_combos(grouped_run_combos: Iterable[Tuple[CollectorPackageInfo, Iterable[RunCommandArgs]]], simulate: bool, inodes_path: str, timeout: int, loop_count: int, need_trace: bool):
+ # nothing will work if the screen isn't unlocked first.
+ execute_arbitrary_command([_UNLOCK_SCREEN_SCRIPT], simulate, timeout)
+
+ for collector_info, run_combos in grouped_run_combos:
+ #collector_args = ["--package", package_name]
+ collector_args = as_run_command(collector_info)
+ # TODO: forward --wait_time for how long systrace runs?
+ # TODO: forward --trace_buffer_size for size of systrace buffer size?
+ collector_cmd, collector_tmp_output_file = make_script_command_with_temp_output(_COLLECTOR_SCRIPT, collector_args, inodes=inodes_path)
+
+ with collector_tmp_output_file:
+ collector_passed = True
+ if need_trace:
+ collector_timeout = timeout and _COLLECTOR_TIMEOUT_MULTIPLIER * timeout
+ (collector_passed, collector_script_output) = execute_arbitrary_command(collector_cmd, simulate, collector_timeout)
+ # TODO: consider to print a ; collector wrote file to <...> into the CSV file so we know it was ran.
+
+ for combos in run_combos:
+ args = as_run_command(combos)
+
+ cmd, tmp_output_file = make_script_command_with_temp_output(_RUN_SCRIPT, args, count=loop_count, input=collector_tmp_output_file.name)
+ with tmp_output_file:
+ (passed, script_output) = execute_arbitrary_command(cmd, simulate, timeout)
+ parsed_output = simulate and [1,2,3] or parse_run_script_csv_file(tmp_output_file)
+ yield (passed, script_output, parsed_output)
+
+def gather_results(commands: Iterable[Tuple[bool, str, List[int]]], key_list: List[str], value_list: List[Tuple[str, ...]]):
+ _debug_print("gather_results: key_list = ", key_list)
+ yield key_list + ["time(ms)"]
+
+ stringify_none = lambda s: s is None and "<none>" or s
+
+ for ((passed, script_output, run_result_list), values) in itertools.zip_longest(commands, value_list):
+ if not passed:
+ continue
+ for result in run_result_list:
+ yield [stringify_none(i) for i in values] + [result]
+
+ yield ["; avg(%s), min(%s), max(%s), count(%s)" %(sum(run_result_list, 0.0) / len(run_result_list), min(run_result_list), max(run_result_list), len(run_result_list)) ]
+
+def eval_and_save_to_csv(output, annotated_result_values):
+ csv_writer = csv.writer(output)
+ for row in annotated_result_values:
+ csv_writer.writerow(row)
+ output.flush() # see the output live.
+
+def main():
+ global _debug
+
+ opts = parse_options()
+ _debug = opts.debug
+ if _DEBUG_FORCE is not None:
+ _debug = _DEBUG_FORCE
+ _debug_print("parsed options: ", opts)
+ need_trace = not not set(opts.readaheads).intersection(set(_TRACING_READAHEADS))
+ if need_trace and not opts.inodes:
+ print("Error: Missing -in/--inodes, required when using a readahead of %s" %(_TRACING_READAHEADS), file=sys.stderr)
+ return 1
+
+ output_file = opts.output and open(opts.output, 'w') or sys.stdout
+
+ combos = lambda: generate_run_combinations(RunCommandArgs, vars(opts))
+ _debug_print_gen("run combinations: ", combos())
+
+ grouped_combos = lambda: generate_group_run_combinations(combos(), CollectorPackageInfo)
+ _debug_print_gen("grouped run combinations: ", grouped_combos())
+
+ exec = execute_run_combos(grouped_combos(), opts.simulate, opts.inodes, opts.timeout, opts.loop_count, need_trace)
+ results = gather_results(exec, _COMBINATORIAL_OPTIONS, combos())
+ eval_and_save_to_csv(output_file, results)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/app_startup_runner_test.py b/startop/scripts/app_startup/app_startup_runner_test.py
new file mode 100755
index 0000000..f96f802a
--- /dev/null
+++ b/startop/scripts/app_startup/app_startup_runner_test.py
@@ -0,0 +1,210 @@
+#!/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 app_startup_runner.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:
+ $> ./app_startup_runner_test.py
+ $> pytest app_startup_runner_test.py
+ $> python -m pytest app_startup_runner_test.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 app_startup_runner as asr
+
+#
+# Argument Parsing Helpers
+#
+
+@contextmanager
+def ignore_stdout_stderr():
+ """Ignore stdout/stderr output for duration of this context."""
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ sys.stdout = io.StringIO()
+ sys.stderr = io.StringIO()
+ try:
+ yield
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+
+@contextmanager
+def argparse_bad_argument(msg):
+ """
+ Assert that a SystemExit is raised when executing this context.
+ If the assertion fails, print the message 'msg'.
+ """
+ with pytest.raises(SystemExit, message=msg):
+ with ignore_stdout_stderr():
+ yield
+
+def assert_bad_argument(args, msg):
+ """
+ Assert that the command line arguments in 'args' are malformed.
+ Prints 'msg' if the assertion fails.
+ """
+ with argparse_bad_argument(msg):
+ parse_args(args)
+
+def parse_args(args):
+ """
+ :param args: command-line like arguments as a single string
+ :return: dictionary of parsed key/values
+ """
+ # "-a b -c d" => ['-a', 'b', '-c', 'd']
+ return vars(asr.parse_options(shlex.split(args)))
+
+def default_dict_for_parsed_args(**kwargs):
+ """
+ # Combine it with all of the "optional" parameters' default values.
+ """
+ d = {'compiler_filters': None, 'simulate': False, 'debug': False, 'output': None, 'timeout': None, 'loop_count': 1, 'inodes': None}
+ d.update(kwargs)
+ return d
+
+def default_mock_dict_for_parsed_args(include_optional=True, **kwargs):
+ """
+ Combine default dict with all optional parameters with some mock required parameters.
+ """
+ d = {'packages': ['com.fake.package'], 'readaheads': ['warm']}
+ if include_optional:
+ d.update(default_dict_for_parsed_args())
+ d.update(kwargs)
+ return d
+
+def parse_optional_args(str):
+ """
+ Parse an argument string which already includes all the required arguments
+ in default_mock_dict_for_parsed_args.
+ """
+ req = "--package com.fake.package --readahead warm"
+ return parse_args("%s %s" %(req, str))
+
+def test_argparse():
+ # missing arguments
+ assert_bad_argument("", "-p and -r are required")
+ assert_bad_argument("-r warm", "-p is required")
+ assert_bad_argument("--readahead warm", "-p is required")
+ assert_bad_argument("-p com.fake.package", "-r is required")
+ assert_bad_argument("--package com.fake.package", "-r is required")
+
+ # required arguments are parsed correctly
+ ad = default_dict_for_parsed_args # assert dict
+
+ assert parse_args("--package xyz --readahead warm") == ad(packages=['xyz'], readaheads=['warm'])
+ assert parse_args("-p xyz -r warm") == ad(packages=['xyz'], readaheads=['warm'])
+
+ assert parse_args("-p xyz -r warm -s") == ad(packages=['xyz'], readaheads=['warm'], simulate=True)
+ assert parse_args("-p xyz -r warm --simulate") == ad(packages=['xyz'], readaheads=['warm'], simulate=True)
+
+ # optional arguments are parsed correctly.
+ mad = default_mock_dict_for_parsed_args # mock assert dict
+ assert parse_optional_args("--output filename.csv") == mad(output='filename.csv')
+ assert parse_optional_args("-o filename.csv") == mad(output='filename.csv')
+
+ assert parse_optional_args("--timeout 123") == mad(timeout=123)
+ assert parse_optional_args("-t 456") == mad(timeout=456)
+
+ assert parse_optional_args("--loop-count 123") == mad(loop_count=123)
+ assert parse_optional_args("-lc 456") == mad(loop_count=456)
+
+ assert parse_optional_args("--inodes bar") == mad(inodes="bar")
+ assert parse_optional_args("-in baz") == mad(inodes="baz")
+
+
+def generate_run_combinations(*args):
+ # expand out the generator values so that assert x == y works properly.
+ return [i for i in asr.generate_run_combinations(*args)]
+
+def test_generate_run_combinations():
+ blank_nd = typing.NamedTuple('Blank')
+ assert generate_run_combinations(blank_nd, {}) == [()], "empty"
+ assert generate_run_combinations(blank_nd, {'a' : ['a1', 'a2']}) == [()], "empty filter"
+ a_nd = typing.NamedTuple('A', [('a', str)])
+ assert generate_run_combinations(a_nd, {'a': None}) == [(None,)], "None"
+ assert generate_run_combinations(a_nd, {'a': ['a1', 'a2']}) == [('a1',), ('a2',)], "one item"
+ assert generate_run_combinations(a_nd,
+ {'a' : ['a1', 'a2'], 'b': ['b1', 'b2']}) == [('a1',), ('a2',)],\
+ "one item filter"
+ ab_nd = typing.NamedTuple('AB', [('a', str), ('b', str)])
+ assert generate_run_combinations(ab_nd,
+ {'a': ['a1', 'a2'],
+ 'b': ['b1', 'b2']}) == [ab_nd('a1', 'b1'),
+ ab_nd('a1', 'b2'),
+ ab_nd('a2', 'b1'),
+ ab_nd('a2', 'b2')],\
+ "two items"
+
+ assert generate_run_combinations(ab_nd,
+ {'as': ['a1', 'a2'],
+ 'bs': ['b1', 'b2']}) == [ab_nd('a1', 'b1'),
+ ab_nd('a1', 'b2'),
+ ab_nd('a2', 'b1'),
+ ab_nd('a2', 'b2')],\
+ "two items plural"
+
+def test_key_to_cmdline_flag():
+ assert asr.key_to_cmdline_flag("abc") == "--abc"
+ assert asr.key_to_cmdline_flag("foos") == "--foo"
+ assert asr.key_to_cmdline_flag("ba_r") == "--ba-r"
+ assert asr.key_to_cmdline_flag("ba_zs") == "--ba-z"
+
+
+def test_make_script_command_with_temp_output():
+ cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script", args=[], count=1)
+ with tmp_file:
+ assert cmd_str == ["fake_script", "--count", "1", "--output", tmp_file.name]
+
+ cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script", args=['a', 'b'], count=2)
+ with tmp_file:
+ assert cmd_str == ["fake_script", "a", "b", "--count", "2", "--output", tmp_file.name]
+
+def test_parse_run_script_csv_file():
+ # empty file -> empty list
+ f = io.StringIO("")
+ assert asr.parse_run_script_csv_file(f) == []
+
+ # common case
+ f = io.StringIO("1,2,3")
+ assert asr.parse_run_script_csv_file(f) == [1,2,3]
+
+ # ignore trailing comma
+ f = io.StringIO("1,2,3,4,5,")
+ assert asr.parse_run_script_csv_file(f) == [1,2,3,4,5]
+
+
+if __name__ == '__main__':
+ pytest.main()
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
new file mode 100755
index 0000000..8a68e50
--- /dev/null
+++ b/startop/scripts/app_startup/launch_application
@@ -0,0 +1,47 @@
+#!/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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "$DIR/lib/common"
+
+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
+ echo "am start failed" >&2
+
+ return 1
+ fi
+
+ # for everything else use the am start "TotalTime" output.
+ verbose_print "$am_output"
+ local total_time="$(echo "$am_output" | grep 'TotalTime:' | sed 's/TotalTime: //g')"
+ verbose_print "total time: $total_time"
+
+ # TODO: Extract alternative metrics such as the #reportFullyDrawn.
+
+ echo "$total_time"
+}
+
+launch_application "$@"
diff --git a/startop/scripts/app_startup/lib/common b/startop/scripts/app_startup/lib/common
new file mode 100755
index 0000000..043d855
--- /dev/null
+++ b/startop/scripts/app_startup/lib/common
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+if [[ -z $ANDROID_BUILD_TOP ]]; then
+ echo "Please run source build/envsetup.sh first" >&2
+ exit 1
+fi
+
+source $ANDROID_BUILD_TOP/build/envsetup.sh
+
+verbose_print() {
+ if [[ "$verbose" == "y" ]]; then
+ 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
new file mode 100755
index 0000000..56bffa8
--- /dev/null
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -0,0 +1,364 @@
+#!/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.
+
+usage() {
+ cat <<EOF
+Usage: run_app_with_prefetch --package <name> [OPTIONS]...
+
+ -p, --package <name> package of the app to test
+ -a, --activity <name> activity to use
+ -h, --help usage information (this)
+ -v, --verbose enable extra verbose printing
+ -i, --input <file> trace file protobuf (default 'TraceFile.pb')
+ -r, --readahead <mode> cold, warm, fadvise, mlock (default 'warm')
+ -w, --when <when> aot or jit (default 'aot')
+ -c, --count <count> how many times to run (default 1)
+ -s, --sleep <sec> how long to sleep after readahead
+ -t, --timeout <sec> how many seconds to timeout in between each app run (default 10)
+ -o, --output <file.csv> what file to write the performance results into as csv (default stdout)
+EOF
+}
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "$DIR/lib/common"
+
+needs_trace_file="n"
+input_file=""
+package=""
+mode='warm'
+count=2
+sleep_time=2
+timeout=10
+output="" # stdout by default
+when="aot"
+parse_arguments() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -p|--package)
+ package="$2"
+ shift
+ ;;
+ -a|--activity)
+ activity="$2"
+ shift
+ ;;
+ -i|--input)
+ input_file="$2"
+ shift
+ ;;
+ -v|--verbose)
+ export verbose="y"
+ ;;
+ -r|--readahead)
+ mode="$2"
+ shift
+ ;;
+ -c|--count)
+ count="$2"
+ ((count+=1))
+ shift
+ ;;
+ -s|--sleep)
+ sleep_time="$2"
+ shift
+ ;;
+ -t|--timeout)
+ timeout="$2"
+ shift
+ ;;
+ -o|--output)
+ output="$2"
+ shift
+ ;;
+ -w|--when)
+ when="$2"
+ shift
+ ;;
+ --compiler-filter)
+ compiler_filter="$2"
+ shift
+ ;;
+ *)
+ echo "Invalid argument: $1" >&2
+ exit 1
+ esac
+ shift
+ done
+}
+
+echo_to_output_file() {
+ if [[ "x$output" != x ]]; then
+ echo "$@" >> $output
+ fi
+ # Always echo to stdout as well.
+ echo "$@"
+}
+
+find_package_path() {
+ local pkg="$1"
+
+ res="$(adb shell find "/data/app/$pkg"-'*' -maxdepth 0 2> /dev/null)"
+ if [[ -z $res ]]; then
+ res="$(adb shell find "/system/app/$pkg"-'*' -maxdepth 0 2> /dev/null)"
+ fi
+ echo "$res"
+}
+
+# Main entry point
+if [[ $# -eq 0 ]]; then
+ usage
+ exit 1
+else
+ parse_arguments "$@"
+
+ # if we do not have have package exit early with an error
+ [[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 1
+
+ if [[ $mode != "cold" && $mode != "warm" ]]; then
+ needs_trace_file="y"
+ if [[ -z "$input_file" ]] || ! [[ -f $input_file ]]; then
+ echo "--input not specified" 1>&2
+ exit 1
+ fi
+ 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
+fi
+
+adb root > /dev/null
+
+if [[ ($when == jit) || ($when == aot) ]] && [[ "$(adb shell getenforce)" != "Permissive" ]]; then
+ echo "Disable selinux permissions and restart framework."
+ adb shell setenforce 0
+ adb shell stop
+ adb shell start
+ adb wait-for-device
+fi
+
+# TODO: set performance governor etc, preferrably only once
+# before every single app run.
+
+# Kill everything before running.
+remote_pkill "$package"
+sleep 1
+
+timings_array=()
+
+package_path="$(find_package_path "$package")"
+if [[ $? -ne 0 ]]; then
+ echo "Failed to detect package path for '$package'" >&2
+ exit 1
+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"
+if [[ $needs_trace_file == y ]]; then
+ # system server always passes down the package path in a hardcoded spot.
+ if [[ $when == "jit" ]]; then
+ verbose_print adb push "$input_file" "$application_trace_file_path"
+ adb push "$input_file" "$application_trace_file_path"
+ keep_application_trace_file="y"
+ else
+ # otherwise use a temporary directory to get normal non-jit behavior.
+ trace_file_directory="/data/local/tmp/prefetch/$package"
+ adb shell mkdir -p "$trace_file_directory"
+ verbose_print adb push "$input_file" "$trace_file_directory/TraceFile.pb"
+ adb push "$input_file" "$trace_file_directory/TraceFile.pb"
+ fi
+fi
+
+# Everything other than JIT: remove the trace file,
+# otherwise system server activity hints will kick in
+# and the new just-in-time app pre-warmup will happen.
+if [[ $keep_application_trace_file == "n" ]]; then
+ adb shell "[[ -f '$application_trace_file_path' ]] && rm '$application_trace_file_path'"
+fi
+
+# Perform AOT readahead/pinning/etc when an application is about to be launched.
+# For JIT readahead, we allow the system to handle it itself (this is a no-op).
+#
+# For warm, cold, etc modes which don't need readahead this is always a no-op.
+perform_aot() {
+ local the_when="$1" # user: aot, jit
+ local the_mode="$2" # warm, cold, fadvise, mlock, etc.
+
+ if [[ $the_when != "aot" ]]; then
+ # TODO: just in time implementation.. should probably use system server.
+ return 0
+ fi
+
+ # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script.
+ if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+
+ # TODO: add activity_hint_sender.exp
+ verbose_print "starting with package=$package package_path=$trace_file_directory"
+ coproc hint_sender_fd { $ANDROID_BUILD_TOP/system/iorap/src/sh/activity_hint_sender.exp "$package" "$trace_file_directory" "$the_mode"; }
+ hint_sender_pid=$!
+ verbose_print "Activity hint sender began"
+
+ notification_success="n"
+ while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do
+ verbose_print "$hint_sender_output"
+ if [[ "$hint_sender_output" == "Press any key to send completed event..."* ]]; then
+ verbose_print "WE DID SEE NOTIFICATION SUCCESS."
+ notification_success='y'
+ # Give it some time to actually perform the readaheads.
+ sleep $sleep_time
+ break
+ fi
+ done
+
+ if [[ $notification_success == 'n' ]]; then
+ echo "[FATAL] Activity hint notification failed." 1>&2
+ exit 1
+ fi
+ fi
+}
+
+perform_aot_cleanup() {
+ local the_when="$1" # user: aot, jit
+ local the_mode="$2" # warm, cold, fadvise, mlock, etc.
+
+ if [[ $the_when != "aot" ]]; then
+ # TODO: just in time implementation.. should probably use system server.
+ return 0
+ fi
+
+ # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script.
+ if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+ # Clean up the hint sender by telling it that the launch was completed,
+ # and to shutdown the watcher.
+ echo "Done\n" >&"${hint_sender_fd[1]}"
+
+ while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do
+ verbose_print "$hint_sender_output"
+ done
+
+ wait $hint_sender_pid
+ 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 "=========================================="
+ verbose_print "==== ITERATION $i ===="
+ verbose_print "=========================================="
+ if [[ $mode != "warm" ]]; then
+ verbose_print "Drop caches for non-warm start."
+ # Drop all caches to get cold starts.
+ adb shell "echo 3 > /proc/sys/vm/drop_caches"
+ fi
+
+ perform_aot "$when" "$mode"
+
+ verbose_print "Running with timeout $timeout"
+
+ # TODO: multiple metrics output.
+ total_time="$(timeout $timeout $DIR/launch_application "$package" "$activity")"
+
+ if [[ $? -ne 0 ]]; then
+ echo "WARNING: Skip bad result, try iteration again." >&2
+ ((i=i-1))
+ continue
+ fi
+
+ perform_aot_cleanup "$when" "$mode"
+
+ echo "Iteration $i. Total time was: $total_time"
+
+ timings_array+=($total_time)
+done
+
+# drop the first result which is usually garbage.
+timings_array=("${timings_array[@]:1}")
+
+
+# Print out interactive/debugging timings and averages.
+# Other scripts should use the --output flag and parse the CSV.
+for tim in "${timings_array[@]}"; do
+ echo_to_output_file -ne "$tim,"
+done
+echo_to_output_file ""
+
+average_string=$(echo "${timings_array[@]}" | awk '{s+=$0}END{print "Average:",s/NR}' RS=" ")
+echo -ne ${average_string}.
+if [[ x$output != x ]]; then
+ echo " Saved results to '$output'"
+fi
+
+# Temporary hack around multiple activities being launched with different package paths (for same app):
+# Clean up all left-over TraceFile.pb
+adb shell 'for i in $(find /data/app -name TraceFile.pb); do rm \$i; done'
+
+# Kill the process to ensure AM isn't keeping it around.
+remote_pkill "$package"
+
+exit 0
diff --git a/startop/scripts/app_startup/unlock_screen b/startop/scripts/app_startup/unlock_screen
new file mode 100755
index 0000000..478294c
--- /dev/null
+++ b/startop/scripts/app_startup/unlock_screen
@@ -0,0 +1,22 @@
+#!/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.
+
+# This turns the screen on if it's off.
+# If it's on it does nothing unless its on the home screen, in which case it opens up some background
+# menu.
+#
+# However, this menu is ignored because "am start" commands still work as expected.
+adb shell input keyevent MENU
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/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index 383d10b..bb066ad 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -195,6 +195,8 @@
public static final int BLOCK_CHECK_FINISHED_TIMING = 9;
public static final int FILTERING_COMPLETED_TIMING = 10;
public static final int FILTERING_TIMED_OUT_TIMING = 11;
+ /** {@hide} */
+ public static final int START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING = 12;
public static final int INVALID = 999999;
@@ -256,6 +258,27 @@
public static final int SIP_PHONE = 0x8;
public static final int THIRD_PARTY_PHONE = 0x10;
+ /**
+ * Indicating the call source is not specified.
+ *
+ * @hide
+ */
+ public static final int CALL_SOURCE_UNSPECIFIED = 0;
+
+ /**
+ * Indicating the call is initiated via emergency dialer's dialpad.
+ *
+ * @hide
+ */
+ public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1;
+
+ /**
+ * Indicating the call is initiated via emergency dialer's shortcut button.
+ *
+ * @hide
+ */
+ public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
+
public static final long MILLIS_IN_5_MINUTES = 1000 * 60 * 5;
public static final long MILLIS_IN_1_SECOND = 1000;
@@ -319,6 +342,9 @@
// A list of video events that have occurred.
private List<VideoEvent> videoEvents;
+ // The source where user initiated this call. ONE OF the CALL_SOURCE_* constants.
+ private int callSource = CALL_SOURCE_UNSPECIFIED;
+
public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
int callTerminationCode, boolean isEmergencyCall, String connectionService,
@@ -356,6 +382,7 @@
isVideoCall = readByteAsBoolean(in);
videoEvents = new LinkedList<>();
in.readTypedList(videoEvents, VideoEvent.CREATOR);
+ callSource = in.readInt();
}
public void writeToParcel(Parcel out, int flags) {
@@ -373,6 +400,7 @@
out.writeTypedList(eventTimings);
writeBooleanAsByte(out, isVideoCall);
out.writeTypedList(videoEvents);
+ out.writeInt(callSource);
}
/** {@hide} */
@@ -385,6 +413,11 @@
this.videoEvents = videoEvents;
}
+ /** {@hide} */
+ public void setCallSource(int callSource) {
+ this.callSource = callSource;
+ }
+
public long getStartTimeMillis() {
return startTimeMillis;
}
@@ -443,6 +476,11 @@
return videoEvents;
}
+ /** {@hide} */
+ public int getCallSource() {
+ return callSource;
+ }
+
@Override
public int describeContents() {
return 0;
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/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 48c1e24..4e22823 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -622,6 +622,18 @@
"android.telecom.extra.USE_ASSISTED_DIALING";
/**
+ * Optional extra for {@link #placeCall(Uri, Bundle)} containing an integer that specifies
+ * the source where user initiated this call. This data is used in metrics.
+ * Valid source are:
+ * {@link ParcelableCallAnalytics#CALL_SOURCE_UNSPECIFIED},
+ * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_DIALPAD},
+ * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_SHORTCUT}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
+
+ /**
* The following 4 constants define how properties such as phone numbers and names are
* displayed to the user.
*/
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index ed84788..722df67 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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1d9e605..e9423f7 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;
@@ -191,7 +193,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;
/**
@@ -1589,12 +1591,14 @@
}
/**
- * @return true if a valid subId else false
- * @hide
+ * Checks if the supplied subscription ID is valid.
+ * Note: a valid subscription ID does not necessarily correspond to an active subscription.
+ *
+ * @param subscriptionId The subscription ID.
+ * @return true if the supplied subscriptionId is valid; false otherwise.
*/
- @UnsupportedAppUsage
- public static boolean isValidSubscriptionId(int subId) {
- return subId > INVALID_SUBSCRIPTION_ID ;
+ public static boolean isValidSubscriptionId(int subscriptionId) {
+ return subscriptionId > INVALID_SUBSCRIPTION_ID;
}
/**
@@ -1608,7 +1612,7 @@
}
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static boolean isValidSlotIndex(int slotIndex) {
return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSimCount();
}
@@ -1808,6 +1812,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);
@@ -1819,6 +1836,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..b5d1f06 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;
@@ -702,7 +703,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 +1500,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 +1692,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);
@@ -6215,36 +6217,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/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/gsm/SmsCbConstants.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
index 0fabc2f..541ca8d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
@@ -118,71 +118,103 @@
public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE
= 0x111E; // 4382
- /** CMAS Message Identifier for Presidential Level alerts for additional languages
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Presidential Level alerts for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE
= 0x111F; // 4383
- /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE
= 0x1120; // 4384
- /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE
= 0x1121; // 4385
- /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE
= 0x1122; // 4386
- /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE
= 0x1123; // 4387
- /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE
= 0x1124; // 4388
- /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely
- * for additional languages.*/
+ /**
+ * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE
= 0x1125; // 4389
- /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE
= 0x1126; // 4390
- /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely
- * for additional languages.*/
+ /**
+ * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE
= 0x1127; // 4391
- /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert)
- * for additional languages. */
+ /**
+ * CMAS Message Identifier for Child Abduction Emergency (Amber Alert)
+ * for additional languages.
+ */
public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE
= 0x1128; // 4392
- /** CMAS Message Identifier for the Required Monthly Test
- * for additional languages. */
+ /** CMAS Message Identifier for the Required Monthly Test for additional languages. */
public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE
= 0x1129; // 4393
- /** CMAS Message Identifier for CMAS Exercise
- * for additional languages. */
+ /** CMAS Message Identifier for CMAS Exercise for additional languages. */
public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE
= 0x112A; // 4394
- /** CMAS Message Identifier for operator defined use
- * for additional languages. */
+ /** CMAS Message Identifier for operator defined use for additional languages. */
public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE
= 0x112B; // 4395
+ /** CMAS Message Identifier for CMAS Public Safety Alerts. */
+ public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY
+ = 0x112C; // 4396
+
+ /** CMAS Message Identifier for CMAS Public Safety Alerts for additional languages. */
+ public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY_LANGUAGE
+ = 0x112D; // 4397
+
+ /** CMAS Message Identifier for CMAS State/Local Test. */
+ public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST
+ = 0x112E; // 4398
+
+ /** CMAS Message Identifier for CMAS State/Local Test for additional languages. */
+ public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST_LANGUAGE
+ = 0x112F; // 4399
+
/** End of CMAS Message Identifier range (including future extensions). */
public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER
= 0x112F; // 4399
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/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/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/android/net/ip/IpServerTest.java
similarity index 71%
rename from tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
rename to tests/net/java/android/net/ip/IpServerTest.java
index 5934653..cff0b54 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity.tethering;
+package android.net.ip;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -37,9 +37,9 @@
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
+import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_TETHERED;
+import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
@@ -50,7 +50,6 @@
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
-import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
@@ -74,7 +73,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class TetherInterfaceStateMachineTest {
+public class IpServerTest {
private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1";
@@ -85,39 +84,38 @@
@Mock private INetworkManagementService mNMService;
@Mock private INetworkStatsService mStatsService;
- @Mock private IControlsTethering mTetherHelper;
+ @Mock private IpServer.Callback mCallback;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
@Mock private DhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
- @Mock private TetheringDependencies mTetheringDependencies;
+ @Mock private IpServer.Dependencies mDependencies;
@Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
- private TetherInterfaceStateMachine mTestedSm;
+ private IpServer mIpServer;
private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */);
}
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
- mTestedSm = new TetherInterfaceStateMachine(
+ mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
- mNMService, mStatsService, mTetherHelper, usingLegacyDhcp,
- mTetheringDependencies);
- mTestedSm.start();
+ mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
+ mIpServer.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
- reset(mNMService, mStatsService, mTetherHelper);
+ reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
- when(mTetheringDependencies.makeDhcpServer(
+ when(mDependencies.makeDhcpServer(
any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
- when(mTetheringDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
- when(mTetheringDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+ when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
+ when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
when(mRaDaemon.start()).thenReturn(true);
}
@@ -130,11 +128,11 @@
private void initTetheredStateMachine(int interfaceType, String upstreamIface,
boolean usingLegacyDhcp) throws Exception {
initStateMachine(interfaceType, usingLegacyDhcp);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) {
dispatchTetherConnectionChanged(upstreamIface);
}
- reset(mNMService, mStatsService, mTetherHelper);
+ reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
}
@@ -145,34 +143,34 @@
@Test
public void startsOutAvailable() {
- mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
- TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
- false /* usingLegacyDhcp */, mTetheringDependencies);
- mTestedSm.start();
+ mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(),
+ TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback,
+ false /* usingLegacyDhcp */, mDependencies);
+ mIpServer.start();
mLooper.dispatchAll();
- verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
+ verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mCallback, mNMService, mStatsService);
}
@Test
public void shouldDoNothingUntilRequested() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
final int [] NOOP_COMMANDS = {
- TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED,
- TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR,
- TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR,
- TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR,
- TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR,
- TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR,
- TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED
+ IpServer.CMD_TETHER_UNREQUESTED,
+ IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
+ IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
+ IpServer.CMD_START_TETHERING_ERROR,
+ IpServer.CMD_STOP_TETHERING_ERROR,
+ IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
+ IpServer.CMD_TETHER_CONNECTION_CHANGED
};
for (int command : NOOP_COMMANDS) {
// None of these commands should trigger us to request action from
// the rest of the system.
dispatchCommand(command);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
}
@@ -180,57 +178,57 @@
public void handlesImmediateInterfaceDown() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
- dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
- verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
- verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
+ verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+ verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canBeTethered() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mTetherHelper, mNMService);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ InOrder inOrder = inOrder(mCallback, mNMService);
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canUnrequestTethering() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+ dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
+ InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canBeTetheredAsUsb() throws Exception {
initStateMachine(TETHERING_USB);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mTetherHelper, mNMService);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ InOrder inOrder = inOrder(mCallback, mNMService);
inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -243,7 +241,7 @@
InOrder inOrder = inOrder(mNMService);
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -257,7 +255,7 @@
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -300,18 +298,18 @@
public void canUnrequestTetheringWithUpstream() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+ dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
+ InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
- inOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), any(LinkProperties.class));
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -322,15 +320,15 @@
if (shouldThrow) {
doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
}
- dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
- InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
IFACE_NAME, mInterfaceConfiguration);
- usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
- usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ usbTeardownOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+ usbTeardownOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
}
@@ -340,15 +338,15 @@
initStateMachine(TETHERING_USB);
doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
- dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
IFACE_NAME, mInterfaceConfiguration);
- usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
- usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ usbTeardownOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
+ usbTeardownOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@@ -358,13 +356,13 @@
doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+ InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
- usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
- mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
- usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ usbTeardownOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
+ usbTeardownOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@@ -372,11 +370,11 @@
public void ignoresDuplicateUpstreamNotifications() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
for (int i = 0; i < 5; i++) {
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
}
@@ -401,11 +399,11 @@
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verify(mTetheringDependencies, never()).makeDhcpServer(any(), any(), any(), any());
+ verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
}
private void assertDhcpStarted(IpPrefix expectedPrefix) {
- verify(mTetheringDependencies, times(1)).makeDhcpServer(
+ verify(mDependencies, times(1)).makeDhcpServer(
eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
verify(mDhcpServer, times(1)).start();
final DhcpServingParams params = mDhcpParamsCaptor.getValue();
@@ -422,21 +420,21 @@
/**
* Send a command to the state machine under test, and run the event loop to idle.
*
- * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
+ * @param command One of the IpServer.CMD_* constants.
* @param arg1 An additional argument to pass.
*/
private void dispatchCommand(int command, int arg1) {
- mTestedSm.sendMessage(command, arg1);
+ mIpServer.sendMessage(command, arg1);
mLooper.dispatchAll();
}
/**
* Send a command to the state machine under test, and run the event loop to idle.
*
- * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
+ * @param command One of the IpServer.CMD_* constants.
*/
private void dispatchCommand(int command) {
- mTestedSm.sendMessage(command);
+ mIpServer.sendMessage(command);
mLooper.dispatchAll();
}
@@ -447,7 +445,7 @@
* @param upstreamIface String name of upstream interface (or null)
*/
private void dispatchTetherConnectionChanged(String upstreamIface) {
- mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+ mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
new InterfaceSet(upstreamIface));
mLooper.dispatchAll();
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 102cb7c..99a5a69 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -41,9 +41,9 @@
import android.net.NetworkUtils;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
-import android.test.mock.MockContext;
import android.support.test.filters.SmallTest;
import android.system.Os;
+import android.test.mock.MockContext;
import java.net.Socket;
import java.util.Arrays;
@@ -121,6 +121,7 @@
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
IpSecService mIpSecService;
Network fakeNetwork = new Network(0xAB);
+ int mUid = Os.getuid();
private static final IpSecAlgorithm AUTH_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
@@ -181,7 +182,7 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -189,8 +190,7 @@
anyInt());
// Verify quota and RefcountedResource objects cleaned up
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
try {
userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
@@ -209,8 +209,7 @@
mIpSecService.allocateSecurityParameterIndex(
mDestinationAddr, TEST_SPI, new Binder());
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
IpSecService.RefcountedResource refcountedRecord =
userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
@@ -218,7 +217,7 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -270,7 +269,7 @@
verify(mMockNetd)
.ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyInt(),
anyString(),
anyString(),
@@ -305,7 +304,7 @@
verify(mMockNetd)
.ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyInt(),
anyString(),
anyString(),
@@ -361,13 +360,12 @@
IpSecTransformResponse createTransformResp =
mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
verify(mMockNetd, times(0))
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -389,7 +387,7 @@
verify(mMockNetd, times(1))
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -397,8 +395,7 @@
anyInt());
// Verify quota and RefcountedResource objects cleaned up
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
@@ -433,8 +430,7 @@
IpSecTransformResponse createTransformResp =
mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
IpSecService.RefcountedResource refcountedRecord =
userRecord.mTransformRecords.getRefcountedResourceOrThrow(
createTransformResp.resourceId);
@@ -443,7 +439,7 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
+ eq(mUid),
anyString(),
anyString(),
eq(TEST_SPI),
@@ -477,7 +473,7 @@
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
- eq(resourceId),
+ eq(mUid),
eq(IpSecManager.DIRECTION_OUT),
anyString(),
anyString(),
@@ -509,8 +505,7 @@
createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
// Check that we have stored the tracking object, and retrieve it
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
IpSecService.RefcountedResource refcountedRecord =
userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
createTunnelResp.resourceId);
@@ -530,8 +525,7 @@
IpSecTunnelInterfaceResponse createTunnelResp =
createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, "blessedPackage");
@@ -551,8 +545,7 @@
IpSecTunnelInterfaceResponse createTunnelResp =
createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
- IpSecService.UserRecord userRecord =
- mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
IpSecService.RefcountedResource refcountedRecord =
userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
createTunnelResp.resourceId);
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 0d3b8e4..40d5544 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -75,6 +75,7 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetworkConstants;
@@ -99,10 +100,8 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadHardwareInterface;
-import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
@@ -190,7 +189,7 @@
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine upstreamNetworkMonitorMasterSM;
- ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+ ArrayList<IpServer> ipv6CoordinatorNotifyList;
int isTetheringSupportedCalls;
public void reset() {
@@ -213,29 +212,35 @@
@Override
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ ArrayList<IpServer> notifyList, SharedLog log) {
ipv6CoordinatorNotifyList = notifyList;
return mIPv6TetheringCoordinator;
}
@Override
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
- return mRouterAdvertisementDaemon;
- }
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies() {
+ @Override
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
+ InterfaceParams ifParams) {
+ return mRouterAdvertisementDaemon;
+ }
- @Override
- public INetd getNetdService() {
- return mNetd;
- }
+ @Override
+ public InterfaceParams getInterfaceParams(String ifName) {
+ final String[] ifaces = new String[] {
+ TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+ final int index = ArrayUtils.indexOf(ifaces, ifName);
+ assertTrue("Non-mocked interface: " + ifName, index >= 0);
+ return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ }
- @Override
- public InterfaceParams getInterfaceParams(String ifName) {
- final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
- TEST_MOBILE_IFNAME };
- final int index = ArrayUtils.indexOf(ifaces, ifName);
- assertTrue("Non-mocked interface: " + ifName, index >= 0);
- return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
- MacAddress.ALL_ZEROS_ADDRESS);
+ @Override
+ public INetd getNetdService() {
+ return mNetd;
+ }
+ };
}
@Override
@@ -458,9 +463,9 @@
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
- // If, and only if, Tethering received an interface status changed
- // then it creates a TetherInterfaceStateMachine and sends out a
- // broadcast indicating that the interface is "available".
+ // If, and only if, Tethering received an interface status changed then
+ // it creates a IpServer and sends out a broadcast indicating that the
+ // interface is "available".
if (emulateInterfaceStatusChanged) {
assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
@@ -557,18 +562,18 @@
}
/**
- * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
+ * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator.
*/
private void sendIPv6TetherUpdates(NetworkState upstreamState) {
// IPv6TetheringCoordinator must have been notified of downstream
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
- eq(IControlsTethering.STATE_TETHERED));
+ eq(IpServer.STATE_TETHERED));
- for (TetherInterfaceStateMachine tism :
+ for (IpServer ipSrv :
mTetheringDependencies.ipv6CoordinatorNotifyList) {
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
- tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
+ ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIPv6Provisioned()
? ipv6OnlyState.linkProperties
: null);
@@ -812,7 +817,7 @@
// We verify get/set called thrice here: once for setup and twice during
// teardown because all events happen over the course of the single
- // dispatchAll() above. Note that once the TISM IPv4 address config
+ // dispatchAll() above. Note that once the IpServer IPv4 address config
// code is refactored the two calls during shutdown will revert to one.
verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(3))
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/Diagnostics.h b/tools/aapt2/Diagnostics.h
index 50e8b33..30deb55 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -133,11 +133,19 @@
void Log(Level level, DiagMessageActual& actual_msg) override {
actual_msg.source.path = source_.path;
diag_->Log(level, actual_msg);
+ if (level == Level::Error) {
+ error = true;
+ }
+ }
+
+ bool HadError() {
+ return error;
}
private:
Source source_;
IDiagnostics* diag_;
+ bool error = false;
DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
};
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/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index d61a15a..2199d00 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -21,6 +21,7 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
+#include "text/Unicode.h"
#include "xml/XmlDom.h"
namespace aapt {
@@ -35,8 +36,9 @@
public:
using xml::Visitor::Visit;
- explicit IdCollector(std::vector<SourcedResourceName>* out_symbols)
- : out_symbols_(out_symbols) {}
+ explicit IdCollector(std::vector<SourcedResourceName>* out_symbols,
+ SourcePathDiagnostics* source_diag) : out_symbols_(out_symbols),
+ source_diag_(source_diag) {}
void Visit(xml::Element* element) override {
for (xml::Attribute& attr : element->attributes) {
@@ -44,12 +46,16 @@
bool create = false;
if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) {
if (create && name.type == ResourceType::kId) {
- auto iter = std::lower_bound(out_symbols_->begin(),
- out_symbols_->end(), name, cmp_name);
- if (iter == out_symbols_->end() || iter->name != name) {
- out_symbols_->insert(iter,
- SourcedResourceName{name.ToResourceName(),
- element->line_number});
+ if (!text::IsValidResourceEntryName(name.entry)) {
+ source_diag_->Error(DiagMessage(element->line_number)
+ << "id '" << name << "' has an invalid entry name");
+ } else {
+ auto iter = std::lower_bound(out_symbols_->begin(),
+ out_symbols_->end(), name, cmp_name);
+ if (iter == out_symbols_->end() || iter->name != name) {
+ out_symbols_->insert(iter, SourcedResourceName{name.ToResourceName(),
+ element->line_number});
+ }
}
}
}
@@ -60,15 +66,17 @@
private:
std::vector<SourcedResourceName>* out_symbols_;
+ SourcePathDiagnostics* source_diag_;
};
} // namespace
bool XmlIdCollector::Consume(IAaptContext* context, xml::XmlResource* xmlRes) {
xmlRes->file.exported_symbols.clear();
- IdCollector collector(&xmlRes->file.exported_symbols);
+ SourcePathDiagnostics source_diag(xmlRes->file.source, context->GetDiagnostics());
+ IdCollector collector(&xmlRes->file.exported_symbols, &source_diag);
xmlRes->root->Accept(&collector);
- return true;
+ return !source_diag.HadError();
}
} // namespace aapt
diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp
index 98da56d..d49af3b 100644
--- a/tools/aapt2/compile/XmlIdCollector_test.cpp
+++ b/tools/aapt2/compile/XmlIdCollector_test.cpp
@@ -64,4 +64,14 @@
EXPECT_TRUE(doc->file.exported_symbols.empty());
}
+TEST(XmlIdCollectorTest, ErrorOnInvalidIds) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ std::unique_ptr<xml::XmlResource> doc =
+ test::BuildXmlDom("<View foo=\"@+id/foo$bar\"/>");
+
+ XmlIdCollector collector;
+ ASSERT_FALSE(collector.Consume(context.get(), doc.get()));
+}
+
} // namespace aapt
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/Files.cpp b/tools/aapt2/util/Files.cpp
index 7cd023b..73105e16 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -102,12 +102,25 @@
#endif
bool mkdirs(const std::string& path) {
- constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP;
+ #ifdef _WIN32
+ // Start after the drive path if present. Calling mkdir with only the drive will cause an error.
+ size_t current_pos = 1u;
+ if (path.size() >= 3 && path[1] == ':' &&
+ (path[2] == '\\' || path[2] == '/')) {
+ current_pos = 3u;
+ }
+ #else
// Start after the first character so that we don't consume the root '/'.
// This is safe to do with unicode because '/' will never match with a continuation character.
size_t current_pos = 1u;
+ #endif
+ constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP;
while ((current_pos = path.find(sDirSep, current_pos)) != std::string::npos) {
std::string parent_path = path.substr(0, current_pos);
+ if (parent_path.empty()) {
+ continue;
+ }
+
int result = ::android::base::utf8::mkdir(parent_path.c_str(), mode);
if (result < 0 && errno != EEXIST) {
return false;
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/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 29bf74c..e50c70d 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -1,18 +1,24 @@
#!/bin/bash
-LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
+LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
-if git branch -vv | grep "^*" | grep "\[aosp/master" > /dev/null; then
+if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then
# Change appears to be in AOSP
exit 0
else
# Change appears to be non-AOSP; search for files
- git show --name-only --pretty=format: $1 | grep $2 | while read file; do
- echo
+ count=0
+ while read -r file ; do
+ if (( count == 0 )); then
+ echo
+ fi
echo -e "\033[0;31mThe source of truth for '$file' is in AOSP.\033[0m"
+ (( count++ ))
+ done < <(git show --name-only --pretty=format: $1 | grep -- "$2")
+ if (( count != 0 )); then
echo
- echo "If your change contains no confidential details, please upload and merge"
- echo "this change at https://android-review.googlesource.com/."
+ echo "If your change contains no confidential details (such as security fixes), please"
+ echo "upload and merge this change at https://android-review.googlesource.com/."
echo
exit 77
- done
+ fi
fi
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;
}