Merge "Multiple shortcut menu for android R (1/n)"
diff --git a/Android.bp b/Android.bp
index 742a70e5..4b82e1d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -353,9 +353,6 @@
"com.android.sysprop.apex",
"PlatformProperties",
],
- aidl: {
- include_dirs: ["system/connectivity/wificond/aidl"],
- },
sdk_version: "core_platform",
installable: false,
}
@@ -605,7 +602,7 @@
java_library {
name: "framework-annotations-lib",
srcs: [ ":framework-annotations" ],
- sdk_version: "current",
+ sdk_version: "core_current",
}
filegroup {
@@ -699,7 +696,7 @@
"core/proto/android/privacy.proto",
"core/proto/android/section.proto",
],
- sdk_version: "current",
+ sdk_version: "9",
srcs: [
"core/proto/**/*.proto",
"libs/incident/proto/android/os/**/*.proto",
@@ -722,6 +719,7 @@
"core/proto/android/privacy.proto",
"core/proto/android/section.proto",
],
+ sdk_version: "core_current",
// Protos have lots of MissingOverride and similar.
errorprone: {
javacflags: ["-XepDisableAllChecks"],
@@ -1024,645 +1022,6 @@
],
}
-// Make the api/current.txt file available for use by modules in other
-// directories.
-filegroup {
- name: "frameworks-base-api-current.txt",
- srcs: [
- "api/current.txt",
- ],
-}
-
-// Make the api/system-current.txt file available for use by modules in other
-// directories.
-filegroup {
- name: "frameworks-base-api-system-current.txt",
- srcs: [
- "api/system-current.txt",
- ],
-}
-
-// Make the api/system-removed.txt file available for use by modules in other
-// directories.
-filegroup {
- name: "frameworks-base-api-system-removed.txt",
- srcs: [
- "api/system-removed.txt",
- ],
-}
-
-framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
- "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
- "-overview $(location core/java/overview.html) " +
- // Federate Support Library references against local API file.
- "-federate SupportLib https://developer.android.com " +
- "-federationapi SupportLib $(location :current-support-api) " +
- // Federate Support Library references against local API file.
- "-federate AndroidX https://developer.android.com " +
- "-federationapi AndroidX $(location :current-androidx-api) "
-
-framework_docs_only_libs = [
- "voip-common",
- "android.test.mock",
- "android-support-annotations",
- "android-support-compat",
- "android-support-core-ui",
- "android-support-core-utils",
- "android-support-design",
- "android-support-dynamic-animation",
- "android-support-exifinterface",
- "android-support-fragment",
- "android-support-media-compat",
- "android-support-percent",
- "android-support-transition",
- "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-vectordrawable",
- "android-support-animatedvectordrawable",
- "android-support-v7-appcompat",
- "android-support-v7-recyclerview",
- "android-support-v8-renderscript",
- "android-support-multidex",
- "android-support-multidex-instrumentation",
-]
-
-metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
- "--ignore-classes-on-classpath " +
- "--hide-package com.android.server " +
- "--error UnhiddenSystemApi " +
- "--hide RequiresPermission " +
- "--hide CallbackInterface " +
- "--hide MissingPermission --hide BroadcastBehavior " +
- "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
- "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
- "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*"
-
-packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
-stubs_defaults {
- name: "framework-doc-stubs-default",
- srcs: [
- ":framework-mime-sources",
- ":framework-non-updatable-sources",
- ":framework-updatable-sources",
- "core/java/**/*.logtags",
- "test-base/src/**/*.java",
- ":opt-telephony-srcs",
- ":opt-net-voip-srcs",
- ":core-current-stubs-source",
- ":core_public_api_files",
- "test-mock/src/**/*.java",
- "test-runner/src/**/*.java",
- ],
- libs: framework_docs_only_libs,
- 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",
- ],
-}
-
-doc_defaults {
- name: "framework-docs-default",
- libs: framework_docs_only_libs +
- ["stub-annotations"],
- html_dirs: [
- "docs/html",
- ],
- knowntags: [
- "docs/knowntags.txt",
- ":known-oj-tags",
- ],
- custom_template: "droiddoc-templates-sdk",
- resourcesdir: "docs/html/reference/images/",
- resourcesoutdir: "reference/android/images/",
- hdf: [
- "dac true",
- "sdk.codename O",
- "sdk.preview.version 1",
- "sdk.version 7.0",
- "sdk.rel.id 1",
- "sdk.preview 0",
- ],
- arg_files: [
- "core/res/AndroidManifest.xml",
- "core/java/overview.html",
- ":current-support-api",
- ":current-androidx-api",
- ],
- create_stubs: false,
-}
-
-doc_defaults {
- name: "framework-dokka-docs-default",
- create_stubs: false,
-}
-
-stubs_defaults {
- name: "metalava-api-stubs-default",
- srcs: [
- ":framework-non-updatable-sources",
- ":framework-updatable-sources",
- "core/java/**/*.logtags",
- ":opt-telephony-srcs",
- ":opt-net-voip-srcs",
- ":core-current-stubs-source",
- ":core_public_api_files",
- ":ike-api-srcs",
- ],
- libs: ["framework-internal-utils"],
- installable: false,
- annotations_enabled: true,
- previous_api: ":last-released-public-api",
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- api_levels_annotations_enabled: true,
- api_levels_annotations_dirs: [
- "sdk-dir",
- "api-versions-jars-dir",
- ],
- sdk_version: "core_platform",
- filter_packages: packages_to_document,
-}
-
-droidstubs {
- name: "framework-doc-stubs",
- defaults: ["framework-doc-stubs-default"],
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- args: metalava_framework_docs_args,
- write_sdk_values: true,
-}
-
-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\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) ",
- write_sdk_values: true,
-}
-
-droiddoc {
- name: "doc-comment-check-docs",
- defaults: ["framework-docs-default"],
- 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_only_args + " -offlinemode -title \"Android SDK\"",
- static_doc_index_redirect: "docs/docs-preview-index.html",
-}
-
-droiddoc {
- // Please sync with android-api-council@ before making any changes for the name property below.
- // Since there's cron jobs that fetch offline-sdk-referenceonly-docs-docs.zip periodically.
- // See b/116221385 for reference.
- 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_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
- static_doc_index_redirect: "docs/docs-documentation-redirect.html",
- static_doc_properties: "docs/source.properties",
-}
-
-droiddoc {
- // Please sync with android-api-council@ before making any changes for the name property below.
- // Since there's cron jobs that fetch offline-system-sdk-referenceonly-docs-docs.zip periodically.
- // See b/116221385 for reference.
- 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_only_args + " -hide 101 -hide 104 -hide 108" +
- " -offlinemode -title \"Android System SDK\" -referenceonly",
- static_doc_index_redirect: "docs/docs-documentation-redirect.html",
- static_doc_properties: "docs/source.properties",
-}
-
-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_only_args +
- " -toroot / -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -samplesdir development/samples/browseable ",
-}
-
-droiddoc {
- name: "online-system-api-sdk-docs",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-system-stubs",
- ],
- hdf: [
- "android.whichdoc online",
- "android.hasSamples true",
- ],
- proofread_file: "online-system-api-sdk-docs-proofrerad.txt",
- args: framework_docs_only_args +
- " -referenceonly " +
- " -title \"Android SDK - Including system APIs.\" " +
- " -hide 101 " +
- " -hide 104 " +
- " -hide 108 " +
- " -toroot / -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -samplesdir development/samples/browseable ",
- installable: false,
-}
-
-droiddoc {
- name: "ds-docs-java",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-stubs",
- ],
- hdf: [
- "android.whichdoc online",
- "android.hasSamples true",
- ],
- proofread_file: "ds-docs-proofrerad.txt",
- args: framework_docs_only_args +
- " -toroot / -yamlV2 -metalavaApiSince -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -devsite -samplesdir development/samples/browseable ",
-}
-
-droiddoc {
- name: "ds-docs-kt",
- defaults: ["framework-dokka-docs-default"],
- srcs: [
- ":framework-doc-stubs",
- ],
- args: "-noJdkLink -links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list " +
- "-noStdlibLink",
- proofread_file: "ds-dokka-proofread.txt",
- dokka_enabled: true,
-}
-
-java_genrule {
- name: "ds-docs",
- tools: [
- "zip2zip",
- "merge_zips",
- ],
- srcs: [
- ":ds-docs-java{.docs.zip}",
- ":ds-docs-kt{.docs.zip}",
- ],
- out: ["ds-docs.zip"],
- dist: {
- targets: ["docs"],
- },
- cmd: "$(location zip2zip) -i $(location :ds-docs-kt{.docs.zip}) -o $(genDir)/ds-docs-kt-moved.zip **/*:en/reference/kotlin && " +
- "$(location merge_zips) $(out) $(location :ds-docs-java{.docs.zip}) $(genDir)/ds-docs-kt-moved.zip",
-}
-
-java_genrule {
- name: "ds-docs-switched",
- tools: [
- "switcher4",
- "soong_zip",
- ],
- srcs: [
- ":ds-docs-java{.docs.zip}",
- ":ds-docs-kt{.docs.zip}",
- ],
- out: ["ds-docs-switched.zip"],
- dist: {
- targets: ["docs"],
- },
- cmd: "unzip $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
- "unzip $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
- "SWITCHER=$$(cd $$(dirname $(location switcher4)) && pwd)/$$(basename $(location switcher4)) && " +
- "(cd $(genDir)/en/reference && $$SWITCHER --work platform) && " +
- "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
-}
-
-
-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_only_args +
- " -staticonly " +
- " -toroot / " +
- " -devsite " +
- " -ignoreJdLinks ",
-}
-
-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_only_args +
- " -toroot / " +
- " -atLinksNavtree " +
- " -navtreeonly ",
-}
-
-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_only_args +
- " -toroot / -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -samplesdir development/samples/browseable ",
-}
-
-droiddoc {
- name: "hidden-docs",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-stubs",
- ],
- proofread_file: "hidden-docs-proofrerad.txt",
- args: framework_docs_only_args +
- " -referenceonly " +
- " -title \"Android SDK - Including hidden APIs.\"",
-}
-
-droidstubs {
- name: "hwbinder-stubs-docs",
- srcs: [
- "core/java/android/os/HidlSupport.java",
- "core/java/android/annotation/IntDef.java",
- "core/java/android/annotation/IntRange.java",
- "core/java/android/annotation/NonNull.java",
- "core/java/android/annotation/SystemApi.java",
- "core/java/android/os/HidlMemory.java",
- "core/java/android/os/HwBinder.java",
- "core/java/android/os/HwBlob.java",
- "core/java/android/os/HwParcel.java",
- "core/java/android/os/IHwBinder.java",
- "core/java/android/os/IHwInterface.java",
- "core/java/android/os/DeadObjectException.java",
- "core/java/android/os/DeadSystemException.java",
- "core/java/android/os/NativeHandle.java",
- "core/java/android/os/RemoteException.java",
- "core/java/android/util/AndroidException.java",
- ],
- installable: false,
- sdk_version: "core_platform",
- annotations_enabled: true,
- previous_api: ":last-released-public-api",
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
-}
-
-java_library_static {
- name: "hwbinder.stubs",
- sdk_version: "core_current",
- srcs: [
- ":hwbinder-stubs-docs",
- ],
-}
-
-droidstubs {
- name: "hiddenapi-lists-docs",
- defaults: ["metalava-api-stubs-default"],
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- dex_api_filename: "public-dex.txt",
- private_dex_api_filename: "private-dex.txt",
- removed_dex_api_filename: "removed-dex.txt",
- args: metalava_framework_docs_args +
- " --show-unannotated " +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
- " --show-annotation android.annotation.TestApi ",
-}
-
-droidstubs {
- name: "hiddenapi-mappings",
- defaults: ["metalava-api-stubs-default"],
- srcs: [
- ":opt-telephony-common-srcs",
- ],
-
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- dex_mapping_filename: "dex-mapping.txt",
- args: metalava_framework_docs_args +
- " --hide ReferencesHidden " +
- " --hide UnhiddenSystemApi " +
- " --show-unannotated " +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
- " --show-annotation android.annotation.TestApi ",
-}
-
-droidstubs {
- name: "api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
- api_filename: "public_api.txt",
- private_api_filename: "private.txt",
- removed_api_filename: "removed.txt",
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- args: metalava_framework_docs_args,
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- last_released: {
- api_file: ":last-released-public-api",
- removed_api_file: "api/removed.txt",
- baseline_file: ":public-api-incompatibilities-with-last-released",
- },
- api_lint: {
- enabled: true,
- new_since: ":last-released-public-api",
- baseline_file: "api/lint-baseline.txt",
- },
- },
- jdiff_enabled: true,
-}
-
-droidstubs {
- name: "system-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
- api_tag_name: "SYSTEM",
- api_filename: "system-api.txt",
- private_api_filename: "system-private.txt",
- private_dex_api_filename: "system-private-dex.txt",
- removed_api_filename: "system-removed.txt",
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
- check_api: {
- current: {
- api_file: "api/system-current.txt",
- removed_api_file: "api/system-removed.txt",
- },
- last_released: {
- api_file: ":last-released-system-api",
- removed_api_file: "api/system-removed.txt",
- baseline_file: ":system-api-incompatibilities-with-last-released"
- },
- api_lint: {
- enabled: true,
- new_since: ":last-released-system-api",
- baseline_file: "api/system-lint-baseline.txt",
- },
- },
- jdiff_enabled: true,
-}
-
-droidstubs {
- name: "test-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
- api_tag_name: "TEST",
- api_filename: "test-api.txt",
- removed_api_filename: "test-removed.txt",
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.TestApi",
- check_api: {
- current: {
- api_file: "api/test-current.txt",
- removed_api_file: "api/test-removed.txt",
- },
- api_lint: {
- enabled: true,
- baseline_file: "api/test-lint-baseline.txt",
- },
- },
-}
-
filegroup {
name: "framework-annotation-nonnull-srcs",
srcs: [
@@ -1716,6 +1075,7 @@
filegroup {
name: "framework-telephony-stack-shared-srcs",
srcs: [
+ "core/java/android/os/BasicShellCommandHandler.java",
"core/java/android/os/RegistrantList.java",
"core/java/android/os/Registrant.java",
"core/java/android/util/LocalLog.java",
@@ -1778,3 +1138,20 @@
"core/java/com/android/internal/util/XmlUtils.java",
],
}
+
+// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
+metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
+ "--ignore-classes-on-classpath " +
+ "--hide-package com.android.server " +
+ "--error UnhiddenSystemApi " +
+ "--hide RequiresPermission " +
+ "--hide CallbackInterface " +
+ "--hide MissingPermission --hide BroadcastBehavior " +
+ "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
+ "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
+ "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*"
+
+build = [
+ "StubLibraries.bp",
+ "ApiDocs.bp",
+]
diff --git a/ApiDocs.bp b/ApiDocs.bp
new file mode 100644
index 0000000..e373db6
--- /dev/null
+++ b/ApiDocs.bp
@@ -0,0 +1,436 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// How API docs are generated:
+//
+// raw source files --(metalava)--> stub source files --(doclava)--> API doc
+//
+// The metalava conversion is done by droidstub modules framework-doc-*-stubs.
+// The API doc generation is done by the various droiddoc modules each of which
+// is for different format.
+
+/////////////////////////////////////////////////////////////////////
+// stub source files are generated using metalava
+/////////////////////////////////////////////////////////////////////
+
+framework_docs_only_libs = [
+ "voip-common",
+ "android.test.mock",
+ "android-support-annotations",
+ "android-support-compat",
+ "android-support-core-ui",
+ "android-support-core-utils",
+ "android-support-design",
+ "android-support-dynamic-animation",
+ "android-support-exifinterface",
+ "android-support-fragment",
+ "android-support-media-compat",
+ "android-support-percent",
+ "android-support-transition",
+ "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-vectordrawable",
+ "android-support-animatedvectordrawable",
+ "android-support-v7-appcompat",
+ "android-support-v7-recyclerview",
+ "android-support-v8-renderscript",
+ "android-support-multidex",
+ "android-support-multidex-instrumentation",
+]
+
+stubs_defaults {
+ name: "framework-doc-stubs-default",
+ srcs: [
+ ":framework-mime-sources",
+ ":framework-non-updatable-sources",
+ ":framework-updatable-sources",
+ "core/java/**/*.logtags",
+ "test-base/src/**/*.java",
+ ":opt-telephony-srcs",
+ ":opt-net-voip-srcs",
+ ":core-current-stubs-source",
+ ":core_public_api_files",
+ "test-mock/src/**/*.java",
+ "test-runner/src/**/*.java",
+ ],
+ libs: framework_docs_only_libs,
+ 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",
+ ],
+}
+
+droidstubs {
+ name: "framework-doc-stubs",
+ defaults: ["framework-doc-stubs-default"],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ args: metalava_framework_docs_args,
+ write_sdk_values: true,
+}
+
+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\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) ",
+ write_sdk_values: true,
+}
+
+/////////////////////////////////////////////////////////////////////
+// API docs are created from the generated stub source files
+// using droiddoc
+/////////////////////////////////////////////////////////////////////
+
+framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
+ "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
+ "-overview $(location core/java/overview.html) " +
+ // Federate Support Library references against local API file.
+ "-federate SupportLib https://developer.android.com " +
+ "-federationapi SupportLib $(location :current-support-api) " +
+ // Federate Support Library references against local API file.
+ "-federate AndroidX https://developer.android.com " +
+ "-federationapi AndroidX $(location :current-androidx-api) "
+
+doc_defaults {
+ name: "framework-docs-default",
+ libs: framework_docs_only_libs +
+ ["stub-annotations"],
+ html_dirs: [
+ "docs/html",
+ ],
+ knowntags: [
+ "docs/knowntags.txt",
+ ":known-oj-tags",
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ resourcesdir: "docs/html/reference/images/",
+ resourcesoutdir: "reference/android/images/",
+ hdf: [
+ "dac true",
+ "sdk.codename O",
+ "sdk.preview.version 1",
+ "sdk.version 7.0",
+ "sdk.rel.id 1",
+ "sdk.preview 0",
+ ],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ "core/java/overview.html",
+ ":current-support-api",
+ ":current-androidx-api",
+ ],
+ create_stubs: false,
+}
+
+doc_defaults {
+ name: "framework-dokka-docs-default",
+ create_stubs: false,
+}
+
+droiddoc {
+ name: "doc-comment-check-docs",
+ defaults: ["framework-docs-default"],
+ 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_only_args + " -offlinemode -title \"Android SDK\"",
+ static_doc_index_redirect: "docs/docs-preview-index.html",
+}
+
+droiddoc {
+ // Please sync with android-api-council@ before making any changes for the name property below.
+ // Since there's cron jobs that fetch offline-sdk-referenceonly-docs-docs.zip periodically.
+ // See b/116221385 for reference.
+ 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_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
+ static_doc_index_redirect: "docs/docs-documentation-redirect.html",
+ static_doc_properties: "docs/source.properties",
+}
+
+droiddoc {
+ // Please sync with android-api-council@ before making any changes for the name property below.
+ // Since there's cron jobs that fetch offline-system-sdk-referenceonly-docs-docs.zip periodically.
+ // See b/116221385 for reference.
+ 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_only_args + " -hide 101 -hide 104 -hide 108" +
+ " -offlinemode -title \"Android System SDK\" -referenceonly",
+ static_doc_index_redirect: "docs/docs-documentation-redirect.html",
+ static_doc_properties: "docs/source.properties",
+}
+
+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_only_args +
+ " -toroot / -samplegroup Admin " +
+ " -samplegroup Background " +
+ " -samplegroup Connectivity " +
+ " -samplegroup Content " +
+ " -samplegroup Input " +
+ " -samplegroup Media " +
+ " -samplegroup Notification " +
+ " -samplegroup RenderScript " +
+ " -samplegroup Security " +
+ " -samplegroup Sensors " +
+ " -samplegroup System " +
+ " -samplegroup Testing " +
+ " -samplegroup UI " +
+ " -samplegroup Views " +
+ " -samplegroup Wearable -samplesdir development/samples/browseable ",
+}
+
+droiddoc {
+ name: "online-system-api-sdk-docs",
+ defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-system-stubs",
+ ],
+ hdf: [
+ "android.whichdoc online",
+ "android.hasSamples true",
+ ],
+ proofread_file: "online-system-api-sdk-docs-proofrerad.txt",
+ args: framework_docs_only_args +
+ " -referenceonly " +
+ " -title \"Android SDK - Including system APIs.\" " +
+ " -hide 101 " +
+ " -hide 104 " +
+ " -hide 108 " +
+ " -toroot / -samplegroup Admin " +
+ " -samplegroup Background " +
+ " -samplegroup Connectivity " +
+ " -samplegroup Content " +
+ " -samplegroup Input " +
+ " -samplegroup Media " +
+ " -samplegroup Notification " +
+ " -samplegroup RenderScript " +
+ " -samplegroup Security " +
+ " -samplegroup Sensors " +
+ " -samplegroup System " +
+ " -samplegroup Testing " +
+ " -samplegroup UI " +
+ " -samplegroup Views " +
+ " -samplegroup Wearable -samplesdir development/samples/browseable ",
+ installable: false,
+}
+
+droiddoc {
+ name: "ds-docs-java",
+ defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
+ hdf: [
+ "android.whichdoc online",
+ "android.hasSamples true",
+ ],
+ proofread_file: "ds-docs-proofrerad.txt",
+ args: framework_docs_only_args +
+ " -toroot / -yamlV2 -metalavaApiSince -samplegroup Admin " +
+ " -samplegroup Background " +
+ " -samplegroup Connectivity " +
+ " -samplegroup Content " +
+ " -samplegroup Input " +
+ " -samplegroup Media " +
+ " -samplegroup Notification " +
+ " -samplegroup RenderScript " +
+ " -samplegroup Security " +
+ " -samplegroup Sensors " +
+ " -samplegroup System " +
+ " -samplegroup Testing " +
+ " -samplegroup UI " +
+ " -samplegroup Views " +
+ " -samplegroup Wearable -devsite -samplesdir development/samples/browseable ",
+}
+
+droiddoc {
+ name: "ds-docs-kt",
+ defaults: ["framework-dokka-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
+ args: "-noJdkLink -links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list " +
+ "-noStdlibLink",
+ proofread_file: "ds-dokka-proofread.txt",
+ dokka_enabled: true,
+}
+
+java_genrule {
+ name: "ds-docs",
+ tools: [
+ "zip2zip",
+ "merge_zips",
+ ],
+ srcs: [
+ ":ds-docs-java{.docs.zip}",
+ ":ds-docs-kt{.docs.zip}",
+ ],
+ out: ["ds-docs.zip"],
+ dist: {
+ targets: ["docs"],
+ },
+ cmd: "$(location zip2zip) -i $(location :ds-docs-kt{.docs.zip}) -o $(genDir)/ds-docs-kt-moved.zip **/*:en/reference/kotlin && " +
+ "$(location merge_zips) $(out) $(location :ds-docs-java{.docs.zip}) $(genDir)/ds-docs-kt-moved.zip",
+}
+
+java_genrule {
+ name: "ds-docs-switched",
+ tools: [
+ "switcher4",
+ "soong_zip",
+ ],
+ srcs: [
+ ":ds-docs-java{.docs.zip}",
+ ":ds-docs-kt{.docs.zip}",
+ ],
+ out: ["ds-docs-switched.zip"],
+ dist: {
+ targets: ["docs"],
+ },
+ cmd: "unzip $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
+ "unzip $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
+ "SWITCHER=$$(cd $$(dirname $(location switcher4)) && pwd)/$$(basename $(location switcher4)) && " +
+ "(cd $(genDir)/en/reference && $$SWITCHER --work platform) && " +
+ "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
+}
+
+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_only_args +
+ " -staticonly " +
+ " -toroot / " +
+ " -devsite " +
+ " -ignoreJdLinks ",
+}
+
+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_only_args +
+ " -toroot / " +
+ " -atLinksNavtree " +
+ " -navtreeonly ",
+}
+
+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_only_args +
+ " -toroot / -samplegroup Admin " +
+ " -samplegroup Background " +
+ " -samplegroup Connectivity " +
+ " -samplegroup Content " +
+ " -samplegroup Input " +
+ " -samplegroup Media " +
+ " -samplegroup Notification " +
+ " -samplegroup RenderScript " +
+ " -samplegroup Security " +
+ " -samplegroup Sensors " +
+ " -samplegroup System " +
+ " -samplegroup Testing " +
+ " -samplegroup UI " +
+ " -samplegroup Views " +
+ " -samplegroup Wearable -samplesdir development/samples/browseable ",
+}
+
+droiddoc {
+ name: "hidden-docs",
+ defaults: ["framework-docs-default"],
+ srcs: [
+ ":framework-doc-stubs",
+ ],
+ proofread_file: "hidden-docs-proofrerad.txt",
+ args: framework_docs_only_args +
+ " -referenceonly " +
+ " -title \"Android SDK - Including hidden APIs.\"",
+}
+
diff --git a/StubLibraries.bp b/StubLibraries.bp
new file mode 100644
index 0000000..78f1b9c
--- /dev/null
+++ b/StubLibraries.bp
@@ -0,0 +1,340 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// How stubs are generated:
+//
+// raw source files --(metalava)--> stub source files --(javac)--> stub jar files
+//
+// The metalava conversion is done by droidstub modules *-api-stubs-docs.
+// The javac compilation is done by java_library modules android_*_stubs_current.
+// The metalava conversion is also responsible for creating API signature files
+// and comparing them against the last API signature in api/*-current.txt files
+// and also against the latest frozen API signature in prebuilts/sdk/*/*/api/android.txt
+// files.
+
+/////////////////////////////////////////////////////////////////////
+// Common metalava configs
+/////////////////////////////////////////////////////////////////////
+
+packages_to_document = [
+ "android",
+ "dalvik",
+ "java",
+ "javax",
+ "junit",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
+stubs_defaults {
+ name: "metalava-api-stubs-default",
+ srcs: [
+ ":framework-non-updatable-sources",
+ ":framework-updatable-sources",
+ "core/java/**/*.logtags",
+ ":opt-telephony-srcs",
+ ":opt-net-voip-srcs",
+ ":core-current-stubs-source",
+ ":core_public_api_files",
+ ":ike-api-srcs",
+ ],
+ libs: ["framework-internal-utils"],
+ installable: false,
+ annotations_enabled: true,
+ previous_api: ":last-released-public-api",
+ merge_annotations_dirs: [
+ "metalava-manual",
+ ],
+ api_levels_annotations_enabled: true,
+ api_levels_annotations_dirs: [
+ "sdk-dir",
+ "api-versions-jars-dir",
+ ],
+ sdk_version: "core_platform",
+ filter_packages: packages_to_document,
+}
+
+/////////////////////////////////////////////////////////////////////
+// *-api-stubs-docs modules providing source files for the stub libraries
+/////////////////////////////////////////////////////////////////////
+
+droidstubs {
+ name: "api-stubs-docs",
+ defaults: ["metalava-api-stubs-default"],
+ api_filename: "public_api.txt",
+ private_api_filename: "private.txt",
+ removed_api_filename: "removed.txt",
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ args: metalava_framework_docs_args,
+ check_api: {
+ current: {
+ api_file: "api/current.txt",
+ removed_api_file: "api/removed.txt",
+ },
+ last_released: {
+ api_file: ":last-released-public-api",
+ removed_api_file: "api/removed.txt",
+ baseline_file: ":public-api-incompatibilities-with-last-released",
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-public-api",
+ baseline_file: "api/lint-baseline.txt",
+ },
+ },
+ jdiff_enabled: true,
+}
+
+droidstubs {
+ name: "system-api-stubs-docs",
+ defaults: ["metalava-api-stubs-default"],
+ api_tag_name: "SYSTEM",
+ api_filename: "system-api.txt",
+ private_api_filename: "system-private.txt",
+ private_dex_api_filename: "system-private-dex.txt",
+ removed_api_filename: "system-removed.txt",
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
+ check_api: {
+ current: {
+ api_file: "api/system-current.txt",
+ removed_api_file: "api/system-removed.txt",
+ },
+ last_released: {
+ api_file: ":last-released-system-api",
+ removed_api_file: "api/system-removed.txt",
+ baseline_file: ":system-api-incompatibilities-with-last-released"
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-system-api",
+ baseline_file: "api/system-lint-baseline.txt",
+ },
+ },
+ jdiff_enabled: true,
+}
+
+droidstubs {
+ name: "test-api-stubs-docs",
+ defaults: ["metalava-api-stubs-default"],
+ api_tag_name: "TEST",
+ api_filename: "test-api.txt",
+ removed_api_filename: "test-removed.txt",
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ args: metalava_framework_docs_args + " --show-annotation android.annotation.TestApi",
+ check_api: {
+ current: {
+ api_file: "api/test-current.txt",
+ removed_api_file: "api/test-removed.txt",
+ },
+ api_lint: {
+ enabled: true,
+ baseline_file: "api/test-lint-baseline.txt",
+ },
+ },
+}
+
+/////////////////////////////////////////////////////////////////////
+// android_*_stubs_current modules are the stubs libraries compiled
+// from *-api-stubs-docs
+/////////////////////////////////////////////////////////////////////
+
+java_defaults {
+ name: "framework-stubs-default",
+ errorprone: {
+ javacflags: [
+ "-XepDisableAllChecks",
+ ],
+ },
+ java_resources: [
+ ":notices-for-framework-stubs",
+ ],
+ sdk_version: "core_current",
+ system_modules: "none",
+ java_version: "1.8",
+ compile_dex: true,
+}
+
+java_library_static {
+ name: "android_stubs_current",
+ srcs: [
+ ":api-stubs-docs",
+ ],
+ libs: [
+ "stub-annotations",
+ ],
+ static_libs: [
+ "private-stub-annotations-jar",
+ ],
+ defaults: ["framework-stubs-default"],
+}
+
+java_library_static {
+ name: "android_system_stubs_current",
+ srcs: [
+ ":system-api-stubs-docs",
+ ],
+ libs: [
+ "stub-annotations",
+ ],
+ static_libs: [
+ "private-stub-annotations-jar",
+ ],
+ defaults: ["framework-stubs-default"],
+}
+
+java_library_static {
+ name: "android_test_stubs_current",
+ srcs: [
+ ":test-api-stubs-docs",
+ ],
+ libs: [
+ "stub-annotations",
+ ],
+ static_libs: [
+ "private-stub-annotations-jar",
+ ],
+ defaults: ["framework-stubs-default"],
+}
+
+java_system_modules {
+ name: "android_stubs_current_system_modules",
+ libs: ["android_stubs_current"],
+}
+
+java_system_modules {
+ name: "android_system_stubs_current_system_modules",
+ libs: ["android_system_stubs_current"],
+}
+
+java_system_modules {
+ name: "android_test_stubs_current_system_modules",
+ libs: ["android_test_stubs_current"],
+}
+
+/////////////////////////////////////////////////////////////////////
+// hwbinder.stubs provides APIs required for building HIDL Java
+// libraries.
+/////////////////////////////////////////////////////////////////////
+
+droidstubs {
+ name: "hwbinder-stubs-docs",
+ srcs: [
+ "core/java/android/os/HidlSupport.java",
+ "core/java/android/annotation/IntDef.java",
+ "core/java/android/annotation/IntRange.java",
+ "core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/SystemApi.java",
+ "core/java/android/os/HidlMemory.java",
+ "core/java/android/os/HwBinder.java",
+ "core/java/android/os/HwBlob.java",
+ "core/java/android/os/HwParcel.java",
+ "core/java/android/os/IHwBinder.java",
+ "core/java/android/os/IHwInterface.java",
+ "core/java/android/os/DeadObjectException.java",
+ "core/java/android/os/DeadSystemException.java",
+ "core/java/android/os/NativeHandle.java",
+ "core/java/android/os/RemoteException.java",
+ "core/java/android/util/AndroidException.java",
+ ],
+ installable: false,
+ sdk_version: "core_platform",
+ annotations_enabled: true,
+ previous_api: ":last-released-public-api",
+ merge_annotations_dirs: [
+ "metalava-manual",
+ ],
+ args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
+}
+
+java_library_static {
+ name: "hwbinder.stubs",
+ sdk_version: "core_current",
+ srcs: [
+ ":hwbinder-stubs-docs",
+ ],
+}
+
+/////////////////////////////////////////////////////////////////////
+// Stubs for hiddenapi processing.
+/////////////////////////////////////////////////////////////////////
+
+droidstubs {
+ name: "hiddenapi-lists-docs",
+ defaults: ["metalava-api-stubs-default"],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ dex_api_filename: "public-dex.txt",
+ private_dex_api_filename: "private-dex.txt",
+ removed_dex_api_filename: "removed-dex.txt",
+ args: metalava_framework_docs_args +
+ " --show-unannotated " +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
+ " --show-annotation android.annotation.TestApi ",
+}
+
+droidstubs {
+ name: "hiddenapi-mappings",
+ defaults: ["metalava-api-stubs-default"],
+ srcs: [
+ ":opt-telephony-common-srcs",
+ ],
+
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
+ dex_mapping_filename: "dex-mapping.txt",
+ args: metalava_framework_docs_args +
+ " --hide ReferencesHidden " +
+ " --hide UnhiddenSystemApi " +
+ " --show-unannotated " +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
+ " --show-annotation android.annotation.TestApi ",
+}
+
+/////////////////////////////////////////////////////////////////////
+// api/*-current.txt files for use by modules in other directories
+// like the CTS test
+/////////////////////////////////////////////////////////////////////
+
+filegroup {
+ name: "frameworks-base-api-current.txt",
+ srcs: [
+ "api/current.txt",
+ ],
+}
+
+filegroup {
+ name: "frameworks-base-api-system-current.txt",
+ srcs: [
+ "api/system-current.txt",
+ ],
+}
+
+filegroup {
+ name: "frameworks-base-api-system-removed.txt",
+ srcs: [
+ "api/system-removed.txt",
+ ],
+}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 37b07a6..a2b0577 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -24,7 +24,7 @@
name: "framework-statsd",
installable: true,
// TODO(b/146209659): Use system_current instead.
- sdk_version: "core_platform",
+ sdk_version: "core_current",
srcs: [
":framework-statsd-sources",
],
diff --git a/api/current.txt b/api/current.txt
index d7176af..0589537 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1062,6 +1062,7 @@
field public static final int popupWindowStyle = 16842870; // 0x1010076
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
+ field public static final int preferMinimalPostProcessing = 16844300; // 0x101060c
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
@@ -3812,6 +3813,7 @@
method public void onPerformDirectAction(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration);
method @Deprecated public void onPictureInPictureModeChanged(boolean);
+ method public void onPictureInPictureRequested();
method @CallSuper protected void onPostCreate(@Nullable android.os.Bundle);
method public void onPostCreate(@Nullable android.os.Bundle, @Nullable android.os.PersistableBundle);
method @CallSuper protected void onPostResume();
@@ -5102,6 +5104,7 @@
method public void callActivityOnDestroy(android.app.Activity);
method public void callActivityOnNewIntent(android.app.Activity, android.content.Intent);
method public void callActivityOnPause(android.app.Activity);
+ method public void callActivityOnPictureInPictureRequested(@NonNull android.app.Activity);
method public void callActivityOnPostCreate(@NonNull android.app.Activity, @Nullable android.os.Bundle);
method public void callActivityOnPostCreate(@NonNull android.app.Activity, @Nullable android.os.Bundle, @Nullable android.os.PersistableBundle);
method public void callActivityOnRestart(android.app.Activity);
@@ -9973,6 +9976,7 @@
field public static final String DOWNLOAD_SERVICE = "download";
field public static final String DROPBOX_SERVICE = "dropbox";
field public static final String EUICC_SERVICE = "euicc";
+ field public static final String FILE_INTEGRITY_SERVICE = "file_integrity";
field public static final String FINGERPRINT_SERVICE = "fingerprint";
field public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final String INPUT_METHOD_SERVICE = "input_method";
@@ -11189,6 +11193,7 @@
field public String parentActivityName;
field public String permission;
field public int persistableMode;
+ field public boolean preferMinimalPostProcessing;
field public int screenOrientation;
field public int softInputMode;
field public String targetActivity;
@@ -23743,6 +23748,7 @@
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
+ field public static final int ENCODING_OPUS = 20; // 0x14
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
@@ -30152,6 +30158,21 @@
package android.net.wifi {
+ public abstract class EasyConnectStatusCallback {
+ field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
+ field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
+ field public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10; // 0xfffffff6
+ field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
+ field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11; // 0xfffffff5
+ field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; // 0xfffffff4
+ field public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; // 0xfffffff9
+ field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
+ field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
+ field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
+ field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
+ field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
+ }
+
public class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public int getWifiStandard();
@@ -39453,6 +39474,10 @@
field public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
field public static final String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
field public static final String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
+ field public static final String EXTRA_EASY_CONNECT_ATTEMPTED_SSID = "android.provider.extra.EASY_CONNECT_ATTEMPTED_SSID";
+ field public static final String EXTRA_EASY_CONNECT_BAND_LIST = "android.provider.extra.EASY_CONNECT_BAND_LIST";
+ field public static final String EXTRA_EASY_CONNECT_CHANNEL_LIST = "android.provider.extra.EASY_CONNECT_CHANNEL_LIST";
+ field public static final String EXTRA_EASY_CONNECT_ERROR_CODE = "android.provider.extra.EASY_CONNECT_ERROR_CODE";
field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
@@ -41388,6 +41413,11 @@
method public android.security.ConfirmationPrompt.Builder setPromptText(CharSequence);
}
+ public final class FileIntegrityManager {
+ method public boolean isApkVeritySupported();
+ method @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public boolean isAppSourceCertificateTrusted(@NonNull java.security.cert.X509Certificate) throws java.security.cert.CertificateEncodingException;
+ }
+
public final class KeyChain {
ctor public KeyChain();
method public static void choosePrivateKeyAlias(@NonNull android.app.Activity, @NonNull android.security.KeyChainAliasCallback, @Nullable String[], @Nullable java.security.Principal[], @Nullable String, int, @Nullable String);
@@ -44965,6 +44995,8 @@
field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
+ field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
+ field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array";
field public static final String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL = "mdn_is_additional_voicemail_number_bool";
field public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
field public static final String KEY_MMS_ALIAS_MAX_CHARS_INT = "aliasMaxChars";
@@ -45744,6 +45776,7 @@
method public byte[] getPdu();
method public int getProtocolIdentifier();
method public String getPseudoSubject();
+ method @Nullable public String getRecipientAddress();
method public String getServiceCenterAddress();
method public int getStatus();
method public int getStatusOnIcc();
@@ -49894,6 +49927,7 @@
method @Deprecated public float[] getSupportedRefreshRates();
method @Deprecated public int getWidth();
method public boolean isHdr();
+ method public boolean isMinimalPostProcessingSupported();
method public boolean isValid();
method public boolean isWideColorGamut();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
@@ -52926,6 +52960,7 @@
method public abstract void setNavigationBarColor(@ColorInt int);
method public void setNavigationBarContrastEnforced(boolean);
method public void setNavigationBarDividerColor(@ColorInt int);
+ method public void setPreferMinimalPostProcessing(boolean);
method public void setReenterTransition(android.transition.Transition);
method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
method public final void setRestrictedCaptionAreaListener(android.view.Window.OnRestrictedCaptionAreaChangedListener);
@@ -53313,6 +53348,7 @@
field public int layoutInDisplayCutoutMode;
field @Deprecated public int memoryType;
field public String packageName;
+ field public boolean preferMinimalPostProcessing;
field public int preferredDisplayModeId;
field @Deprecated public float preferredRefreshRate;
field public int rotationAnimation;
@@ -54325,6 +54361,7 @@
public static final class InlinePresentationSpec.Builder {
ctor public InlinePresentationSpec.Builder(@NonNull android.util.Size, @NonNull android.util.Size);
method @NonNull public android.view.inline.InlinePresentationSpec build();
+ method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@Nullable String);
}
}
@@ -55262,6 +55299,7 @@
method public int describeContents();
method @NonNull public android.os.Bundle getExtras();
method @NonNull public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
+ method @NonNull public String getText();
method public void writeToParcel(android.os.Parcel, int);
field public static final int APPLY_STRATEGY_IGNORE = 0; // 0x0
field public static final int APPLY_STRATEGY_REPLACE = 1; // 0x1
@@ -55288,6 +55326,7 @@
method @Nullable public android.os.LocaleList getDefaultLocales();
method @Nullable public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
method @NonNull public android.os.Bundle getExtras();
+ method @Nullable public java.time.ZonedDateTime getReferenceTime();
method @NonNull public CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.Request> CREATOR;
@@ -55299,6 +55338,7 @@
method @NonNull public android.view.textclassifier.TextLinks.Request.Builder setDefaultLocales(@Nullable android.os.LocaleList);
method @NonNull public android.view.textclassifier.TextLinks.Request.Builder setEntityConfig(@Nullable android.view.textclassifier.TextClassifier.EntityConfig);
method public android.view.textclassifier.TextLinks.Request.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.textclassifier.TextLinks.Request.Builder setReferenceTime(@Nullable java.time.ZonedDateTime);
}
public static final class TextLinks.TextLink implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index dfdcb19..d3f4ac4 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8,6 +8,7 @@
field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
field public static final String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
+ field public static final String ACCESS_MESSAGES_ON_ICC = "android.permission.ACCESS_MESSAGES_ON_ICC";
field public static final String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
field public static final String ACCESS_MTP = "android.permission.ACCESS_MTP";
field public static final String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
@@ -777,6 +778,7 @@
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserProvisioningState();
+ method public boolean hasDeviceIdentifierAccess(@NonNull String, int, int);
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
@@ -4066,6 +4068,7 @@
method public android.media.PlayerProxy getPlayerProxy();
method public int getPlayerState();
method public int getPlayerType();
+ method public boolean isActive();
field public static final int PLAYER_STATE_IDLE = 1; // 0x1
field public static final int PLAYER_STATE_PAUSED = 3; // 0x3
field public static final int PLAYER_STATE_RELEASED = 0; // 0x0
@@ -4728,6 +4731,13 @@
method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
}
+ public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+ ctor public MatchAllNetworkSpecifier();
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
+ }
+
public class Network implements android.os.Parcelable {
ctor public Network(@NonNull android.net.Network);
method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
@@ -5393,18 +5403,6 @@
method public void onFailure(int);
method public void onFailure(int, @Nullable String, @NonNull android.util.SparseArray<int[]>, @NonNull int[]);
method public abstract void onProgress(int);
- field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
- field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
- field public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10; // 0xfffffff6
- field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
- field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11; // 0xfffffff5
- field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; // 0xfffffff4
- field public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; // 0xfffffff9
- field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
- field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
- field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
- field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
- field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3; // 0x3
field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2; // 0x2
@@ -5610,11 +5608,19 @@
field public static final int KEY_MGMT_WAPI_PSK = 13; // 0xd
}
+ public final class SoftApCapability implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getMaxSupportedClients();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
+ }
+
public final class SoftApConfiguration implements android.os.Parcelable {
method public int describeContents();
method public int getBand();
method @Nullable public android.net.MacAddress getBssid();
method public int getChannel();
+ method public int getMaxNumberOfClients();
method public int getSecurityType();
method @Nullable public String getSsid();
method @Nullable public String getWpa2Passphrase();
@@ -5637,6 +5643,7 @@
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
}
@@ -5870,6 +5877,7 @@
field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1
field public static final int SAP_START_FAILURE_GENERAL = 0; // 0x0
field public static final int SAP_START_FAILURE_NO_CHANNEL = 1; // 0x1
+ field public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2; // 0x2
field public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED";
field public static final int WIFI_AP_STATE_DISABLED = 11; // 0xb
field public static final int WIFI_AP_STATE_DISABLING = 10; // 0xa
@@ -5908,6 +5916,7 @@
}
public static interface WifiManager.SoftApCallback {
+ method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability);
method public default void onConnectedClientsChanged(@NonNull java.util.List<android.net.wifi.WifiClient>);
method public default void onInfoChanged(@NonNull android.net.wifi.SoftApInfo);
method public default void onStateChanged(int, int);
@@ -8910,6 +8919,11 @@
field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
}
+ public static final class CarrierConfigManager.Wifi {
+ field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count";
+ field public static final String KEY_PREFIX = "wifi.";
+ }
+
public final class CarrierRestrictionRules implements android.os.Parcelable {
method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
method public int describeContents();
@@ -9620,6 +9634,7 @@
}
public final class PreciseDataConnectionState implements android.os.Parcelable {
+ ctor public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int, @Nullable android.telephony.data.ApnSetting);
method @Deprecated @NonNull public String getDataConnectionApn();
method @Deprecated public int getDataConnectionApnTypeBitMask();
method @Deprecated public int getDataConnectionFailCause();
@@ -9863,8 +9878,10 @@
}
public final class SmsManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int);
method public boolean disableCellBroadcastRange(int, int, int);
method public boolean enableCellBroadcastRange(int, int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
@@ -9890,6 +9907,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(boolean, int);
field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
@@ -9972,6 +9990,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
method public int getMaxNumberOfSimultaneouslyActiveSims();
method public static long getMaxNumberVerificationTimeoutMillis();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
diff --git a/api/test-current.txt b/api/test-current.txt
index 08a2160..3bf2a85 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -105,6 +105,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index afff614..887d17c 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -409,7 +409,7 @@
// ==== java proto device library (for test only) ==============================
java_library {
name: "statsdprotolite",
- sdk_version: "core_platform",
+ sdk_version: "core_current",
proto: {
type: "lite",
include_dirs: ["external/protobuf/src"],
diff --git a/core/java/android/annotation/CallbackExecutor.java b/core/java/android/annotation/CallbackExecutor.java
index 5671a3d..4258f73 100644
--- a/core/java/android/annotation/CallbackExecutor.java
+++ b/core/java/android/annotation/CallbackExecutor.java
@@ -19,9 +19,6 @@
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.content.Context;
-import android.os.AsyncTask;
-
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.concurrent.Executor;
@@ -30,9 +27,10 @@
* @paramDoc Callback and listener events are dispatched through this
* {@link Executor}, providing an easy way to control which thread is
* used. To dispatch events through the main thread of your
- * application, you can use {@link Context#getMainExecutor()}. To
- * dispatch events through a shared thread pool, you can use
- * {@link AsyncTask#THREAD_POOL_EXECUTOR}.
+ * application, you can use
+ * {@link android.content.Context#getMainExecutor() Context.getMainExecutor()}.
+ * To dispatch events through a shared thread pool, you can use
+ * {@link android.os.AsyncTask#THREAD_POOL_EXECUTOR AsyncTask#THREAD_POOL_EXECUTOR}.
* @hide
*/
@Retention(SOURCE)
diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java
index e5c0654..1d89e31 100644
--- a/core/java/android/annotation/RequiresPermission.java
+++ b/core/java/android/annotation/RequiresPermission.java
@@ -15,9 +15,6 @@
*/
package android.annotation;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
@@ -25,6 +22,9 @@
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
/**
* Denotes that the annotated element requires (or may require) one or more permissions.
* <p/>
@@ -55,7 +55,8 @@
* <p>
* When specified on a parameter, the annotation indicates that the method requires
* a permission which depends on the value of the parameter. For example, consider
- * {@link android.app.Activity#startActivity(android.content.Intent)}:
+ * {@link android.app.Activity#startActivity(android.content.Intent)
+ * Activity#startActivity(Intent)}:
* <pre>{@code
* public void startActivity(@RequiresPermission Intent intent) { ... }
* }</pre>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9e0c2fc..070a4f8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2837,6 +2837,17 @@
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
}
+ /**
+ * Called by the system when picture in picture mode should be entered if supported.
+ */
+ public void onPictureInPictureRequested() {
+ // Previous recommendation was for apps to enter picture-in-picture in onUserLeaveHint()
+ // which is sent after onPause(). This new method allows the system to request the app to
+ // go into picture-in-picture decoupling it from life cycle events. For backwards
+ // compatibility we schedule the life cycle events if the app didn't override this method.
+ mMainThread.schedulePauseAndReturnToCurrentState(mToken);
+ }
+
void dispatchMovedToDisplay(int displayId, Configuration config) {
updateDisplay(displayId);
onMovedToDisplay(displayId, config);
@@ -7871,6 +7882,7 @@
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
+ mWindow.setPreferMinimalPostProcessing(info.preferMinimalPostProcessing);
setAutofillOptions(application.getAutofillOptions());
setContentCaptureOptions(application.getContentCaptureOptions());
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 122004c..dd9a2bc 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -433,4 +434,18 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Requests that an activity should enter picture-in-picture mode if possible.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public void requestPictureInPictureMode(@NonNull IBinder token) {
+ try {
+ getService().requestPictureInPictureMode(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index be14556..08f8734 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -41,8 +41,10 @@
import android.app.servertransaction.ActivityResultItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.PendingTransactionActions.StopInfo;
+import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
import android.compat.annotation.UnsupportedAppUsage;
@@ -522,6 +524,8 @@
boolean startsNotResumed;
public final boolean isForward;
int pendingConfigChanges;
+ // Whether we are in the process of performing on user leaving.
+ boolean mIsUserLeaving;
Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
@@ -3763,6 +3767,66 @@
}
}
+ @Override
+ public void handlePictureInPictureRequested(IBinder token) {
+ final ActivityClientRecord r = mActivities.get(token);
+ if (r == null) {
+ Log.w(TAG, "Activity to request PIP to no longer exists");
+ return;
+ }
+
+ r.activity.onPictureInPictureRequested();
+ }
+
+ /**
+ * Cycle activity through onPause and onUserLeaveHint so that PIP is entered if supported, then
+ * return to its previous state. This allows activities that rely on onUserLeaveHint instead of
+ * onPictureInPictureRequested to enter picture-in-picture.
+ */
+ public void schedulePauseAndReturnToCurrentState(IBinder token) {
+ final ActivityClientRecord r = mActivities.get(token);
+ if (r == null) {
+ Log.w(TAG, "Activity to request pause with user leaving hint to no longer exists");
+ return;
+ }
+
+ if (r.mIsUserLeaving) {
+ // The activity is about to perform user leaving, so there's no need to cycle ourselves.
+ return;
+ }
+
+ final int prevState = r.getLifecycleState();
+ if (prevState != ON_RESUME && prevState != ON_PAUSE) {
+ return;
+ }
+
+ switch (prevState) {
+ case ON_RESUME:
+ // Schedule a PAUSE then return to RESUME.
+ schedulePauseWithUserLeavingHint(r);
+ scheduleResume(r);
+ break;
+ case ON_PAUSE:
+ // Schedule a RESUME then return to PAUSE.
+ scheduleResume(r);
+ schedulePauseWithUserLeavingHint(r);
+ break;
+ }
+ }
+
+ private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
+ final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+ transaction.setLifecycleStateRequest(PauseActivityItem.obtain(r.activity.isFinishing(),
+ /* userLeaving */ true, r.activity.mConfigChangeFlags, /* dontReport */ false));
+ executeTransaction(transaction);
+ }
+
+ private void scheduleResume(ActivityClientRecord r) {
+ final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+ transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(/* isForward */ false));
+ executeTransaction(transaction);
+ }
+
private void handleLocalVoiceInteractionStarted(IBinder token, IVoiceInteractor interactor) {
final ActivityClientRecord r = mActivities.get(token);
if (r != null) {
@@ -4483,6 +4547,7 @@
if (r != null) {
if (userLeaving) {
performUserLeavingActivity(r);
+ r.mIsUserLeaving = false;
}
r.activity.mConfigChangeFlags |= configChanges;
@@ -4497,6 +4562,8 @@
}
final void performUserLeavingActivity(ActivityClientRecord r) {
+ r.mIsUserLeaving = true;
+ mInstrumentation.callActivityOnPictureInPictureRequested(r.activity);
mInstrumentation.callActivityOnUserLeaving(r.activity);
}
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index d308adc..f9a689a 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -158,6 +158,9 @@
public abstract void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
Configuration overrideConfig);
+ /** Request that an activity enter picture-in-picture. */
+ public abstract void handlePictureInPictureRequested(IBinder token);
+
/** Update window visibility. */
public abstract void handleWindowVisibility(IBinder token, boolean show);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index df5d6c7..700b3c1 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -333,6 +333,7 @@
boolean isInPictureInPictureMode(in IBinder token);
boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
+ void requestPictureInPictureMode(in IBinder token);
int getMaxNumPictureInPictureActions(in IBinder token);
IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 9e552e6..62c905d 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1519,6 +1519,16 @@
public void callActivityOnUserLeaving(Activity activity) {
activity.performUserLeaving();
}
+
+ /**
+ * Perform calling of an activity's {@link Activity#onPictureInPictureRequested} method.
+ * The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being notified that picture-in-picture is being requested.
+ */
+ public void callActivityOnPictureInPictureRequested(@NonNull Activity activity) {
+ activity.onPictureInPictureRequested();
+ }
/*
* Starts allocation counting. This triggers a gc and resets the counts.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 69c37ec..31c73b9 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -50,6 +50,8 @@
import android.content.Context;
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
+import android.content.integrity.AppIntegrityManager;
+import android.content.integrity.IAppIntegrityManager;
import android.content.om.IOverlayManager;
import android.content.om.OverlayManager;
import android.content.pm.CrossProfileApps;
@@ -160,6 +162,8 @@
import android.permission.PermissionManager;
import android.print.IPrintManager;
import android.print.PrintManager;
+import android.security.FileIntegrityManager;
+import android.security.IFileIntegrityService;
import android.service.oemlock.IOemLockService;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.IPersistentDataBlockService;
@@ -1212,6 +1216,7 @@
return new DynamicSystemManager(
IDynamicSystemService.Stub.asInterface(b));
}});
+
registerService(Context.BATTERY_STATS_SERVICE, BatteryStatsManager.class,
new CachedServiceFetcher<BatteryStatsManager>() {
@Override
@@ -1245,7 +1250,26 @@
return new IncrementalManager(
IIncrementalManagerNative.Stub.asInterface(b));
}});
+
+ registerService(Context.FILE_INTEGRITY_SERVICE, FileIntegrityManager.class,
+ new CachedServiceFetcher<FileIntegrityManager>() {
+ @Override
+ public FileIntegrityManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.FILE_INTEGRITY_SERVICE);
+ return new FileIntegrityManager(
+ IFileIntegrityService.Stub.asInterface(b));
+ }});
//CHECKSTYLE:ON IndentationCheck
+ registerService(Context.APP_INTEGRITY_SERVICE, AppIntegrityManager.class,
+ new CachedServiceFetcher<AppIntegrityManager>() {
+ @Override
+ public AppIntegrityManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE);
+ return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b));
+ }});
sInitializing = true;
try {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3ca8f49..acdf919 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5061,12 +5061,17 @@
* owner. If Device ID attestation is requested (using {@link #ID_TYPE_SERIAL},
* {@link #ID_TYPE_IMEI} or {@link #ID_TYPE_MEID}), the caller must be the Device Owner
* or the Certificate Installer delegate.
- * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, if the
- * algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
- * or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the
- * {@code keySpec} does not contain an attestation challenge.
- * @throws UnsupportedOperationException if Device ID attestation was requested but the
- * underlying hardware does not support it.
+ * @throws IllegalArgumentException in the following cases:
+ * <p>
+ * <ul>
+ * <li>The alias in {@code keySpec} is empty.</li>
+ * <li>The algorithm specification in {@code keySpec} is not
+ * {@code RSAKeyGenParameterSpec} or {@code ECGenParameterSpec}.</li>
+ * <li>Device ID attestation was requested but the {@code keySpec} does not contain an
+ * attestation challenge.</li>
+ * </ul>
+ * @throws UnsupportedOperationException if Device ID attestation or individual attestation
+ * was requested but the underlying hardware does not support it.
* @throws StrongBoxUnavailableException if the use of StrongBox for key generation was
* specified in {@code keySpec} but the device does not have one.
* @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
@@ -6759,8 +6764,9 @@
*
* @hide
*/
- public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
- throwIfParentInstance("checkDeviceIdentifierAccess");
+ @SystemApi
+ public boolean hasDeviceIdentifierAccess(@NonNull String packageName, int pid, int uid) {
+ throwIfParentInstance("hasDeviceIdentifierAccess");
if (packageName == null) {
return false;
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 4d2e9a5..3d04437 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -77,8 +77,9 @@
/** Get the list of callbacks. */
@Nullable
+ @VisibleForTesting
@UnsupportedAppUsage
- List<ClientTransactionItem> getCallbacks() {
+ public List<ClientTransactionItem> getCallbacks() {
return mActivityCallbacks;
}
diff --git a/core/java/android/app/servertransaction/EnterPipRequestedItem.java b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
new file mode 100644
index 0000000..b2a1276
--- /dev/null
+++ b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+
+/**
+ * Request an activity to enter picture-in-picture mode.
+ * @hide
+ */
+public final class EnterPipRequestedItem extends ClientTransactionItem {
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ client.handlePictureInPictureRequested(token);
+ }
+
+ // ObjectPoolItem implementation
+
+ private EnterPipRequestedItem() {}
+
+ /** Obtain an instance initialized with provided params. */
+ public static EnterPipRequestedItem obtain() {
+ EnterPipRequestedItem instance = ObjectPool.obtain(EnterPipRequestedItem.class);
+ if (instance == null) {
+ instance = new EnterPipRequestedItem();
+ }
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) { }
+
+ public static final @android.annotation.NonNull Creator<EnterPipRequestedItem> CREATOR =
+ new Creator<EnterPipRequestedItem>() {
+ public EnterPipRequestedItem createFromParcel(Parcel in) {
+ return new EnterPipRequestedItem();
+ }
+
+ public EnterPipRequestedItem[] newArray(int size) {
+ return new EnterPipRequestedItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ return this == o;
+ }
+
+ @Override
+ public String toString() {
+ return "EnterPipRequestedItem{}";
+ }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 483bf3f..4815d78 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5045,6 +5045,14 @@
public static final String INCREMENTAL_SERVICE = "incremental_service";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.security.FileIntegrityManager}.
+ * @see #getSystemService(String)
+ * @see android.security.FileIntegrityManager
+ */
+ public static final String FILE_INTEGRITY_SERVICE = "file_integrity";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java
index c963475..70776c7 100644
--- a/core/java/android/content/integrity/AppInstallMetadata.java
+++ b/core/java/android/content/integrity/AppInstallMetadata.java
@@ -86,6 +86,19 @@
return mIsPreInstalled;
}
+ @Override
+ public String toString() {
+ return String.format(
+ "AppInstallMetadata { PackageName = %s, AppCert = %s, InstallerName = %s,"
+ + " InstallerCert = %s, VersionCode = %d, PreInstalled = %b }",
+ mPackageName,
+ mAppCertificate,
+ mInstallerName == null ? "null" : mInstallerName,
+ mInstallerCertificate == null ? "null" : mInstallerCertificate,
+ mVersionCode,
+ mIsPreInstalled);
+ }
+
/** Builder class for constructing {@link AppInstallMetadata} objects. */
public static final class Builder {
private String mPackageName;
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 5b1f926..e5daaca 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -290,6 +290,34 @@
public int colorMode = COLOR_MODE_DEFAULT;
/**
+ * Indicates whether the activity wants the connected display to do minimal post processing on
+ * the produced image or video frames. This will only be requested if this activity's main
+ * window is visible on the screen.
+ *
+ * <p>This setting should be used when low latency has a higher priority than image enhancement
+ * processing (e.g. for games or video conferencing).
+ *
+ * <p>If the Display sink is connected via HDMI, the device will begin to send infoframes with
+ * Auto Low Latency Mode enabled and Game Content Type. This will switch the connected display
+ * to a minimal image processing mode (if available), which reduces latency, improving the user
+ * experience for gaming or video conferencing applications. For more information, see HDMI 2.1
+ * specification.
+ *
+ * <p>If the Display sink has an internal connection or uses some other protocol than HDMI,
+ * effects may be similar but implementation-defined.
+ *
+ * <p>The ability to switch to a mode with minimal post proessing may be disabled by a user
+ * setting in the system settings menu. In that case, this field is ignored and the display will
+ * remain in its current mode.
+ *
+ * <p>Set from attribute {@link android.R.attr#preferMinimalPostProcessing}.
+ *
+ * @see android.view.WindowManager.LayoutParams#preferMinimalPostProcessing
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ */
+ public boolean preferMinimalPostProcessing = false;
+
+ /**
* Bit in {@link #flags} indicating whether this activity is able to
* run in multiple processes. If
* true, the system may instantiate it in the some process as the
@@ -1004,6 +1032,7 @@
requestedVrComponent = orig.requestedVrComponent;
rotationAnimation = orig.rotationAnimation;
colorMode = orig.colorMode;
+ preferMinimalPostProcessing = orig.preferMinimalPostProcessing;
maxAspectRatio = orig.maxAspectRatio;
minAspectRatio = orig.minAspectRatio;
}
@@ -1231,6 +1260,7 @@
dest.writeInt(colorMode);
dest.writeFloat(maxAspectRatio);
dest.writeFloat(minAspectRatio);
+ dest.writeBoolean(preferMinimalPostProcessing);
}
/**
@@ -1349,6 +1379,7 @@
colorMode = source.readInt();
maxAspectRatio = source.readFloat();
minAspectRatio = source.readFloat();
+ preferMinimalPostProcessing = source.readBoolean();
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index c955137..79a339f 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -24,7 +24,6 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -151,11 +150,15 @@
* has a preference.
* @param requestedModeId The preferred mode id for the top-most visible window that has a
* preference.
+ * @param requestedMinimalPostProcessing The preferred minimal post processing setting for the
+ * display. This is true when there is at least one visible window that wants minimal post
+ * processng on.
* @param inTraversal True if called from WindowManagerService during a window traversal
* prior to call to performTraversalInTransactionFromWindowManager.
*/
public abstract void setDisplayProperties(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedModeId, boolean inTraversal);
+ float requestedRefreshRate, int requestedModeId, boolean requestedMinimalPostProcessing,
+ boolean inTraversal);
/**
* Applies an offset to the contents of a display, for example to avoid burn-in.
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
index ab4f627..68a3935 100644
--- a/core/java/android/net/MatchAllNetworkSpecifier.java
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,10 +29,12 @@
*
* @hide
*/
+@SystemApi
public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements Parcelable {
/**
* Utility method which verifies that the ns argument is not a MatchAllNetworkSpecifier and
* throws an IllegalArgumentException if it is.
+ * @hide
*/
public static void checkNotMatchAllNetworkSpecifier(NetworkSpecifier ns) {
if (ns instanceof MatchAllNetworkSpecifier) {
@@ -38,6 +42,7 @@
}
}
+ /** @hide */
public boolean satisfiedBy(NetworkSpecifier other) {
/*
* The method is called by a NetworkRequest to see if it is satisfied by a proposed
@@ -64,11 +69,11 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
// Nothing to write.
}
- public static final @android.annotation.NonNull Parcelable.Creator<MatchAllNetworkSpecifier> CREATOR =
+ public static final @NonNull Parcelable.Creator<MatchAllNetworkSpecifier> CREATOR =
new Parcelable.Creator<MatchAllNetworkSpecifier>() {
public MatchAllNetworkSpecifier createFromParcel(Parcel in) {
return new MatchAllNetworkSpecifier();
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index ba4042d..39ddcb2 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -90,6 +90,15 @@
throw new RemoteException();
}
+ public void registerClientCallback(String name, IBinder service, IClientCallback cb)
+ throws RemoteException {
+ throw new RemoteException();
+ }
+
+ public void tryUnregisterService(String name, IBinder service) throws RemoteException {
+ throw new RemoteException();
+ }
+
/**
* Same as mServiceManager but used by apps.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 462627e..588bb18 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -494,25 +494,94 @@
* Activity Action: Show setting page to process an Easy Connect (Wi-Fi DPP) QR code and start
* configuration. This intent should be used when you want to use this device to take on the
* configurator role for an IoT/other device. When provided with a valid DPP URI string Settings
- * will open a wifi selection screen for the user to indicate which network they would like
- * to configure the device specified in the DPP URI string for and carry them through the rest
- * of the flow for provisioning the device.
+ * will open a wifi selection screen for the user to indicate which network they would like to
+ * configure the device specified in the DPP URI string for and carry them through the rest of
+ * the flow for provisioning the device.
* <p>
- * In some cases, a matching Activity may not exist, so ensure you safeguard
- * against this by checking WifiManager.isEasyConnectSupported();
+ * In some cases, a matching Activity may not exist, so ensure you safeguard against this by
+ * checking WifiManager.isEasyConnectSupported();
* <p>
* Input: The Intent's data URI specifies bootstrapping information for authenticating and
* provisioning the peer, with the "DPP" scheme.
* <p>
* Output: After {@code startActivityForResult}, the callback {@code onActivityResult} will have
- * resultCode {@link android.app.Activity#RESULT_OK} if Wi-Fi Easy Connect configuration
- * success and the user clicks 'Done' button.
+ * resultCode {@link android.app.Activity#RESULT_OK} if Wi-Fi Easy Connect configuration succeeded
+ * and the user tapped 'Done' button, or {@link android.app.Activity#RESULT_CANCELED} if operation
+ * failed and user tapped 'Cancel'. In case the operation has failed, a status code from {@link
+ * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode} will be returned as
+ * Extra {@link #EXTRA_EASY_CONNECT_ERROR_CODE}. Easy Connect R2 Enrollees report additional
+ * details about the error they encountered, which will be provided in the {@link
+ * #EXTRA_EASY_CONNECT_ATTEMPTED_SSID}, {@link #EXTRA_EASY_CONNECT_CHANNEL_LIST}, and {@link
+ * #EXTRA_EASY_CONNECT_BAND_LIST}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI =
"android.settings.PROCESS_WIFI_EASY_CONNECT_URI";
/**
+ * Activity Extra: The Easy Connect operation error code
+ * <p>
+ * An extra returned on the result intent received when using the {@link
+ * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
+ * extra contains the error code of the operation - one of
+ * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode}.
+ * If there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK},
+ * then this extra is not attached to the result intent.
+ */
+ public static final String EXTRA_EASY_CONNECT_ERROR_CODE =
+ "android.provider.extra.EASY_CONNECT_ERROR_CODE";
+
+ /**
+ * Activity Extra: The SSID that the Enrollee tried to connect to.
+ * <p>
+ * An extra returned on the result intent received when using the {@link
+ * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
+ * extra contains the SSID of the Access Point that the remote Enrollee tried to connect to.
+ * This value is populated only by remote R2 devices, and only for the following error codes:
+ * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
+ * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}.
+ * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
+ * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
+ * this extra is not attached to the result intent.
+ */
+ public static final String EXTRA_EASY_CONNECT_ATTEMPTED_SSID =
+ "android.provider.extra.EASY_CONNECT_ATTEMPTED_SSID";
+
+ /**
+ * Activity Extra: The Channel List that the Enrollee used to scan a network.
+ * <p>
+ * An extra returned on the result intent received when using the {@link
+ * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
+ * extra contains the list channels the Enrollee used to scan for a network. This value is
+ * populated only by remote R2 devices, and only for the following error code: {@link
+ * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}.
+ * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
+ * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
+ * this extra is not attached to the result intent. The list is JSON formatted, as an array
+ * (Wi-Fi global operating classes) of arrays (Wi-Fi channels).
+ */
+ public static final String EXTRA_EASY_CONNECT_CHANNEL_LIST =
+ "android.provider.extra.EASY_CONNECT_CHANNEL_LIST";
+
+ /**
+ * Activity Extra: The Band List that the Enrollee supports.
+ * <p>
+ * An extra returned on the result intent received when using the {@link
+ * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
+ * extra contains the bands the Enrollee supports, expressed as the Global Operating Class,
+ * see Table E-4 in IEEE Std 802.11-2016 -Global operating classes. This value is populated only
+ * by remote R2 devices, and only for the following error codes: {@link
+ * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
+ * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}
+ * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION}.
+ * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
+ * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
+ * this extra is not attached to the result intent.
+ */
+ public static final String EXTRA_EASY_CONNECT_BAND_LIST =
+ "android.provider.extra.EASY_CONNECT_BAND_LIST";
+
+ /**
* Activity Action: Show settings to allow configuration of data and view data usage.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
new file mode 100644
index 0000000..cdd6584
--- /dev/null
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.security;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+/**
+ * This class provides access to file integrity related operations.
+ */
+@SystemService(Context.FILE_INTEGRITY_SERVICE)
+public final class FileIntegrityManager {
+ @NonNull private final IFileIntegrityService mService;
+
+ /** @hide */
+ public FileIntegrityManager(@NonNull IFileIntegrityService service) {
+ mService = service;
+ }
+
+ /**
+ * Returns true if APK Verity is supported on the device. When supported, an APK can be
+ * installed with a fs-verity signature (if verified with trusted App Source Certificate) for
+ * continuous on-access verification.
+ */
+ public boolean isApkVeritySupported() {
+ try {
+ // Go through the service just to avoid exposing the vendor controlled system property
+ // to all apps.
+ return mService.isApkVeritySupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the given certificate can be used to prove app's install source. Always
+ * return false if the feature is not supported.
+ *
+ * <p>A store can use this API to decide if a signature file needs to be downloaded. Also, if a
+ * store has shipped different certificates before (e.g. with stronger and weaker key), it can
+ * also use this API to download the best signature on the running device.
+ *
+ * @return whether the certificate is trusted in the system
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INSTALL_PACKAGES,
+ android.Manifest.permission.REQUEST_INSTALL_PACKAGES
+ })
+ public boolean isAppSourceCertificateTrusted(@NonNull X509Certificate certificate)
+ throws CertificateEncodingException {
+ try {
+ return mService.isAppSourceCertificateTrusted(certificate.getEncoded());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/security/IFileIntegrityService.aidl b/core/java/android/security/IFileIntegrityService.aidl
new file mode 100644
index 0000000..ebb8bcb
--- /dev/null
+++ b/core/java/android/security/IFileIntegrityService.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.security;
+
+/**
+ * Binder interface to communicate with FileIntegrityService.
+ * @hide
+ */
+interface IFileIntegrityService {
+ boolean isApkVeritySupported();
+ boolean isAppSourceCertificateTrusted(in byte[] certificateBytes);
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index b555d20..4368115 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -876,6 +877,31 @@
}
/**
+ * <p> Returns true if the connected display can be switched into a mode with minimal
+ * post processing. </p>
+ *
+ * <p> If the Display sink is connected via HDMI, this method will return true if the
+ * display supports either Auto Low Latency Mode or Game Content Type.
+ *
+ * <p> If the Display sink has an internal connection or uses some other protocol than
+ * HDMI, this method will return true if the sink can be switched into an
+ * implementation-defined low latency image processing mode. </p>
+ *
+ * <p> The ability to switch to a mode with minimal post processing may be disabled
+ * by a user setting in the system settings menu. In that case, this method returns
+ * false. </p>
+ *
+ * @see android.view.Window#setPreferMinimalPostProcessing
+ */
+ @SuppressLint("VisiblySynchronized")
+ public boolean isMinimalPostProcessingSupported() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.minimalPostProcessingSupported;
+ }
+ }
+
+ /**
* Request the display applies a color mode.
* @hide
*/
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a5385d..7d455c9 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -184,6 +184,14 @@
public Display.HdrCapabilities hdrCapabilities;
/**
+ * Indicates whether the display can be switched into a mode with minimal post
+ * processing.
+ *
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ */
+ public boolean minimalPostProcessingSupported;
+
+ /**
* The logical display density which is the basis for density-independent
* pixels.
*/
@@ -305,6 +313,7 @@
&& colorMode == other.colorMode
&& Arrays.equals(supportedColorModes, other.supportedColorModes)
&& Objects.equals(hdrCapabilities, other.hdrCapabilities)
+ && minimalPostProcessingSupported == other.minimalPostProcessingSupported
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
&& physicalYDpi == other.physicalYDpi
@@ -346,6 +355,7 @@
supportedColorModes = Arrays.copyOf(
other.supportedColorModes, other.supportedColorModes.length);
hdrCapabilities = other.hdrCapabilities;
+ minimalPostProcessingSupported = other.minimalPostProcessingSupported;
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
@@ -388,6 +398,7 @@
supportedColorModes[i] = source.readInt();
}
hdrCapabilities = source.readParcelable(null);
+ minimalPostProcessingSupported = source.readBoolean();
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
@@ -430,6 +441,7 @@
dest.writeInt(supportedColorModes[i]);
}
dest.writeParcelable(hdrCapabilities, flags);
+ dest.writeBoolean(minimalPostProcessingSupported);
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 5674de8..c67ff6e 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -25,6 +25,7 @@
import dalvik.system.CloseGuard;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
/**
@@ -86,6 +87,7 @@
/**
* Disposes the receiver.
+ * Must be called on the same Looper thread to which the receiver is attached.
*/
public void dispose() {
dispose(false);
@@ -109,6 +111,7 @@
mInputChannel = null;
}
mMessageQueue = null;
+ Reference.reachabilityFence(this);
}
/**
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index bcf5c78..7be3e6a 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -39,6 +39,17 @@
mSurfacePosition = surfacePosition;
}
+ public InsetsSourceControl(InsetsSourceControl other) {
+ mType = other.mType;
+ if (other.mLeash != null) {
+ mLeash = new SurfaceControl();
+ mLeash.copyFrom(other.mLeash);
+ } else {
+ mLeash = null;
+ }
+ mSurfacePosition = new Point(other.mSurfacePosition);
+ }
+
public int getType() {
return mType;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index c566eae..f6d6522 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -169,6 +169,8 @@
private static native int nativeGetActiveColorMode(IBinder displayToken);
private static native boolean nativeSetActiveColorMode(IBinder displayToken,
int colorMode);
+ private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on);
+ private static native void nativeSetGameContentType(IBinder displayToken, boolean on);
private static native void nativeSetDisplayPowerMode(
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
@@ -186,6 +188,9 @@
private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
+ private static native boolean nativeGetAutoLowLatencyModeSupport(IBinder displayToken);
+ private static native boolean nativeGetGameContentTypeSupport(IBinder displayToken);
+
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
@@ -441,10 +446,16 @@
public static final int METADATA_TASK_ID = 3;
/**
+ * The style of mouse cursor and hotspot.
+ * @hide
+ */
+ public static final int METADATA_MOUSE_CURSOR = 4;
+
+ /**
* Accessibility ID to allow association between surfaces and accessibility tree.
* @hide
*/
- public static final int METADATA_ACCESSIBILITY_ID = 4;
+ public static final int METADATA_ACCESSIBILITY_ID = 5;
/**
* A wrapper around GraphicBuffer that contains extra information about how to
@@ -1683,6 +1694,28 @@
/**
* @hide
*/
+ public static void setAutoLowLatencyMode(IBinder displayToken, boolean on) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ nativeSetAutoLowLatencyMode(displayToken, on);
+ }
+
+ /**
+ * @hide
+ */
+ public static void setGameContentType(IBinder displayToken, boolean on) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ nativeSetGameContentType(displayToken, on);
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage
public static void setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
@@ -1734,6 +1767,28 @@
/**
* @hide
*/
+ public static boolean getAutoLowLatencyModeSupport(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ return nativeGetAutoLowLatencyModeSupport(displayToken);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean getGameContentTypeSupport(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ return nativeGetGameContentTypeSupport(displayToken);
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage
public static IBinder createDisplay(String name, boolean secure) {
if (name == null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3bab9eb..9fd3464 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7619,11 +7619,22 @@
}
private void dispatchInsetsChanged(InsetsState insetsState) {
+ if (Binder.getCallingPid() == android.os.Process.myPid()) {
+ insetsState = new InsetsState(insetsState, true /* copySource */);
+ }
mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
}
private void dispatchInsetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
+ if (Binder.getCallingPid() == android.os.Process.myPid()) {
+ insetsState = new InsetsState(insetsState, true /* copySource */);
+ if (activeControls != null) {
+ for (int i = activeControls.length - 1; i >= 0; i--) {
+ activeControls[i] = new InsetsSourceControl(activeControls[i]);
+ }
+ }
+ }
SomeArgs args = SomeArgs.obtain();
args.arg1 = insetsState;
args.arg2 = activeControls;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0776469..a1894f3 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1199,6 +1199,44 @@
}
/**
+ * If {@code isPreferred} is true, this method requests that the connected display does minimal
+ * post processing when this window is visible on the screen. Otherwise, it requests that the
+ * display switches back to standard image processing.
+ *
+ * <p> By default, the display does not do minimal post processing and if this is desired, this
+ * method should not be used. It should be used with {@code isPreferred=true} when low
+ * latency has a higher priority than image enhancement processing (e.g. for games or video
+ * conferencing). The display will automatically go back into standard image processing mode
+ * when no window requesting minimal posst processing is visible on screen anymore.
+ * {@code setPreferMinimalPostProcessing(false)} can be used if
+ * {@code setPreferMinimalPostProcessing(true)} was previously called for this window and
+ * minimal post processing is no longer required.
+ *
+ * <p>If the Display sink is connected via HDMI, the device will begin to send infoframes with
+ * Auto Low Latency Mode enabled and Game Content Type. This will switch the connected display
+ * to a minimal image processing mode (if available), which reduces latency, improving the user
+ * experience for gaming or video conferencing applications. For more information, see HDMI 2.1
+ * specification.
+ *
+ * <p>If the Display sink has an internal connection or uses some other protocol than HDMI,
+ * effects may be similar but implementation-defined.
+ *
+ * <p>The ability to switch to a mode with minimal post proessing may be disabled by a user
+ * setting in the system settings menu. In that case, this method does nothing.
+ *
+ * @see android.content.pm.ActivityInfo#preferMinimalPostProcessing
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ * @see android.view.WindowManager.LayoutParams#preferMinimalPostProcessing
+ *
+ * @param isPreferred Indicates whether minimal post processing is preferred for this window
+ * ({@code isPreferred=true}) or not ({@code isPreferred=false}).
+ */
+ public void setPreferMinimalPostProcessing(boolean isPreferred) {
+ mWindowAttributes.preferMinimalPostProcessing = isPreferred;
+ dispatchWindowAttributesChanged(mWindowAttributes);
+ }
+
+ /**
* Returns the requested color mode of the window, one of
* {@link ActivityInfo#COLOR_MODE_DEFAULT}, {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT}
* or {@link ActivityInfo#COLOR_MODE_HDR}. If {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index fc12639..f7d9706 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2607,6 +2607,33 @@
public long hideTimeoutMilliseconds = -1;
/**
+ * Indicates whether this window wants the connected display to do minimal post processing
+ * on the produced image or video frames. This will only be requested if the window is
+ * visible on the screen.
+ *
+ * <p>This setting should be used when low latency has a higher priority than image
+ * enhancement processing (e.g. for games or video conferencing).
+ *
+ * <p>If the Display sink is connected via HDMI, the device will begin to send infoframes
+ * with Auto Low Latency Mode enabled and Game Content Type. This will switch the connected
+ * display to a minimal image processing mode (if available), which reduces latency,
+ * improving the user experience for gaming or video conferencing applications. For more
+ * information, see HDMI 2.1 specification.
+ *
+ * <p>If the Display sink has an internal connection or uses some other protocol than HDMI,
+ * effects may be similar but implementation-defined.
+ *
+ * <p>The ability to switch to a mode with minimal post proessing may be disabled by a user
+ * setting in the system settings menu. In that case, this field is ignored and the display
+ * will remain in its current mode.
+ *
+ * @see android.content.pm.ActivityInfo#preferMinimalPostProcessing
+ * @see android.view.Display#isMinimalPostProcessingSupported
+ * @see android.view.Window#setPreferMinimalPostProcessing
+ */
+ public boolean preferMinimalPostProcessing = false;
+
+ /**
* The color mode requested by this window. The target display may
* not be able to honor the request. When the color mode is not set
* to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2909,6 +2936,7 @@
out.writeInt(mFitWindowInsetsTypes);
out.writeInt(mFitWindowInsetsSides);
out.writeBoolean(mFitIgnoreVisibility);
+ out.writeBoolean(preferMinimalPostProcessing);
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -2969,6 +2997,7 @@
mFitWindowInsetsTypes = in.readInt();
mFitWindowInsetsSides = in.readInt();
mFitIgnoreVisibility = in.readBoolean();
+ preferMinimalPostProcessing = in.readBoolean();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -3014,6 +3043,8 @@
public static final int COLOR_MODE_CHANGED = 1 << 26;
/** {@hide} */
public static final int INSET_FLAGS_CHANGED = 1 << 27;
+ /** {@hide} */
+ public static final int MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED = 1 << 28;
// internal buffer to backup/restore parameters under compatibility mode.
private int[] mCompatibilityParamsBackup = null;
@@ -3194,6 +3225,11 @@
changes |= COLOR_MODE_CHANGED;
}
+ if (preferMinimalPostProcessing != o.preferMinimalPostProcessing) {
+ preferMinimalPostProcessing = o.preferMinimalPostProcessing;
+ changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
+ }
+
// This can't change, it's only set at window creation time.
hideTimeoutMilliseconds = o.hideTimeoutMilliseconds;
@@ -3348,6 +3384,10 @@
if (mColorMode != COLOR_MODE_DEFAULT) {
sb.append(" colorMode=").append(ActivityInfo.colorModeToString(mColorMode));
}
+ if (preferMinimalPostProcessing) {
+ sb.append(" preferMinimalPostProcessing=");
+ sb.append(preferMinimalPostProcessing);
+ }
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
ViewDebug.flagsToString(LayoutParams.class, "flags", flags));
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index 1eddef5..a85b5f1 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -31,6 +31,7 @@
*/
@DataClass(genEqualsHashCode = true, genToString = true, genBuilder = true)
public final class InlinePresentationSpec implements Parcelable {
+
/** The minimal size of the suggestion. */
@NonNull
private final Size mMinSize;
@@ -38,7 +39,26 @@
@NonNull
private final Size mMaxSize;
- // TODO(b/137800469): add more attributes, such as text appearance info.
+ /**
+ * The fully qualified resource name of the UI style resource identifier, defaults to {@code
+ * null}.
+ *
+ * <p> The value can be obtained by calling {@code Resources#getResourceName(int)}.
+ */
+ @Nullable
+ private final String mStyle;
+
+ private static String defaultStyle() {
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public @Nullable String getStyle() {
+ return mStyle;
+ }
+
/** @hide */
@DataClass.Suppress({"setMaxSize", "setMinSize"})
@@ -63,13 +83,15 @@
@DataClass.Generated.Member
/* package-private */ InlinePresentationSpec(
@NonNull Size minSize,
- @NonNull Size maxSize) {
+ @NonNull Size maxSize,
+ @Nullable String style) {
this.mMinSize = minSize;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMinSize);
this.mMaxSize = maxSize;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMaxSize);
+ this.mStyle = style;
// onConstructed(); // You can define this method to get a callback
}
@@ -98,7 +120,8 @@
return "InlinePresentationSpec { " +
"minSize = " + mMinSize + ", " +
- "maxSize = " + mMaxSize +
+ "maxSize = " + mMaxSize + ", " +
+ "style = " + mStyle +
" }";
}
@@ -116,7 +139,8 @@
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mMinSize, that.mMinSize)
- && java.util.Objects.equals(mMaxSize, that.mMaxSize);
+ && java.util.Objects.equals(mMaxSize, that.mMaxSize)
+ && java.util.Objects.equals(mStyle, that.mStyle);
}
@Override
@@ -128,6 +152,7 @@
int _hash = 1;
_hash = 31 * _hash + java.util.Objects.hashCode(mMinSize);
_hash = 31 * _hash + java.util.Objects.hashCode(mMaxSize);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mStyle);
return _hash;
}
@@ -137,8 +162,12 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (mStyle != null) flg |= 0x4;
+ dest.writeByte(flg);
dest.writeSize(mMinSize);
dest.writeSize(mMaxSize);
+ if (mStyle != null) dest.writeString(mStyle);
}
@Override
@@ -152,8 +181,10 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
Size minSize = (Size) in.readSize();
Size maxSize = (Size) in.readSize();
+ String style = (flg & 0x4) == 0 ? null : in.readString();
this.mMinSize = minSize;
com.android.internal.util.AnnotationValidations.validate(
@@ -161,6 +192,7 @@
this.mMaxSize = maxSize;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMaxSize);
+ this.mStyle = style;
// onConstructed(); // You can define this method to get a callback
}
@@ -188,6 +220,7 @@
private @NonNull Size mMinSize;
private @NonNull Size mMaxSize;
+ private @Nullable String mStyle;
private long mBuilderFieldsSet = 0L;
@@ -210,19 +243,37 @@
NonNull.class, null, mMaxSize);
}
+ /**
+ * The fully qualified resource name of the UI style resource identifier, defaults to {@code
+ * null}.
+ *
+ * <p> The value can be obtained by calling {@code Resources#getResourceName(int)}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setStyle(@Nullable String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mStyle = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlinePresentationSpec build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
+ mBuilderFieldsSet |= 0x8; // Mark builder used
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mStyle = defaultStyle();
+ }
InlinePresentationSpec o = new InlinePresentationSpec(
mMinSize,
- mMaxSize);
+ mMaxSize,
+ mStyle);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x4) != 0) {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -230,10 +281,10 @@
}
@DataClass.Generated(
- time = 1574406062532L,
+ time = 1577145109444L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
- inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static java.lang.String defaultStyle()\npublic @android.annotation.Nullable java.lang.String getStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index dd609ee..386c9cb 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -36,7 +36,17 @@
/** Constant used to indicate not putting a cap on the number of suggestions to return. */
public static final int SUGGESTION_COUNT_UNLIMITED = Integer.MAX_VALUE;
+ /**
+ * Max number of suggestions expected from the response. Defaults to {@code
+ * SUGGESTION_COUNT_UNLIMITED} if not set.
+ */
private final int mMaxSuggestionCount;
+
+ /**
+ * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
+ * count is larger than the number of specs in the list, then the last spec is used for the
+ * remainder of the suggestions.
+ */
private final @NonNull List<InlinePresentationSpec> mPresentationSpecs;
private void onConstructed() {
@@ -79,11 +89,20 @@
onConstructed();
}
+ /**
+ * Max number of suggestions expected from the response. Defaults to {@code
+ * SUGGESTION_COUNT_UNLIMITED} if not set.
+ */
@DataClass.Generated.Member
public int getMaxSuggestionCount() {
return mMaxSuggestionCount;
}
+ /**
+ * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
+ * count is larger than the number of specs in the list, then the last spec is used for the
+ * remainder of the suggestions.
+ */
@DataClass.Generated.Member
public @NonNull List<InlinePresentationSpec> getPresentationSpecs() {
return mPresentationSpecs;
@@ -189,6 +208,14 @@
private long mBuilderFieldsSet = 0L;
+ /**
+ * Creates a new Builder.
+ *
+ * @param presentationSpecs
+ * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
+ * count is larger than the number of specs in the list, then the last spec is used for the
+ * remainder of the suggestions.
+ */
public Builder(
@NonNull List<InlinePresentationSpec> presentationSpecs) {
mPresentationSpecs = presentationSpecs;
@@ -196,6 +223,10 @@
NonNull.class, null, mPresentationSpecs);
}
+ /**
+ * Max number of suggestions expected from the response. Defaults to {@code
+ * SUGGESTION_COUNT_UNLIMITED} if not set.
+ */
@DataClass.Generated.Member
public @NonNull Builder setMaxSuggestionCount(int value) {
checkNotUsed();
@@ -204,6 +235,11 @@
return this;
}
+ /**
+ * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
+ * count is larger than the number of specs in the list, then the last spec is used for the
+ * remainder of the suggestions.
+ */
@DataClass.Generated.Member
@Override
@NonNull Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value) {
@@ -247,7 +283,7 @@
}
@DataClass.Generated(
- time = 1574406255024L,
+ time = 1576637222199L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nclass BaseBuilder extends java.lang.Object implements []")
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 237d1a9..1aa2aec 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -42,6 +42,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -110,7 +111,6 @@
/**
* Returns the text that was used to generate these links.
- * @hide
*/
@NonNull
public String getText() {
@@ -342,6 +342,7 @@
private final boolean mLegacyFallback;
@Nullable private String mCallingPackageName;
private final Bundle mExtras;
+ @Nullable private final ZonedDateTime mReferenceTime;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
@@ -350,11 +351,13 @@
LocaleList defaultLocales,
EntityConfig entityConfig,
boolean legacyFallback,
+ ZonedDateTime referenceTime,
Bundle extras) {
mText = text;
mDefaultLocales = defaultLocales;
mEntityConfig = entityConfig;
mLegacyFallback = legacyFallback;
+ mReferenceTime = referenceTime;
mExtras = extras;
}
@@ -395,6 +398,15 @@
}
/**
+ * @return reference time based on which relative dates (e.g. "tomorrow") should be
+ * interpreted.
+ */
+ @Nullable
+ public ZonedDateTime getReferenceTime() {
+ return mReferenceTime;
+ }
+
+ /**
* Sets the name of the package that is sending this request.
* <p>
* Package-private for SystemTextClassifier's use.
@@ -454,6 +466,7 @@
@Nullable private EntityConfig mEntityConfig;
private boolean mLegacyFallback = true; // Use legacy fall back by default.
@Nullable private Bundle mExtras;
+ @Nullable private ZonedDateTime mReferenceTime;
public Builder(@NonNull CharSequence text) {
mText = Objects.requireNonNull(text);
@@ -511,13 +524,26 @@
}
/**
+ * @param referenceTime reference time based on which relative dates (e.g. "tomorrow"
+ * should be interpreted. This should usually be the time when the text was
+ * originally composed.
+ *
+ * @return this builder
+ */
+ @NonNull
+ public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
+ mReferenceTime = referenceTime;
+ return this;
+ }
+
+ /**
* Builds and returns the request object.
*/
@NonNull
public Request build() {
return new Request(
mText, mDefaultLocales, mEntityConfig,
- mLegacyFallback,
+ mLegacyFallback, mReferenceTime,
mExtras == null ? Bundle.EMPTY : mExtras);
}
}
@@ -535,6 +561,7 @@
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
+ dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
}
private static Request readFromParcel(Parcel in) {
@@ -544,9 +571,12 @@
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extras = in.readBundle();
+ final String referenceTimeString = in.readString();
+ final ZonedDateTime referenceTime = referenceTimeString == null
+ ? null : ZonedDateTime.parse(referenceTimeString);
final Request request = new Request(text, defaultLocales, entityConfig,
- /* legacyFallback= */ true, extras);
+ /* legacyFallback= */ true, referenceTime, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
return request;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 392e67e..a86b566 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1439,20 +1439,22 @@
final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
- // Stacked apps get a disambiguation first
if (targetInfo instanceof MultiDisplayResolveInfo) {
MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
- CharSequence[] labels = new CharSequence[mti.getTargets().size()];
- int i = 0;
- for (TargetInfo ti : mti.getTargets()) {
- labels[i++] = ti.getResolveInfo().loadLabel(getPackageManager());
- }
- ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(
- targetInfo.getDisplayLabel(),
- ((MultiDisplayResolveInfo) targetInfo).getTargets(), labels);
+ if (!mti.hasSelected()) {
+ // Stacked apps get a disambiguation first
+ CharSequence[] labels = new CharSequence[mti.getTargets().size()];
+ int i = 0;
+ for (TargetInfo ti : mti.getTargets()) {
+ labels[i++] = ti.getResolveInfo().loadLabel(getPackageManager());
+ }
+ ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(
+ targetInfo.getDisplayLabel(),
+ ((MultiDisplayResolveInfo) targetInfo), labels, which);
- f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
- return;
+ f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
+ return;
+ }
}
super.startSelected(which, always, filtered);
diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
index ff6582d..f4c69a5 100644
--- a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
@@ -24,10 +24,7 @@
import android.content.res.Configuration;
import android.os.Bundle;
-import com.android.internal.app.chooser.DisplayResolveInfo;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.android.internal.app.chooser.MultiDisplayResolveInfo;
/**
* Shows individual actions for a "stacked" app target - such as an app with multiple posting
@@ -38,24 +35,20 @@
private static final String TITLE_KEY = "title";
private static final String PINNED_KEY = "pinned";
- private List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+ private MultiDisplayResolveInfo mTargetInfos;
private CharSequence[] mLabels;
+ private int mParentWhich;
public ChooserStackedAppDialogFragment() {
}
- public ChooserStackedAppDialogFragment(CharSequence title) {
- Bundle args = new Bundle();
- args.putCharSequence(TITLE_KEY, title);
- setArguments(args);
- }
-
public ChooserStackedAppDialogFragment(CharSequence title,
- List<DisplayResolveInfo> targets, CharSequence[] labels) {
+ MultiDisplayResolveInfo targets, CharSequence[] labels, int parentWhich) {
Bundle args = new Bundle();
args.putCharSequence(TITLE_KEY, title);
mTargetInfos = targets;
mLabels = labels;
+ mParentWhich = parentWhich;
setArguments(args);
}
@@ -72,7 +65,8 @@
@Override
public void onClick(DialogInterface dialog, int which) {
final Bundle args = getArguments();
- mTargetInfos.get(which).start(getActivity(), null);
+ mTargetInfos.setSelected(which);
+ ((ChooserActivity) getActivity()).startSelected(mParentWhich, false, true);
dismiss();
}
diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
index 55200c8..e582583 100644
--- a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
@@ -16,6 +16,12 @@
package com.android.internal.app.chooser;
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.internal.app.ResolverActivity;
+
import java.util.ArrayList;
import java.util.List;
@@ -27,6 +33,8 @@
List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
// We'll use this DRI for basic presentation info - eg icon, name.
final DisplayResolveInfo mBaseInfo;
+ // Index of selected target
+ private int mSelected = -1;
/**
* @param firstInfo A representative DRI to use for the main icon, title, etc for this Info.
@@ -57,4 +65,30 @@
return mTargetInfos;
}
+ public void setSelected(int selected) {
+ mSelected = selected;
+ }
+
+ /**
+ * Whether or not the user has selected a specific target for this MultiInfo.
+ */
+ public boolean hasSelected() {
+ return mSelected >= 0;
+ }
+
+ @Override
+ public boolean start(Activity activity, Bundle options) {
+ return mTargetInfos.get(mSelected).start(activity, options);
+ }
+
+ @Override
+ public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+ return mTargetInfos.get(mSelected).startAsCaller(activity, options, userId);
+ }
+
+ @Override
+ public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
+ return mTargetInfos.get(mSelected).startAsUser(activity, options, user);
+ }
+
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index dec9ae7..221cd6d 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -66,6 +66,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Pattern;
/**
* A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
@@ -388,7 +389,9 @@
resolveProjection(projection), parentDocumentId, parent);
if (parent.isDirectory()) {
for (File file : FileUtils.listFilesOrEmpty(parent)) {
- includeFile(result, null, file);
+ if (!shouldHide(file)) {
+ includeFile(result, null, file);
+ }
}
} else {
Log.w(TAG, "parentDocumentId '" + parentDocumentId + "' is not Directory");
@@ -422,6 +425,8 @@
pending.add(folder);
while (!pending.isEmpty() && result.getCount() < 24) {
final File file = pending.removeFirst();
+ if (shouldHide(file)) continue;
+
if (file.isDirectory()) {
for (File child : file.listFiles()) {
pending.add(child);
@@ -540,6 +545,7 @@
} else {
file = getFileForDocId(docId);
}
+
final String mimeType = getDocumentType(docId, file);
row.add(Document.COLUMN_DOCUMENT_ID, docId);
row.add(Document.COLUMN_MIME_TYPE, mimeType);
@@ -598,6 +604,17 @@
return row;
}
+ private static final Pattern PATTERN_HIDDEN_PATH = Pattern.compile(
+ "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb|sandbox)$");
+
+ /**
+ * In a scoped storage world, access to "Android/data" style directories are
+ * hidden for privacy reasons.
+ */
+ protected boolean shouldHide(@NonNull File file) {
+ return (PATTERN_HIDDEN_PATH.matcher(file.getAbsolutePath()).matches());
+ }
+
protected boolean shouldBlockFromTree(@NonNull String docId) {
return false;
}
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 99b5f85..a3c455b 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -38,6 +38,7 @@
#define ENCODING_AC4 17
#define ENCODING_E_AC3_JOC 18
#define ENCODING_DOLBY_MAT 19
+#define ENCODING_OPUS 20
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -88,6 +89,8 @@
return AUDIO_FORMAT_DEFAULT;
case ENCODING_DOLBY_MAT:
return AUDIO_FORMAT_MAT;
+ case ENCODING_OPUS:
+ return AUDIO_FORMAT_OPUS;
default:
return AUDIO_FORMAT_INVALID;
}
@@ -142,6 +145,8 @@
case AUDIO_FORMAT_MAT_2_0:
case AUDIO_FORMAT_MAT_2_1:
return ENCODING_DOLBY_MAT;
+ case AUDIO_FORMAT_OPUS:
+ return ENCODING_OPUS;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 33da34d..4a7276c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1174,6 +1174,34 @@
capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance());
}
+static jboolean nativeGetAutoLowLatencyModeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return NULL;
+
+ return SurfaceComposerClient::getAutoLowLatencyModeSupport(token);
+}
+
+static jboolean nativeGetGameContentTypeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return NULL;
+
+ return SurfaceComposerClient::getGameContentTypeSupport(token);
+}
+
+static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return;
+
+ SurfaceComposerClient::setAutoLowLatencyMode(token, on);
+}
+
+static void nativeSetGameContentType(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return;
+
+ SurfaceComposerClient::setGameContentType(token, on);
+}
+
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == NULL) {
@@ -1376,6 +1404,14 @@
(void*)nativeGetActiveColorMode},
{"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
(void*)nativeSetActiveColorMode},
+ {"nativeGetAutoLowLatencyModeSupport", "(Landroid/os/IBinder;)Z",
+ (void*)nativeGetAutoLowLatencyModeSupport },
+ {"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V",
+ (void*)nativeSetAutoLowLatencyMode },
+ {"nativeGetGameContentTypeSupport", "(Landroid/os/IBinder;)Z",
+ (void*)nativeGetGameContentTypeSupport },
+ {"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V",
+ (void*)nativeSetGameContentType },
{"nativeGetCompositionDataspaces", "()[I",
(void*)nativeGetCompositionDataspaces},
{"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;",
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index b2a19cf..97c94ec 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -716,7 +716,7 @@
// OS: 6.0
ACCESSIBILITY = 2;
- // OPEN: Settings > Accessibility > Captions
+ // OPEN: Settings > Accessibility > Captions preference
// CATEGORY: SETTINGS
// OS: 6.0
ACCESSIBILITY_CAPTION_PROPERTIES = 3;
@@ -2517,4 +2517,24 @@
// OS: R
DIALOG_MAGNIFICATION_CAPABILITY = 1816;
+ // OPEN: Settings > Accessibility > Color inversion
+ // CATEGORY: SETTINGS
+ // OS: R
+ ACCESSIBILITY_COLOR_INVERSION_SETTINGS = 1817;
+
+ // OPEN: Settings > Accessibility > Color inversion > Edit shortcut dialog
+ // CATEGORY: SETTINGS
+ // OS: R
+ DIALOG_COLOR_INVERSION_EDIT_SHORTCUT = 1818;
+
+ // OPEN: Settings > Accessibility > Captions preference > Captions appearance
+ // CATEGORY: SETTINGS
+ // OS: R
+ ACCESSIBILITY_CAPTION_APPEARANCE = 1819;
+
+ // OPEN: Settings > Accessibility > Captions preference > More options
+ // CATEGORY: SETTINGS
+ // OS: R
+ ACCESSIBILITY_CAPTION_MORE_OPTIONS = 1820;
+
}
diff --git a/core/proto/android/util/quotatracker.proto b/core/proto/android/util/quotatracker.proto
index 0dea853..5d022ed 100644
--- a/core/proto/android/util/quotatracker.proto
+++ b/core/proto/android/util/quotatracker.proto
@@ -34,17 +34,25 @@
// Current elapsed realtime.
optional int64 elapsed_realtime = 3;
- message AlarmListener {
+ message InQuotaAlarmListener {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- // Whether the listener is waiting for an alarm or not.
- optional bool is_waiting = 1;
- // The time at which the alarm should go off, in the elapsed realtime timebase. Only
- // valid if is_waiting is true.
- optional int64 trigger_time_elapsed = 2;
- }
+ // The time at which the alarm is set to go off, in the elapsed realtime timebase.
+ optional int64 trigger_time_elapsed = 1;
- // Next tag: 4
+ message Alarm {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UptcProto uptc = 1;
+
+ // The time at which the UPTC will be in quota, in the elapsed realtime timebase.
+ optional int64 in_quota_time_elapsed = 2;
+ }
+ repeated Alarm alarms = 2;
+ }
+ optional InQuotaAlarmListener in_quota_alarm_listener = 4;
+
+ // Next tag: 5
}
// A com.android.util.quota.Category object.
@@ -118,8 +126,6 @@
repeated Event events = 3;
repeated ExecutionStats execution_stats = 4;
-
- optional QuotaTrackerProto.AlarmListener in_quota_alarm_listener = 5;
}
repeated UptcStats uptc_stats = 3;
@@ -195,8 +201,6 @@
repeated TimingSession saved_sessions = 4;
repeated ExecutionStats execution_stats = 5;
-
- optional QuotaTrackerProto.AlarmListener in_quota_alarm_listener = 6;
}
repeated UptcStats uptc_stats = 3;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 073ab5a..7719165 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -347,6 +347,10 @@
<protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
<protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
<protected-broadcast android:name="com.android.server.net.action.SNOOZE_RAPID" />
+ <protected-broadcast android:name="com.android.server.wifi.ACTION_SHOW_SET_RANDOMIZATION_DETAILS" />
+ <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP" />
+ <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP" />
+ <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
@@ -722,6 +726,11 @@
<!-- ====================================================================== -->
<eat-comment />
+ <!-- @SystemApi Allows accessing the messages on ICC
+ @hide Used internally. -->
+ <permission android:name="android.permission.ACCESS_MESSAGES_ON_ICC"
+ android:protectionLevel="signature|telephony" />
+
<!-- Used for runtime permissions related to user's SMS messages. -->
<permission-group android:name="android.permission-group.SMS"
android:icon="@drawable/perm_group_sms"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b17d473..6350bf6 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2497,6 +2497,30 @@
<enum name="hdr" value="2" />
</attr>
<attr name="forceQueryable" format="boolean" />
+ <!-- Indicates whether the activity wants the connected display to do minimal
+ post processing on the produced image or video frames. This will only be
+ requested if this activity's main window is visible on the screen.
+
+ <p> This setting should be used when low latency has a higher priority than
+ image enhancement processing (e.g. for games or video conferencing).
+
+ <p> If the Display sink is connected via HDMI, the device will begin to
+ send infoframes with Auto Low Latency Mode enabled and Game Content Type.
+ This will switch the connected display to a minimal image processing mode
+ (if available), which reduces latency, improving the user experience for
+ gaming or video conferencing applications. For more information,
+ see HDMI 2.1 specification.
+
+ <p> If the Display sink has an internal connection or uses some other
+ protocol than HDMI, effects may be similar but implementation-defined.
+
+ <p> The ability to switch to a mode with minimal post proessing may be
+ disabled by a user setting in the system settings menu. In that case,
+ this field is ignored and the display will remain in its current
+ mode.
+
+ <p> See {@link android.content.pm.ActivityInfo #preferMinimalPostProcessing} -->
+ <attr name="preferMinimalPostProcessing" format="boolean"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f28f08b..9073a02 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3560,13 +3560,14 @@
set to empty string), a default textclassifier will be loaded in the calling app's process.
See android.view.textclassifier.TextClassificationManager.
-->
+ <!-- TODO(b/144896755) remove the config -->
<string name="config_defaultTextClassifierPackage" translatable="false"></string>
- <!-- A list of the default system textclassifier service package name. Only one of the packages
- will be activated and the fist package is the default system textclassifier service. OS
- only tries to bind the first trusted service and the others can be selected via device
- config. These services must be trusted, as they can be activated without explicit consent
- of the user. Example: "com.android.textclassifier"
+ <!-- A list of supported system textClassifier service package names. Only one of the packages
+ will be activated. The first package in the list is the default system textClassifier
+ service. OS only tries to bind and grant permissions to the first trusted service and the
+ others can be selected via device config. These services must be trusted, as they can be
+ activated without explicit consent of the user. Example: "com.android.textclassifier"
-->
<string-array name="config_defaultTextClassifierPackages" translatable="false">
<item>android.ext.services</item>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4bbfeaf..0b63518 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3049,6 +3049,10 @@
<public name="accessibilitySystemActionLockScreen" />
<public name="accessibilitySystemActionTakeScreenshot" />
</public-group>
+
+ <public-group type="attr" first-id="0x0101060c">
+ <public name="preferMinimalPostProcessing"/>
+ </public-group>
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index c7e54f3..1aea98a 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1399,6 +1399,7 @@
</activity>
<activity android:name="android.app.activity.ActivityThreadTest$TestActivity"
+ android:supportsPictureInPicture="true"
android:exported="true">
</activity>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index c50cbe3..beaaa37 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -25,10 +25,12 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.IApplicationThread;
+import android.app.PictureInPictureParams;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
@@ -332,6 +334,50 @@
assertThat(activity.isResumed()).isTrue();
}
+ @Test
+ public void testHandlePictureInPictureRequested_overriddenToEnter() {
+ final Intent startIntent = new Intent();
+ startIntent.putExtra(TestActivity.PIP_REQUESTED_OVERRIDE_ENTER, true);
+ final TestActivity activity = mActivityTestRule.launchActivity(startIntent);
+ final ActivityThread activityThread = activity.getActivityThread();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ activityThread.handlePictureInPictureRequested(activity.getActivityToken());
+ });
+
+ assertTrue(activity.pipRequested());
+ assertTrue(activity.enteredPip());
+ }
+
+ @Test
+ public void testHandlePictureInPictureRequested_overriddenToSkip() {
+ final Intent startIntent = new Intent();
+ startIntent.putExtra(TestActivity.PIP_REQUESTED_OVERRIDE_SKIP, true);
+ final TestActivity activity = mActivityTestRule.launchActivity(startIntent);
+ final ActivityThread activityThread = activity.getActivityThread();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ activityThread.handlePictureInPictureRequested(activity.getActivityToken());
+ });
+
+ assertTrue(activity.pipRequested());
+ assertTrue(activity.enterPipSkipped());
+ }
+
+ @Test
+ public void testHandlePictureInPictureRequested_notOverridden() {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ activityThread.handlePictureInPictureRequested(activity.getActivityToken());
+ });
+
+ assertTrue(activity.pipRequested());
+ assertFalse(activity.enteredPip());
+ assertFalse(activity.enterPipSkipped());
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(IBinder, Configuration, int)}
* to try to push activity configuration to the activity for the given sequence number.
@@ -428,9 +474,16 @@
// Test activity
public static class TestActivity extends Activity {
+ static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter";
+ static final String PIP_REQUESTED_OVERRIDE_SKIP = "pip_requested_override_skip";
+
int mNumOfConfigChanges;
final Configuration mConfig = new Configuration();
+ private boolean mPipRequested;
+ private boolean mPipEntered;
+ private boolean mPipEnterSkipped;
+
/**
* A latch used to notify tests that we're about to wait for configuration latch. This
* is used to notify test code that preExecute phase for activity configuration change
@@ -460,5 +513,29 @@
}
}
}
+
+ @Override
+ public void onPictureInPictureRequested() {
+ mPipRequested = true;
+ if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_ENTER, false)) {
+ enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+ mPipEntered = true;
+ } else if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_SKIP, false)) {
+ mPipEnterSkipped = true;
+ }
+ super.onPictureInPictureRequested();
+ }
+
+ boolean pipRequested() {
+ return mPipRequested;
+ }
+
+ boolean enteredPip() {
+ return mPipEntered;
+ }
+
+ boolean enterPipSkipped() {
+ return mPipEnterSkipped;
+ }
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index b9cc8f4..ec5813f 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -29,6 +29,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -77,6 +80,7 @@
final TextLinks result = TextLinks.CREATOR.createFromParcel(parcel);
final List<TextLinks.TextLink> resultList = new ArrayList<>(result.getLinks());
+ assertEquals(fullText, result.getText());
assertEquals(2, resultList.size());
assertEquals(0, resultList.get(0).getStart());
assertEquals(4, resultList.get(0).getEnd());
@@ -103,10 +107,13 @@
Arrays.asList(TextClassifier.HINT_TEXT_IS_EDITABLE),
Arrays.asList("a", "b", "c"),
Arrays.asList("b"));
+ final ZonedDateTime referenceTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(1000L),
+ ZoneId.of("UTC"));
final TextLinks.Request reference = new TextLinks.Request.Builder("text")
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
.setEntityConfig(entityConfig)
.setExtras(BUNDLE)
+ .setReferenceTime(referenceTime)
.build();
reference.setCallingPackageName(packageName);
@@ -124,5 +131,6 @@
result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
assertEquals(packageName, result.getCallingPackageName());
+ assertEquals(referenceTime, result.getReferenceTime());
}
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 20395fb..1d3a399 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -28,6 +28,13 @@
}
prebuilt_etc {
+ name: "preinstalled-packages-platform-overlays.xml",
+ product_specific: true,
+ sub_dir: "sysconfig",
+ src: "preinstalled-packages-platform-overlays.xml",
+}
+
+prebuilt_etc {
name: "hiddenapi-package-whitelist.xml",
sub_dir: "sysconfig",
src: "hiddenapi-package-whitelist.xml",
diff --git a/data/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml
new file mode 100644
index 0000000..1724715
--- /dev/null
+++ b/data/etc/preinstalled-packages-platform-overlays.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- System packages to preinstall on all devices with frameworks-base-overlays, per user type.
+ Documentation at frameworks/base/data/etc/preinstalled-packages-platform.xml
+-->
+<config>
+ <install-in-user-type package="com.android.internal.display.cutout.emulation.corner">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.display.cutout.emulation.double">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.display.cutout.emulation.tall">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.gestural">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.gestural_extra_wide_back">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.gestural_narrow_back">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.gestural_wide_back">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.threebutton">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.twobutton">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+</config>
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 604b407..efab27f 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -106,4 +106,7 @@
<install-in user-type="FULL" />
<install-in user-type="PROFILE" />
</install-in-user-type>
+ <install-in-user-type package="com.android.wallpaperbackup">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
</config>
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index eb469a8..c1fed26 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -22,7 +22,6 @@
#include <log/log.h>
#include <private/gui/SyncFeatures.h>
#include <sync/sync.h>
-#include <system/window.h>
#include <utils/Trace.h>
#include <string>
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index bbffb35..a7ea21d 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -29,12 +29,12 @@
static int InvertTransform(int transform) {
switch (transform) {
- case NATIVE_WINDOW_TRANSFORM_ROT_90:
- return NATIVE_WINDOW_TRANSFORM_ROT_270;
- case NATIVE_WINDOW_TRANSFORM_ROT_180:
- return NATIVE_WINDOW_TRANSFORM_ROT_180;
- case NATIVE_WINDOW_TRANSFORM_ROT_270:
- return NATIVE_WINDOW_TRANSFORM_ROT_90;
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
+ return ANATIVEWINDOW_TRANSFORM_ROTATE_270;
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
+ return ANATIVEWINDOW_TRANSFORM_ROTATE_180;
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
+ return ANATIVEWINDOW_TRANSFORM_ROTATE_90;
default:
return 0;
}
@@ -47,11 +47,11 @@
switch (transform) {
case 0:
return SkMatrix::I();
- case NATIVE_WINDOW_TRANSFORM_ROT_90:
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
- case NATIVE_WINDOW_TRANSFORM_ROT_180:
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
- case NATIVE_WINDOW_TRANSFORM_ROT_270:
+ case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
default:
LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform);
@@ -168,7 +168,7 @@
outWindowInfo->transform = query_value;
outWindowInfo->actualSize = outWindowInfo->size;
- if (outWindowInfo->transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ if (outWindowInfo->transform & ANATIVEWINDOW_TRANSFORM_ROTATE_90) {
outWindowInfo->actualSize.set(outWindowInfo->size.height(), outWindowInfo->size.width());
}
diff --git a/media/Android.bp b/media/Android.bp
index 75ccb22..a136517 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -26,7 +26,8 @@
installable: true,
- // Make sure that the implementaion only relies on SDK or system APIs.
+ // TODO: build against stable API surface. Use core_platform for now to avoid
+ // link-check failure with exoplayer building against "current".
sdk_version: "core_platform",
libs: [
// The order matters. android_system_* library should come later.
@@ -101,6 +102,7 @@
name: "framework_media_annotation",
srcs: [":framework-media-annotation-srcs"],
installable: false,
+ sdk_version: "core_current",
}
aidl_interface {
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 489d050..7ff15df 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -281,6 +281,8 @@
* metadata (object audio) over HDMI (e.g. Dolby Atmos content).
**/
public static final int ENCODING_DOLBY_MAT = 19;
+ /** Audio data format: OPUS compressed. */
+ public static final int ENCODING_OPUS = 20;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -323,6 +325,8 @@
return "ENCODING_E_AC3_JOC";
case ENCODING_DOLBY_MAT:
return "ENCODING_DOLBY_MAT";
+ case ENCODING_OPUS:
+ return "ENCODING_OPUS";
default :
return "invalid encoding " + enc;
}
@@ -548,6 +552,7 @@
case ENCODING_AC4:
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
+ case ENCODING_OPUS:
return true;
default:
return false;
@@ -576,6 +581,7 @@
case ENCODING_AC4:
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
+ case ENCODING_OPUS:
return true;
default:
return false;
@@ -607,6 +613,7 @@
case ENCODING_AC4:
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
+ case ENCODING_OPUS:
return false;
case ENCODING_INVALID:
default:
@@ -638,6 +645,7 @@
case ENCODING_AC4:
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
+ case ENCODING_OPUS:
return false;
case ENCODING_INVALID:
default:
@@ -917,6 +925,7 @@
case ENCODING_AC4:
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
+ case ENCODING_OPUS:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1136,7 +1145,8 @@
ENCODING_AAC_XHE,
ENCODING_AC4,
ENCODING_E_AC3_JOC,
- ENCODING_DOLBY_MAT }
+ ENCODING_DOLBY_MAT,
+ ENCODING_OPUS }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index ab80b3a..515e9d0 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -400,6 +400,7 @@
* configurations
* @return true if active
*/
+ @SystemApi
public boolean isActive() {
switch (mPlayerState) {
case PLAYER_STATE_STARTED:
@@ -420,18 +421,7 @@
* @param pw
*/
public void dump(PrintWriter pw) {
- pw.println(" " + toLogFriendlyString(this));
- }
-
- /**
- * @hide
- */
- public static String toLogFriendlyString(AudioPlaybackConfiguration apc) {
- return new String("ID:" + apc.mPlayerIId
- + " -- type:" + toLogFriendlyPlayerType(apc.mPlayerType)
- + " -- u/pid:" + apc.mClientUid +"/" + apc.mClientPid
- + " -- state:" + toLogFriendlyPlayerState(apc.mPlayerState)
- + " -- attr:" + apc.mPlayerAttr);
+ pw.println(" " + this);
}
public static final @android.annotation.NonNull Parcelable.Creator<AudioPlaybackConfiguration> CREATOR
@@ -498,6 +488,15 @@
&& (mClientPid == that.mClientPid));
}
+ @Override
+ public String toString() {
+ return "AudioPlaybackConfiguration piid:" + mPlayerIId
+ + " type:" + toLogFriendlyPlayerType(mPlayerType)
+ + " u/pid:" + mClientUid + "/" + mClientPid
+ + " state:" + toLogFriendlyPlayerState(mPlayerState)
+ + " attr:" + mPlayerAttr;
+ }
+
//=====================================================================
// Inner class for corresponding IPlayer and its death monitoring
static final class IPlayerShell implements IBinder.DeathRecipient {
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index d8fd1ff..a03aa86 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -24,6 +24,8 @@
*/
oneway interface IMediaRoute2Provider {
void setClient(IMediaRoute2ProviderClient client);
+ void requestCreateSession(String packageName, String routeId, String controlCategory,
+ int requestId);
void requestSelectRoute(String packageName, String id, int seq);
void unselectRoute(String packageName, String id);
void notifyControlRequestSent(String id, in Intent request);
diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl
index f4fb7f4..657f703 100644
--- a/media/java/android/media/IMediaRoute2ProviderClient.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl
@@ -18,6 +18,7 @@
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2Info;
+import android.media.RouteSessionInfo;
import android.os.Bundle;
/**
@@ -25,5 +26,7 @@
*/
oneway interface IMediaRoute2ProviderClient {
void updateProviderInfo(in MediaRoute2ProviderInfo info);
- void notifyRouteSelected(String packageName, String routeId, in Bundle controlHints, int seq);
+ void notifyRouteSelected(String packageName, String routeId, in @nullable Bundle controlHints,
+ int seq);
+ void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId);
}
diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl
index b04af7d..293116c1 100644
--- a/media/java/android/media/IMediaRouter2Client.aidl
+++ b/media/java/android/media/IMediaRouter2Client.aidl
@@ -17,6 +17,7 @@
package android.media;
import android.media.MediaRoute2Info;
+import android.media.RouteSessionInfo;
import android.os.Bundle;
/**
@@ -27,5 +28,5 @@
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
- void notifyRouteSelected(in MediaRoute2Info route, int reason, in Bundle controlHints);
+ void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index d803f04..faf2563 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -48,13 +48,9 @@
void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
- /**
- * Changes the selected route of the client.
- *
- * @param client the client that changes it's selected route
- * @param route the route to be selected
- */
- void requestSelectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route);
+
+ void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route,
+ String controlCategory, int requestId);
void setControlCategories(IMediaRouter2Client client, in List<String> categories);
void registerManager(IMediaRouter2Manager manager, String packageName);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 7008d32..d919d78 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -16,13 +16,17 @@
package android.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -46,6 +50,34 @@
}
};
+ /** @hide */
+ @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
+ CONNECTION_STATE_CONNECTED})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface ConnectionState {}
+
+ /**
+ * The default connection state indicating the route is disconnected.
+ *
+ * @see #getConnectionState
+ */
+ public static final int CONNECTION_STATE_DISCONNECTED = 0;
+
+ /**
+ * A connection state indicating the route is in the process of connecting and is not yet
+ * ready for use.
+ *
+ * @see #getConnectionState
+ */
+ public static final int CONNECTION_STATE_CONNECTING = 1;
+
+ /**
+ * A connection state indicating the route is connected.
+ *
+ * @see #getConnectionState
+ */
+ public static final int CONNECTION_STATE_CONNECTED = 2;
+
/**
* Playback information indicating the playback volume is fixed, i.e. it cannot be
* controlled from this object. An example of fixed playback volume is a remote player,
@@ -61,6 +93,46 @@
*/
public static final int PLAYBACK_VOLUME_VARIABLE = 1;
+ /** @hide */
+ @IntDef({
+ DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV,
+ DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface DeviceType {}
+
+ /**
+ * The default receiver device type of the route indicating the type is unknown.
+ *
+ * @see #getDeviceType
+ * @hide
+ */
+ public static final int DEVICE_TYPE_UNKNOWN = 0;
+
+ /**
+ * A receiver device type of the route indicating the presentation of the media is happening
+ * on a TV.
+ *
+ * @see #getDeviceType
+ */
+ public static final int DEVICE_TYPE_TV = 1;
+
+ /**
+ * A receiver device type of the route indicating the presentation of the media is happening
+ * on a speaker.
+ *
+ * @see #getDeviceType
+ */
+ public static final int DEVICE_TYPE_SPEAKER = 2;
+
+ /**
+ * A receiver device type of the route indicating the presentation of the media is happening
+ * on a bluetooth device such as a bluetooth speaker.
+ *
+ * @see #getDeviceType
+ * @hide
+ */
+ public static final int DEVICE_TYPE_BLUETOOTH = 3;
+
@NonNull
final String mId;
@Nullable
@@ -70,12 +142,17 @@
@Nullable
final CharSequence mDescription;
@Nullable
+ final @ConnectionState int mConnectionState;
+ @Nullable
+ final Uri mIconUri;
+ @Nullable
final String mClientPackageName;
@NonNull
final List<String> mSupportedCategories;
final int mVolume;
final int mVolumeMax;
final int mVolumeHandling;
+ final @DeviceType int mDeviceType;
@Nullable
final Bundle mExtras;
@@ -86,11 +163,14 @@
mProviderId = builder.mProviderId;
mName = builder.mName;
mDescription = builder.mDescription;
+ mConnectionState = builder.mConnectionState;
+ mIconUri = builder.mIconUri;
mClientPackageName = builder.mClientPackageName;
mSupportedCategories = builder.mSupportedCategories;
mVolume = builder.mVolume;
mVolumeMax = builder.mVolumeMax;
mVolumeHandling = builder.mVolumeHandling;
+ mDeviceType = builder.mDeviceType;
mExtras = builder.mExtras;
mUniqueId = createUniqueId();
}
@@ -100,11 +180,14 @@
mProviderId = in.readString();
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mConnectionState = in.readInt();
+ mIconUri = in.readParcelable(null);
mClientPackageName = in.readString();
mSupportedCategories = in.createStringArrayList();
mVolume = in.readInt();
mVolumeMax = in.readInt();
mVolumeHandling = in.readInt();
+ mDeviceType = in.readInt();
mExtras = in.readBundle();
mUniqueId = createUniqueId();
}
@@ -145,18 +228,22 @@
&& Objects.equals(mProviderId, other.mProviderId)
&& Objects.equals(mName, other.mName)
&& Objects.equals(mDescription, other.mDescription)
+ && (mConnectionState == other.mConnectionState)
+ && Objects.equals(mIconUri, other.mIconUri)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
&& Objects.equals(mSupportedCategories, other.mSupportedCategories)
&& (mVolume == other.mVolume)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolumeHandling == other.mVolumeHandling)
+ && (mDeviceType == other.mDeviceType)
//TODO: This will be evaluated as false in most cases. Try not to.
&& Objects.equals(mExtras, other.mExtras);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mName, mDescription, mSupportedCategories);
+ return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri,
+ mSupportedCategories, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
}
/**
@@ -204,6 +291,29 @@
}
/**
+ * Gets the connection state of the route.
+ *
+ * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
+ * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
+ */
+ @ConnectionState
+ public int getConnectionState() {
+ return mConnectionState;
+ }
+
+ /**
+ * Gets the URI of the icon representing this route.
+ * <p>
+ * This icon will be used in picker UIs if available.
+ *
+ * @return The URI of the icon representing this route, or null if none.
+ */
+ @Nullable
+ public Uri getIconUri() {
+ return mIconUri;
+ }
+
+ /**
* Gets the package name of the client that uses the route.
* Returns null if no clients use this.
* @hide
@@ -221,6 +331,18 @@
return mSupportedCategories;
}
+ //TODO: once device types are confirmed, reflect those into the comment.
+ /**
+ * Gets the type of the receiver device associated with this route.
+ *
+ * @return The type of the receiver device associated with this route:
+ * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}.
+ */
+ @DeviceType
+ public int getDeviceType() {
+ return mDeviceType;
+ }
+
/**
* Gets the current volume of the route. This may be invalid if the route is not selected.
*/
@@ -293,11 +415,14 @@
dest.writeString(mProviderId);
TextUtils.writeToParcel(mName, dest, flags);
TextUtils.writeToParcel(mDescription, dest, flags);
+ dest.writeInt(mConnectionState);
+ dest.writeParcelable(mIconUri, flags);
dest.writeString(mClientPackageName);
dest.writeStringList(mSupportedCategories);
dest.writeInt(mVolume);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolumeHandling);
+ dest.writeInt(mDeviceType);
dest.writeBundle(mExtras);
}
@@ -308,9 +433,12 @@
.append("id=").append(getId())
.append(", name=").append(getName())
.append(", description=").append(getDescription())
+ .append(", connectionState=").append(getConnectionState())
+ .append(", iconUri=").append(getIconUri())
.append(", volume=").append(getVolume())
.append(", volumeMax=").append(getVolumeMax())
.append(", volumeHandling=").append(getVolumeHandling())
+ .append(", deviceType=").append(getDeviceType())
.append(", providerId=").append(getProviderId())
.append(" }");
return result.toString();
@@ -324,11 +452,16 @@
String mProviderId;
CharSequence mName;
CharSequence mDescription;
+ @ConnectionState
+ int mConnectionState;
+ Uri mIconUri;
String mClientPackageName;
List<String> mSupportedCategories;
int mVolume;
int mVolumeMax;
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
+ @DeviceType
+ int mDeviceType = DEVICE_TYPE_UNKNOWN;
Bundle mExtras;
public Builder(@NonNull String id, @NonNull CharSequence name) {
@@ -348,11 +481,14 @@
}
setName(routeInfo.mName);
mDescription = routeInfo.mDescription;
+ mConnectionState = routeInfo.mConnectionState;
+ mIconUri = routeInfo.mIconUri;
setClientPackageName(routeInfo.mClientPackageName);
setSupportedCategories(routeInfo.mSupportedCategories);
setVolume(routeInfo.mVolume);
setVolumeMax(routeInfo.mVolumeMax);
setVolumeHandling(routeInfo.mVolumeHandling);
+ setDeviceType(routeInfo.mDeviceType);
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
@@ -403,6 +539,39 @@
}
/**
+ * Sets the route's connection state.
+ *
+ * {@link #CONNECTION_STATE_DISCONNECTED},
+ * {@link #CONNECTION_STATE_CONNECTING}, or
+ * {@link #CONNECTION_STATE_CONNECTED}.
+ */
+ @NonNull
+ public Builder setConnectionState(@ConnectionState int connectionState) {
+ mConnectionState = connectionState;
+ return this;
+ }
+
+ /**
+ * Sets the URI of the icon representing this route.
+ * <p>
+ * This icon will be used in picker UIs if available.
+ * </p><p>
+ * The URI must be one of the following formats:
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+ * </li>
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * </ul>
+ * </p>
+ */
+ @NonNull
+ public Builder setIconUri(@Nullable Uri iconUri) {
+ mIconUri = iconUri;
+ return this;
+ }
+
+ /**
* Sets the package name of the app using the route.
*/
@NonNull
@@ -470,6 +639,16 @@
mVolumeHandling = volumeHandling;
return this;
}
+
+ /**
+ * Sets the route's device type.
+ */
+ @NonNull
+ public Builder setDeviceType(@DeviceType int deviceType) {
+ mDeviceType = deviceType;
+ return this;
+ }
+
/**
* Sets a bundle of extras for the route.
*/
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index b5de88a..7078d4a 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -46,9 +46,9 @@
};
@Nullable
- private final String mUniqueId;
+ final String mUniqueId;
@NonNull
- private final ArrayMap<String, MediaRoute2Info> mRoutes;
+ final ArrayMap<String, MediaRoute2Info> mRoutes;
MediaRoute2ProviderInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
@@ -142,6 +142,7 @@
public Builder(@NonNull MediaRoute2ProviderInfo descriptor) {
Objects.requireNonNull(descriptor, "descriptor must not be null");
+ mUniqueId = descriptor.mUniqueId;
mRoutes = new ArrayMap<>(descriptor.mRoutes);
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 1b6183e..387b042 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -27,8 +27,13 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -40,10 +45,15 @@
public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
private final Handler mHandler;
+ private final Object mSessionLock = new Object();
private ProviderStub mStub;
+ // TODO: Rename this to mService (and accordingly IMediaRoute2ProviderClient to something else)
private IMediaRoute2ProviderClient mClient;
private MediaRoute2ProviderInfo mProviderInfo;
+ @GuardedBy("mSessionLock")
+ private ArrayMap<Integer, RouteSessionInfo> mSessionInfo = new ArrayMap<>();
+
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
}
@@ -93,6 +103,7 @@
/**
* Called when requestSetVolume is called on a route of the provider
+ *
* @param routeId the id of the route
* @param volume the target volume
*/
@@ -100,15 +111,174 @@
/**
* Called when requestUpdateVolume is called on a route of the provider
+ *
* @param routeId id of the route
* @param delta the delta to add to the current volume
*/
public abstract void onUpdateVolume(@NonNull String routeId, int delta);
/**
- * Updates provider info and publishes routes
+ * Gets information of the session with the given id.
+ *
+ * @param sessionId id of the session
+ * @return information of the session with the given id.
+ * null if the session is destroyed or id is not valid.
*/
- public final void setProviderInfo(MediaRoute2ProviderInfo info) {
+ @Nullable
+ public final RouteSessionInfo getSessionInfo(int sessionId) {
+ synchronized (mSessionLock) {
+ return mSessionInfo.get(sessionId);
+ }
+ }
+
+ /**
+ * Gets the list of {@link RouteSessionInfo session info} that the provider service maintains.
+ */
+ @NonNull
+ public final List<RouteSessionInfo> getAllSessionInfo() {
+ synchronized (mSessionLock) {
+ return new ArrayList<>(mSessionInfo.values());
+ }
+ }
+
+ /**
+ * Updates the information of a session.
+ * If the session is destroyed or not created before, it will be ignored.
+ * A session will be destroyed if it has no selected route.
+ * Call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify clients of
+ * session info changes.
+ *
+ * @param sessionInfo new session information
+ * @see #notifySessionCreated(RouteSessionInfo, int)
+ */
+ public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) {
+ Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ int sessionId = sessionInfo.getSessionId();
+
+ synchronized (mSessionLock) {
+ if (mSessionInfo.containsKey(sessionId)) {
+ mSessionInfo.put(sessionId, sessionInfo);
+ } else {
+ Log.w(TAG, "Ignoring unknown session info.");
+ }
+ }
+ }
+
+ /**
+ * Notifies clients of that the session is created and ready for use. If the session can be
+ * controlled, pass a {@link Bundle} that contains how to control it.
+ *
+ * @param sessionInfo information of the new session.
+ * Pass {@code null} to reject the request or inform clients that
+ * session creation has failed.
+ * @param requestId id of the previous request to create this session
+ */
+ //TODO: fail reason?
+ public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+ //TODO: validate sessionInfo.getSessionId() (it must be in "waiting list")
+ if (sessionInfo != null) {
+ synchronized (mSessionLock) {
+ mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo);
+ }
+ }
+
+ if (mClient == null) {
+ return;
+ }
+ try {
+ mClient.notifySessionCreated(sessionInfo, requestId);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session created.");
+ }
+ }
+
+ /**
+ * Releases a session with the given id.
+ * {@link #onDestroySession} is called if the session is released.
+ *
+ * @param sessionId id of the session to be released
+ * @see #onDestroySession(int, RouteSessionInfo)
+ */
+ public final void releaseSession(int sessionId) {
+ RouteSessionInfo sessionInfo;
+ synchronized (mSessionLock) {
+ sessionInfo = mSessionInfo.put(sessionId, null);
+ }
+ if (sessionInfo != null) {
+ mHandler.sendMessage(obtainMessage(
+ MediaRoute2ProviderService::onDestroySession, this, sessionId, sessionInfo));
+ }
+ }
+
+ /**
+ * Called when a session should be created.
+ * You should create and maintain your own session and notifies the client of
+ * session info. Call {@link #notifySessionCreated(RouteSessionInfo, int)}
+ * with the given {@code requestId} to notify the information of a new session.
+ * If you can't create the session or want to reject the request, pass {@code null}
+ * as session info in {@link #notifySessionCreated(RouteSessionInfo, int)}
+ * with the given {@code requestId}.
+ *
+ * @param packageName the package name of the application that selected the route
+ * @param routeId the id of the route initially being connected
+ * @param controlCategory the control category of the new session
+ * @param requestId the id of this session creation request
+ */
+ public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
+ @NonNull String controlCategory, int requestId);
+
+ /**
+ * Called when a session is about to be destroyed.
+ * You can clean up your session here. This can happen by the
+ * client or provider itself.
+ *
+ * @param sessionId id of the session being destroyed.
+ * @param lastSessionInfo information of the session being destroyed.
+ * @see #releaseSession(int)
+ */
+ public abstract void onDestroySession(int sessionId, @NonNull RouteSessionInfo lastSessionInfo);
+
+ //TODO: make a way to reject the request
+ /**
+ * Called when a client requests adding a route to a session.
+ * After the route is added, call {@link #updateSessionInfo(RouteSessionInfo)} to update
+ * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
+ * clients of updated session info.
+ *
+ * @param sessionId id of the session
+ * @param routeId id of the route
+ * @see #updateSessionInfo(RouteSessionInfo)
+ */
+ public abstract void onAddRoute(int sessionId, @NonNull String routeId);
+
+ //TODO: make a way to reject the request
+ /**
+ * Called when a client requests removing a route from a session.
+ * After the route is removed, call {@link #updateSessionInfo(RouteSessionInfo)} to update
+ * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
+ * clients of updated session info.
+ *
+ * @param sessionId id of the session
+ * @param routeId id of the route
+ */
+ public abstract void onRemoveRoute(int sessionId, @NonNull String routeId);
+
+ //TODO: make a way to reject the request
+ /**
+ * Called when a client requests transferring a session to a route.
+ * After the transfer is finished, call {@link #updateSessionInfo(RouteSessionInfo)} to update
+ * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
+ * clients of updated session info.
+ *
+ * @param sessionId id of the session
+ * @param routeId id of the route
+ */
+ public abstract void onTransferRoute(int sessionId, @NonNull String routeId);
+
+ /**
+ * Updates provider info and publishes routes and session info.
+ */
+ public final void updateProviderInfo(MediaRoute2ProviderInfo info) {
mProviderInfo = info;
publishState();
}
@@ -142,6 +312,7 @@
}
void publishState() {
+ //TODO: sends session info
if (mClient == null) {
return;
}
@@ -179,35 +350,43 @@
}
@Override
- public void requestSelectRoute(String packageName, String id, int seq) {
+ public void requestCreateSession(String packageName, String routeId, String controlCategory,
+ int requestId) {
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
+ MediaRoute2ProviderService.this, packageName, routeId, controlCategory,
+ requestId));
+ }
+
+ @Override
+ public void requestSelectRoute(String packageName, String routeId, int seq) {
+ //TODO: call onCreateSession instead
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
- MediaRoute2ProviderService.this, packageName, id,
- new SelectToken(packageName, id, seq)));
-
+ MediaRoute2ProviderService.this, packageName, routeId,
+ new SelectToken(packageName, routeId, seq)));
}
@Override
- public void unselectRoute(String packageName, String id) {
+ public void unselectRoute(String packageName, String routeId) {
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUnselectRoute,
- MediaRoute2ProviderService.this, packageName, id));
+ MediaRoute2ProviderService.this, packageName, routeId));
}
@Override
- public void notifyControlRequestSent(String id, Intent request) {
+ public void notifyControlRequestSent(String routeId, Intent request) {
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest,
- MediaRoute2ProviderService.this, id, request));
+ MediaRoute2ProviderService.this, routeId, request));
}
@Override
- public void requestSetVolume(String id, int volume) {
+ public void requestSetVolume(String routeId, int volume) {
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
- MediaRoute2ProviderService.this, id, volume));
+ MediaRoute2ProviderService.this, routeId, volume));
}
@Override
- public void requestUpdateVolume(String id, int delta) {
+ public void requestUpdateVolume(String routeId, int delta) {
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume,
- MediaRoute2ProviderService.this, id, delta));
+ MediaRoute2ProviderService.this, routeId, delta));
}
}
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index f9dabcf..d58a105 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -29,6 +29,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
@@ -46,6 +47,7 @@
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A new Media Router
@@ -98,7 +100,10 @@
private final Context mContext;
private final IMediaRouterService mMediaRouterService;
- private final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords =
+ private final CopyOnWriteArrayList<RouteCallbackRecord> mRouteCallbackRecords =
+ new CopyOnWriteArrayList<>();
+
+ private final CopyOnWriteArrayList<SessionCreationRequest> mSessionCreationRequests =
new CopyOnWriteArrayList<>();
private final String mPackageName;
@@ -108,12 +113,12 @@
@GuardedBy("sLock")
private List<String> mControlCategories = Collections.emptyList();
- private MediaRoute2Info mSelectedRoute;
- @GuardedBy("sLock")
- private MediaRoute2Info mSelectingRoute;
+ // TODO: Make MediaRouter2 is always connected to the MediaRouterService.
@GuardedBy("sLock")
private Client2 mClient;
+ private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1);
+
final Handler mHandler;
@GuardedBy("sLock")
private boolean mShouldUpdateRoutes;
@@ -154,18 +159,14 @@
for (MediaRoute2Info route : currentSystemRoutes) {
mRoutes.put(route.getId(), route);
}
- // The first route is the currently selected system route.
- // For example, if there are two system routes (BT and device speaker),
- // BT will be the first route in the list.
- mSelectedRoute = currentSystemRoutes.get(0);
}
/**
* Registers a callback to discover routes and to receive events when they change.
*/
public void registerCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull Callback callback) {
- registerCallback(executor, callback, 0);
+ @NonNull RouteCallback routeCallback) {
+ registerCallback(executor, routeCallback, 0);
}
/**
@@ -175,12 +176,12 @@
* </p>
*/
public void registerCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull Callback callback, int flags) {
+ @NonNull RouteCallback routeCallback, int flags) {
Objects.requireNonNull(executor, "executor must not be null");
- Objects.requireNonNull(callback, "callback must not be null");
+ Objects.requireNonNull(routeCallback, "callback must not be null");
- CallbackRecord record = new CallbackRecord(callback, executor, flags);
- if (!mCallbackRecords.addIfAbsent(record)) {
+ RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, flags);
+ if (!mRouteCallbackRecords.addIfAbsent(record)) {
Log.w(TAG, "Ignoring the same callback");
return;
}
@@ -205,19 +206,20 @@
* Unregisters the given callback. The callback will no longer receive events.
* If the callback has not been added or been removed already, it is ignored.
*
- * @param callback the callback to unregister
+ * @param routeCallback the callback to unregister
* @see #registerCallback
*/
- public void unregisterCallback(@NonNull Callback callback) {
- Objects.requireNonNull(callback, "callback must not be null");
+ public void unregisterCallback(@NonNull RouteCallback routeCallback) {
+ Objects.requireNonNull(routeCallback, "callback must not be null");
- if (!mCallbackRecords.remove(new CallbackRecord(callback, null, 0))) {
+ if (!mRouteCallbackRecords.remove(
+ new RouteCallbackRecord(null, routeCallback, 0))) {
Log.w(TAG, "Ignoring unknown callback");
return;
}
synchronized (sLock) {
- if (mCallbackRecords.size() == 0 && mClient != null) {
+ if (mRouteCallbackRecords.size() == 0 && mClient != null) {
try {
mMediaRouterService.unregisterClient2(mClient);
} catch (RemoteException ex) {
@@ -282,38 +284,51 @@
}
/**
- * Gets the currently selected route.
+ * Requests the media route provider service to create a session with the given route.
*
- * @return the selected route
+ * @param route the route you want to create a session with.
+ * @param controlCategory the control category of the session. Should not be empty
+ * @param executor the executor to get the result of the session creation
+ * @param callback the callback to get the result of the session creation
+ *
+ * @see SessionCreationCallback#onSessionCreated(RouteSessionController, Bundle)
+ * @see SessionCreationCallback#onSessionCreationFailed()
*/
@NonNull
- public MediaRoute2Info getSelectedRoute() {
- return mSelectedRoute;
- }
-
- /**
- * Request to select the specified route. When the route is selected,
- * {@link Callback#onRouteSelected(MediaRoute2Info, int, Bundle)} will be called.
- *
- * @param route the route to select
- */
- public void requestSelectRoute(@NonNull MediaRoute2Info route) {
+ public void requestCreateSession(@NonNull MediaRoute2Info route,
+ @NonNull String controlCategory,
+ @CallbackExecutor Executor executor, @NonNull SessionCreationCallback callback) {
Objects.requireNonNull(route, "route must not be null");
+ if (TextUtils.isEmpty(controlCategory)) {
+ throw new IllegalArgumentException("controlCategory must not be empty");
+ }
+ Objects.requireNonNull(executor, "executor must not be null");
+ Objects.requireNonNull(callback, "callback must not be null");
+
+ // TODO: Check the given route exists
+ // TODO: Check the route supports the given controlCategory
+
+ final int requestId;
+ // TODO: This does not ensure the uniqueness of the request ID.
+ // Find the way to ensure it. (e.g. have mapping inside MediaRouterService)
+ requestId = Process.myPid() * 10000 + mSessionCreationRequestCnt.getAndIncrement();
+
+ SessionCreationRequest request = new SessionCreationRequest(
+ requestId, route, controlCategory, executor, callback);
+ mSessionCreationRequests.add(request);
Client2 client;
synchronized (sLock) {
- if (mSelectingRoute == route) {
- Log.w(TAG, "The route selection request is already sent.");
- return;
- }
- mSelectingRoute = route;
client = mClient;
}
if (client != null) {
try {
- mMediaRouterService.requestSelectRoute2(client, route);
+ mMediaRouterService.requestCreateSession(
+ client, route, controlCategory, requestId);
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to request to select route.", ex);
+ Log.e(TAG, "Unable to request to create session.", ex);
+ mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
+ MediaRouter2.this, null, requestId));
}
}
}
@@ -472,57 +487,70 @@
}
}
- void selectRouteOnHandler(MediaRoute2Info route, int reason, Bundle controlHints) {
- synchronized (sLock) {
- if (reason == SELECT_REASON_USER_SELECTED) {
- if (mSelectingRoute == null
- || !TextUtils.equals(mSelectingRoute.getUniqueId(), route.getUniqueId())) {
- Log.w(TAG, "Ignoring invalid or outdated notifyRouteSelected call. "
- + "selectingRoute=" + mSelectingRoute + " route=" + route);
- return;
- }
+ /**
+ * Creates a controller and calls the {@link SessionCreationCallback#onSessionCreated}.
+ * If session creation has failed, then it calls
+ * {@link SessionCreationCallback#onSessionCreationFailed()}.
+ * <p>
+ * Pass {@code null} to sessionInfo for the failure case.
+ */
+ void createControllerOnHandler(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+ SessionCreationRequest matchingRequest = null;
+ for (SessionCreationRequest request : mSessionCreationRequests) {
+ if (request.mRequestId == requestId) {
+ matchingRequest = request;
+ break;
}
- mSelectingRoute = null;
}
- if (reason == SELECT_REASON_SYSTEM_SELECTED) {
- reason = SELECT_REASON_USER_SELECTED;
+
+ if (matchingRequest == null) {
+ Log.w(TAG, "Ignoring session creation result for unknown request."
+ + " requestId=" + requestId + ", sessionInfo=" + sessionInfo);
+ return;
}
- mSelectedRoute = route;
- notifyRouteSelected(route, reason, controlHints);
+
+ mSessionCreationRequests.remove(matchingRequest);
+
+ final Executor executor = matchingRequest.mExecutor;
+ final SessionCreationCallback callback = matchingRequest.mSessionCreationCallback;
+
+ if (sessionInfo == null) {
+ // TODO: We may need to distinguish between failure and rejection.
+ // One way can be introducing 'reason'.
+ executor.execute(callback::onSessionCreationFailed);
+ } else {
+ // TODO: RouteSessionController should be created with full info (e.g. routes)
+ // from RouteSessionInfo.
+ RouteSessionController controller = new RouteSessionController(sessionInfo);
+ executor.execute(() -> callback.onSessionCreated(controller));
+ }
}
private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
- for (CallbackRecord record: mCallbackRecords) {
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
record.mExecutor.execute(
- () -> record.mCallback.onRoutesAdded(routes));
+ () -> record.mRouteCallback.onRoutesAdded(routes));
}
}
private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
- for (CallbackRecord record: mCallbackRecords) {
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
record.mExecutor.execute(
- () -> record.mCallback.onRoutesRemoved(routes));
+ () -> record.mRouteCallback.onRoutesRemoved(routes));
}
}
private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
- for (CallbackRecord record: mCallbackRecords) {
+ for (RouteCallbackRecord record: mRouteCallbackRecords) {
record.mExecutor.execute(
- () -> record.mCallback.onRoutesChanged(routes));
- }
- }
-
- private void notifyRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
- for (CallbackRecord record: mCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mCallback.onRouteSelected(route, reason, controlHints));
+ () -> record.mRouteCallback.onRoutesChanged(routes));
}
}
/**
- * Interface for receiving events about media routing changes.
+ * Callback for receiving events about media route discovery.
*/
- public static class Callback {
+ public static class RouteCallback {
/**
* Called when routes are added. Whenever you registers a callback, this will
* be invoked with known routes.
@@ -549,30 +577,33 @@
* @param routes the list of routes that have been changed. It's never empty.
*/
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
-
- /**
- * Called when a route is selected. Exactly one route can be selected at a time.
- *
- * @param route the selected route.
- * @param reason the reason why the route is selected.
- * @param controlHints An optional bundle of provider-specific arguments which may be
- * used to control the selected route. Can be empty.
- * @see #SELECT_REASON_UNKNOWN
- * @see #SELECT_REASON_USER_SELECTED
- * @see #SELECT_REASON_FALLBACK
- * @see #getSelectedRoute()
- */
- public void onRouteSelected(@NonNull MediaRoute2Info route, @SelectReason int reason,
- @NonNull Bundle controlHints) {}
}
- final class CallbackRecord {
- public final Callback mCallback;
- public Executor mExecutor;
- public int mFlags;
+ /**
+ * Callback for receiving a result of session creation.
+ */
+ public static class SessionCreationCallback {
+ /**
+ * Called when the route session is created by the route provider.
+ *
+ * @param controller the controller to control the created session
+ */
+ public void onSessionCreated(RouteSessionController controller) {}
- CallbackRecord(@NonNull Callback callback, @Nullable Executor executor, int flags) {
- mCallback = callback;
+ /**
+ * Called when the session creation request failed.
+ */
+ public void onSessionCreationFailed() {}
+ }
+
+ final class RouteCallbackRecord {
+ public final Executor mExecutor;
+ public final RouteCallback mRouteCallback;
+ public final int mFlags;
+
+ RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback,
+ int flags) {
+ mRouteCallback = routeCallback;
mExecutor = executor;
mFlags = flags;
}
@@ -582,15 +613,35 @@
if (this == obj) {
return true;
}
- if (!(obj instanceof CallbackRecord)) {
+ if (!(obj instanceof RouteCallbackRecord)) {
return false;
}
- return mCallback == ((CallbackRecord) obj).mCallback;
+ return mRouteCallback
+ == ((RouteCallbackRecord) obj).mRouteCallback;
}
@Override
public int hashCode() {
- return mCallback.hashCode();
+ return mRouteCallback.hashCode();
+ }
+ }
+
+ final class SessionCreationRequest {
+ public final MediaRoute2Info mRoute;
+ public final String mControlCategory;
+ public final Executor mExecutor;
+ public final SessionCreationCallback mSessionCreationCallback;
+ public final int mRequestId;
+
+ SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route,
+ @NonNull String controlCategory,
+ @Nullable Executor executor,
+ @NonNull SessionCreationCallback sessionCreationCallback) {
+ mRoute = route;
+ mControlCategory = controlCategory;
+ mExecutor = executor;
+ mSessionCreationCallback = sessionCreationCallback;
+ mRequestId = requestId;
}
}
@@ -617,10 +668,9 @@
}
@Override
- public void notifyRouteSelected(MediaRoute2Info route, int reason,
- Bundle controlHints) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::selectRouteOnHandler,
- MediaRouter2.this, route, reason, controlHints));
+ public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
+ MediaRouter2.this, sessionInfo, requestId));
}
}
}
diff --git a/media/java/android/media/RouteSessionController.java b/media/java/android/media/RouteSessionController.java
index 5ff7218..95fcdc6 100644
--- a/media/java/android/media/RouteSessionController.java
+++ b/media/java/android/media/RouteSessionController.java
@@ -17,9 +17,13 @@
package android.media;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
import com.android.internal.annotations.GuardedBy;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -38,6 +42,9 @@
private final int mSessionId;
private final String mCategory;
private final Object mLock = new Object();
+ private final Bundle mControlHints;
+
+ private List<String> mSelectedRoutes;
@GuardedBy("mLock")
private final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords =
@@ -46,12 +53,14 @@
private volatile boolean mIsReleased;
/**
- * @param sessionId the ID of the session.
- * @param category The category of media routes that the session includes.
+ * @param sessionInfo
*/
- RouteSessionController(int sessionId, @NonNull String category) {
- mSessionId = sessionId;
- mCategory = category;
+ RouteSessionController(@NonNull RouteSessionInfo sessionInfo) {
+ mSessionId = sessionInfo.getSessionId();
+ mCategory = sessionInfo.getControlCategory();
+ mSelectedRoutes = sessionInfo.getSelectedRoutes();
+ mControlHints = sessionInfo.getControlHints();
+ // TODO: Create getters for all other types of routes
}
/**
@@ -70,12 +79,19 @@
}
/**
- * @return the list of currently connected routes
+ * @return the control hints used to control route session if available.
+ */
+ @Nullable
+ public Bundle getControlHints() {
+ return mControlHints;
+ }
+
+ /**
+ * @return the list of currently selected routes
*/
@NonNull
- public List<MediaRoute2Info> getRoutes() {
- // TODO: Implement this when SessionInfo is introduced.
- return null;
+ public List<String> getSelectedRoutes() {
+ return Collections.unmodifiableList(mSelectedRoutes);
}
/**
@@ -93,7 +109,7 @@
/**
* Add routes to the remote session.
*
- * @see #getRoutes()
+ * @see #getSelectedRoutes()
* @see Callback#onSessionInfoChanged
*/
public void addRoutes(List<MediaRoute2Info> routes) {
@@ -102,9 +118,10 @@
/**
* Remove routes from this session. Media may be stopped on those devices.
- * Route removal requests that are not currently in {@link #getRoutes()} will be ignored.
+ * Route removal requests that are not currently in {@link #getSelectedRoutes()} will be
+ * ignored.
*
- * @see #getRoutes()
+ * @see #getSelectedRoutes()
* @see Callback#onSessionInfoChanged
*/
public void removeRoutes(List<MediaRoute2Info> routes) {
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java
index 0878e6b..c22fc40 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -17,6 +17,8 @@
package android.media;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -51,6 +53,7 @@
final List<String> mDeselectableRoutes;
final List<String> mGroupableRoutes;
final List<String> mTransferrableRoutes;
+ final Bundle mControlHints;
RouteSessionInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
@@ -63,6 +66,8 @@
mDeselectableRoutes = Collections.unmodifiableList(builder.mDeselectableRoutes);
mGroupableRoutes = Collections.unmodifiableList(builder.mGroupableRoutes);
mTransferrableRoutes = Collections.unmodifiableList(builder.mTransferrableRoutes);
+
+ mControlHints = builder.mControlHints;
}
RouteSessionInfo(@NonNull Parcel src) {
@@ -76,6 +81,8 @@
mDeselectableRoutes = ensureList(src.createStringArrayList());
mGroupableRoutes = ensureList(src.createStringArrayList());
mTransferrableRoutes = ensureList(src.createStringArrayList());
+
+ mControlHints = src.readBundle();
}
private static String ensureString(String str) {
@@ -150,6 +157,14 @@
return mTransferrableRoutes;
}
+ /**
+ * Gets the control hints
+ */
+ @Nullable
+ public Bundle getControlHints() {
+ return mControlHints;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -164,6 +179,7 @@
dest.writeStringList(mDeselectableRoutes);
dest.writeStringList(mGroupableRoutes);
dest.writeStringList(mTransferrableRoutes);
+ dest.writeBundle(mControlHints);
}
@Override
@@ -192,6 +208,7 @@
final List<String> mDeselectableRoutes;
final List<String> mGroupableRoutes;
final List<String> mTransferrableRoutes;
+ Bundle mControlHints;
public Builder(int sessionId, @NonNull String packageName,
@NonNull String controlCategory) {
@@ -215,82 +232,130 @@
mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes);
mGroupableRoutes = new ArrayList<>(sessionInfo.mGroupableRoutes);
mTransferrableRoutes = new ArrayList<>(sessionInfo.mTransferrableRoutes);
+
+ mControlHints = sessionInfo.mControlHints;
}
/**
- * Adds a selected route
+ * Clears the selected routes.
*/
@NonNull
- public Builder addSelectedRoute(String routeId) {
- mSelectedRoutes.add(routeId);
+ public Builder clearSelectedRoutes() {
+ mSelectedRoutes.clear();
return this;
}
/**
- * Removes a selected route
+ * Adds a route to the selected routes.
*/
@NonNull
- public Builder removeSelectedRoute(String routeId) {
- mSelectedRoutes.remove(routeId);
+ public Builder addSelectedRoute(@NonNull String routeId) {
+ mSelectedRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
return this;
}
/**
- * Adds a deselectable route
+ * Removes a route from the selected routes.
*/
@NonNull
- public Builder addDeselectableRoute(String routeId) {
- mDeselectableRoutes.add(routeId);
+ public Builder removeSelectedRoute(@NonNull String routeId) {
+ mSelectedRoutes.remove(Objects.requireNonNull(routeId, "routeId must not be null"));
return this;
}
/**
- * Removes a deselecable route
+ * Clears the deselectable routes.
*/
@NonNull
- public Builder removeDeselectableRoute(String routeId) {
- mDeselectableRoutes.remove(routeId);
+ public Builder clearDeselectableRoutes() {
+ mDeselectableRoutes.clear();
return this;
}
/**
- * Adds a groupable route
+ * Adds a route to the deselectable routes.
*/
@NonNull
- public Builder addGroupableRoute(String routeId) {
- mGroupableRoutes.add(routeId);
+ public Builder addDeselectableRoute(@NonNull String routeId) {
+ mDeselectableRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
return this;
}
/**
- * Removes a groupable route
+ * Removes a route from the deselectable routes.
*/
@NonNull
- public Builder removeGroupableRoute(String routeId) {
- mGroupableRoutes.remove(routeId);
+ public Builder removeDeselectableRoute(@NonNull String routeId) {
+ mDeselectableRoutes.remove(Objects.requireNonNull(routeId, "routeId must not be null"));
return this;
}
/**
- * Adds a transferrable route
+ * Clears the groupable routes.
*/
@NonNull
- public Builder addTransferrableRoute(String routeId) {
- mTransferrableRoutes.add(routeId);
+ public Builder clearGroupableRoutes() {
+ mGroupableRoutes.clear();
return this;
}
/**
- * Removes a transferrable route
+ * Adds a route to the groupable routes.
*/
@NonNull
- public Builder removeTransferrableRoute(String routeId) {
- mTransferrableRoutes.remove(routeId);
+ public Builder addGroupableRoute(@NonNull String routeId) {
+ mGroupableRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
return this;
}
/**
- * Builds a route session info
+ * Removes a route from the groupable routes.
+ */
+ @NonNull
+ public Builder removeGroupableRoute(@NonNull String routeId) {
+ mGroupableRoutes.remove(Objects.requireNonNull(routeId, "routeId must not be null"));
+ return this;
+ }
+
+ /**
+ * Clears the transferrable routes.
+ */
+ @NonNull
+ public Builder clearTransferrableRoutes() {
+ mTransferrableRoutes.clear();
+ return this;
+ }
+
+ /**
+ * Adds a route to the transferrable routes.
+ */
+ @NonNull
+ public Builder addTransferrableRoute(@NonNull String routeId) {
+ mTransferrableRoutes.add(Objects.requireNonNull(routeId, "routeId must not be null"));
+ return this;
+ }
+
+ /**
+ * Removes a route from the transferrable routes.
+ */
+ @NonNull
+ public Builder removeTransferrableRoute(@NonNull String routeId) {
+ mTransferrableRoutes.remove(
+ Objects.requireNonNull(routeId, "routeId must not be null"));
+ return this;
+ }
+
+ /**
+ * Sets control hints.
+ */
+ @NonNull
+ public Builder setControlHints(@Nullable Bundle controlHints) {
+ mControlHints = controlHints;
+ return this;
+ }
+
+ /**
+ * Builds a route session info.
*/
@NonNull
public RouteSessionInfo build() {
diff --git a/media/java/android/media/tv/tuner/FilterEvent.java b/media/java/android/media/tv/tuner/FilterEvent.java
new file mode 100644
index 0000000..7c165ce
--- /dev/null
+++ b/media/java/android/media/tv/tuner/FilterEvent.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.tv.tuner;
+
+import android.os.NativeHandle;
+
+/**
+ * Demux filter event.
+ *
+ * @hide
+ */
+public abstract class FilterEvent {
+
+ /**
+ * Section event.
+ */
+ public static class SectionEvent extends FilterEvent {
+ private int mTableId;
+ private int mVersion;
+ private int mSectionNum;
+ private int mDataLength;
+ }
+
+ /**
+ * Media event.
+ */
+ public static class MediaEvent extends FilterEvent {
+ private int mStreamId;
+ private boolean mIsPtsPresent;
+ private long mPts;
+ private int mDataLength;
+ private NativeHandle mHandle;
+ private boolean mIsSecureMemory;
+ private int mMpuSequenceNumber;
+ private boolean mIsPrivateData;
+ private AudioExtraMetaData mExtraMetaData;
+ }
+
+ /**
+ * PES event.
+ */
+ public static class PesEvent extends FilterEvent {
+ private int mStreamId;
+ private int mDataLength;
+ private int mMpuSequenceNumber;
+ }
+
+ /**
+ * TS record event.
+ */
+ public static class TsRecordEvent extends FilterEvent {
+ private int mTpid;
+ private int mIndexMask;
+ private long mByteNumber;
+ }
+
+ /**
+ * MMPT record event.
+ */
+ public static class MmtpRecordEvent extends FilterEvent {
+ private int mScHevcIndexMask;
+ private long mByteNumber;
+ }
+
+ /**
+ * Download event.
+ */
+ public static class DownloadEvent extends FilterEvent {
+ private int mItemId;
+ private int mMpuSequenceNumber;
+ private int mItemFragmentIndex;
+ private int mLastItemFragmentIndex;
+ private int mDataLength;
+ }
+
+ /**
+ * IP payload event.
+ */
+ public static class IpPayloadEvent extends FilterEvent {
+ private int mDataLength;
+ }
+
+ /**
+ * TEMI event.
+ */
+ public static class TemiEvent extends FilterEvent {
+ private long mPts;
+ private byte mDescrTag;
+ private byte[] mDescrData;
+ }
+
+ /**
+ * Extra Meta Data from AD (Audio Descriptor) according to
+ * ETSI TS 101 154 V2.1.1.
+ */
+ public static class AudioExtraMetaData {
+ private byte mAdFade;
+ private byte mAdPan;
+ private byte mVersionTextTag;
+ private byte mAdGainCenter;
+ private byte mAdGainFront;
+ private byte mAdGainSurround;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/FrontendStatus.java b/media/java/android/media/tv/tuner/FrontendStatus.java
new file mode 100644
index 0000000..f8b2d12
--- /dev/null
+++ b/media/java/android/media/tv/tuner/FrontendStatus.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.tv.tuner;
+
+import android.media.tv.tuner.TunerConstants.FrontendDvbcSpectralInversion;
+import android.media.tv.tuner.TunerConstants.FrontendDvbtHierarchy;
+import android.media.tv.tuner.TunerConstants.FrontendInnerFec;
+import android.media.tv.tuner.TunerConstants.FrontendModulation;
+import android.media.tv.tuner.TunerConstants.FrontendStatusType;
+import android.media.tv.tuner.TunerConstants.LnbVoltage;
+
+/**
+ * Frontend status
+ *
+ * @hide
+ */
+public class FrontendStatus {
+
+ private final int mType;
+ private final Object mValue;
+
+ private FrontendStatus(int type, Object value) {
+ mType = type;
+ mValue = value;
+ }
+
+ /** Gets frontend status type. */
+ @FrontendStatusType
+ public int getStatusType() {
+ return mType;
+ }
+ /** Lock status for Demod in True/False. */
+ public boolean getIsDemodLocked() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_DEMOD_LOCK) {
+ throw new IllegalStateException();
+ }
+ return (Boolean) mValue;
+ }
+ /** SNR value measured by 0.001 dB. */
+ public int getSnr() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SNR) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** The number of error bit per 1 billion bits. */
+ public int getBer() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_BER) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** The number of error package per 1 billion packages. */
+ public int getPer() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PER) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** The number of error bit per 1 billion bits before FEC. */
+ public int getPerBer() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PRE_BER) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Signal Quality in percent. */
+ public int getSignalQuality() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SIGNAL_QUALITY) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Signal Strength measured by 0.001 dBm. */
+ public int getSignalStrength() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Symbols per second. */
+ public int getSymbolRate() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SYMBOL_RATE) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /**
+ * Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
+ * and ETSI EN 302 307-2 V1.1.1.
+ */
+ @FrontendInnerFec
+ public long getFec() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_FEC) {
+ throw new IllegalStateException();
+ }
+ return (long) mValue;
+ }
+ /** Modulation */
+ @FrontendModulation
+ public int getModulation() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_MODULATION) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Spectral Inversion for DVBC. */
+ @FrontendDvbcSpectralInversion
+ public int getSpectralInversion() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SPECTRAL) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Power Voltage Type for LNB. */
+ @LnbVoltage
+ public int getLnbVoltage() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNB_VOLTAGE) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** PLP ID */
+ public byte getPlpId() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PLP_ID) {
+ throw new IllegalStateException();
+ }
+ return (byte) mValue;
+ }
+ /** Emergency Warning Broadcasting System */
+ public boolean getIsEwbs() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_EWBS) {
+ throw new IllegalStateException();
+ }
+ return (Boolean) mValue;
+ }
+ /** AGC value is normalized from 0 to 255. */
+ public byte getAgc() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_AGC) {
+ throw new IllegalStateException();
+ }
+ return (byte) mValue;
+ }
+ /** LNA(Low Noise Amplifier) is on or not. */
+ public boolean getLnaOn() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNA) {
+ throw new IllegalStateException();
+ }
+ return (Boolean) mValue;
+ }
+ /** Error status by layer. */
+ public boolean[] getIsLayerError() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LAYER_ERROR) {
+ throw new IllegalStateException();
+ }
+ return (boolean[]) mValue;
+ }
+ /** CN value by VBER measured by 0.001 dB. */
+ public int getVberCn() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_VBER_CN) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** CN value by LBER measured by 0.001 dB. */
+ public int getLberCn() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LBER_CN) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** CN value by XER measured by 0.001 dB. */
+ public int getXerCn() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_XER_CN) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** MER value measured by 0.001 dB. */
+ public int getMer() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_MER) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Frequency difference in Hertz. */
+ public int getFreqOffset() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_FREQ_OFFSET) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Hierarchy Type for DVBT. */
+ @FrontendDvbtHierarchy
+ public int getHierarchy() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_HIERARCHY) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+ /** Lock status for RF. */
+ public boolean getIsRfLock() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_RF_LOCK) {
+ throw new IllegalStateException();
+ }
+ return (Boolean) mValue;
+ }
+ /** A list of PLP status for tuned PLPs for ATSC3 frontend. */
+ public Atsc3PlpInfo[] getAtsc3PlpInfo() {
+ if (mType != TunerConstants.FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO) {
+ throw new IllegalStateException();
+ }
+ return (Atsc3PlpInfo[]) mValue;
+ }
+
+ /** Status for each tuning PLPs. */
+ public static class Atsc3PlpInfo {
+ private final int mPlpId;
+ private final boolean mIsLock;
+ private final int mUec;
+
+ private Atsc3PlpInfo(int plpId, boolean isLock, int uec) {
+ mPlpId = plpId;
+ mIsLock = isLock;
+ mUec = uec;
+ }
+
+ /** Gets PLP IDs. */
+ public int getPlpId() {
+ return mPlpId;
+ }
+ /** Gets Demod Lock/Unlock status of this particular PLP. */
+ public boolean getIsLock() {
+ return mIsLock;
+ }
+ /**
+ * Gets Uncorrectable Error Counts (UEC) of this particular PLP since last tune
+ * operation.
+ */
+ public int getUec() {
+ return mUec;
+ }
+ }
+}
diff --git a/media/java/android/media/tv/tuner/ScanMessage.java b/media/java/android/media/tv/tuner/ScanMessage.java
new file mode 100644
index 0000000..35f54f8
--- /dev/null
+++ b/media/java/android/media/tv/tuner/ScanMessage.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media.tv.tuner;
+
+import android.media.tv.tuner.TunerConstants.ScanMessageType;
+
+/**
+ * Message from frontend during scan operations.
+ *
+ * @hide
+ */
+public class ScanMessage {
+ private final int mType;
+ private final Object mValue;
+
+ private ScanMessage(int type, Object value) {
+ mType = type;
+ mValue = value;
+ }
+
+ /** Gets scan message type. */
+ @ScanMessageType
+ public int getMessageType() {
+ return mType;
+ }
+ /** Message indicates whether frontend is locked or not. */
+ public boolean getIsLocked() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_LOCKED) {
+ throw new IllegalStateException();
+ }
+ return (Boolean) mValue;
+ }
+ /** Message indicates whether the scan has reached the end or not. */
+ public boolean getIsEnd() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_END) {
+ throw new IllegalStateException();
+ }
+ return (Boolean) mValue;
+ }
+ /** Progress message in percent. */
+ public int getProgressPercent() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PROGRESS_PERCENT) {
+ throw new IllegalStateException();
+ }
+ return (Integer) mValue;
+ }
+ /** Gets frequency. */
+ public int getFrequency() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_FREQUENCY) {
+ throw new IllegalStateException();
+ }
+ return (Integer) mValue;
+ }
+ /** Gets symbol rate. */
+ public int getSymbolRate() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_SYMBOL_RATE) {
+ throw new IllegalStateException();
+ }
+ return (Integer) mValue;
+ }
+ /** Gets PLP IDs. */
+ public int[] getPlpIds() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PLP_IDS) {
+ throw new IllegalStateException();
+ }
+ return (int[]) mValue;
+ }
+ /** Gets group IDs. */
+ public int[] getGroupIds() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_GROUP_IDS) {
+ throw new IllegalStateException();
+ }
+ return (int[]) mValue;
+ }
+ /** Gets Input stream IDs. */
+ public int[] getInputStreamIds() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS) {
+ throw new IllegalStateException();
+ }
+ return (int[]) mValue;
+ }
+ /** Gets the DVB-T or DVB-S standard. */
+ public int getStandard() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_STANDARD) {
+ throw new IllegalStateException();
+ }
+ return (int) mValue;
+ }
+
+ /** Gets PLP information for ATSC3. */
+ public Atsc3PlpInfo[] getAtsc3PlpInfos() {
+ if (mType != TunerConstants.SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO) {
+ throw new IllegalStateException();
+ }
+ return (Atsc3PlpInfo[]) mValue;
+ }
+
+ /** PLP information for ATSC3. */
+ public static class Atsc3PlpInfo {
+ private final int mPlpId;
+ private final boolean mLlsFlag;
+
+ private Atsc3PlpInfo(int plpId, boolean llsFlag) {
+ mPlpId = plpId;
+ mLlsFlag = llsFlag;
+ }
+
+ /** Gets PLP IDs. */
+ public int getPlpId() {
+ return mPlpId;
+ }
+ /** Gets LLS flag. */
+ public boolean getLlsFlag() {
+ return mLlsFlag;
+ }
+ }
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 62d069d..0ac0c72 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -24,6 +24,7 @@
import android.media.tv.tuner.TunerConstants.DemuxPidType;
import android.media.tv.tuner.TunerConstants.FilterSubtype;
import android.media.tv.tuner.TunerConstants.FilterType;
+import android.media.tv.tuner.TunerConstants.FrontendScanType;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -100,9 +101,10 @@
private native Frontend nativeOpenFrontendById(int id);
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
+ private native int nativeScan(int settingsType, FrontendSettings settings, int scanType);
private native int nativeSetLnb(int lnbId);
private native int nativeSetLna(boolean enable);
-
+ private native FrontendStatus[] nativeGetFrontendStatus(int[] statusTypes);
private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
private native List<Integer> nativeGetLnbIds();
@@ -123,6 +125,12 @@
* Invoked when there is a frontend event.
*/
void onEvent(int frontendEventType);
+
+ /**
+ * Invoked when there is a scan message.
+ * @param msg
+ */
+ void onScanMessage(ScanMessage msg);
}
/**
@@ -144,6 +152,10 @@
*/
public interface FilterCallback {
/**
+ * Invoked when there are filter events.
+ */
+ void onFilterEvent(FilterEvent[] events);
+ /**
* Invoked when filter status changed.
*/
void onFilterStatus(int status);
@@ -266,6 +278,14 @@
}
/**
+ * Scan channels.
+ * @hide
+ */
+ public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType) {
+ return nativeScan(settings.getType(), settings, scanType);
+ }
+
+ /**
* Sets Low-Noise Block downconverter (LNB) for satellite frontend.
*
* This assigns a hardware LNB resource to the satellite tuner. It can be
@@ -292,6 +312,21 @@
return nativeSetLna(enable);
}
+ /**
+ * Gets the statuses of the frontend.
+ *
+ * This retrieve the statuses of the frontend for given status types.
+ *
+ * @param statusTypes an array of status type which the caller request.
+ *
+ * @return statuses an array of statuses which response the caller's
+ * request.
+ * @hide
+ */
+ public FrontendStatus[] getFrontendStatus(int[] statusTypes) {
+ return nativeGetFrontendStatus(statusTypes);
+ }
+
private List<Integer> getFrontendIds() {
mFrontendIds = nativeGetFrontendIds();
return mFrontendIds;
@@ -424,26 +459,81 @@
* <p> Descrambler is a hardware component used to descramble data.
*
* <p> This class controls the TIS interaction with Tuner HAL.
- *
+ * TODO: make it static and extends Closable.
*/
public class Descrambler {
private long mNativeContext;
- private native boolean nativeAddPid(int pidType, int pid, Filter filter);
- private native boolean nativeRemovePid(int pidType, int pid, Filter filter);
+ private native int nativeAddPid(int pidType, int pid, Filter filter);
+ private native int nativeRemovePid(int pidType, int pid, Filter filter);
+ private native int nativeSetKeyToken(byte[] keyToken);
+ private native int nativeClose();
private Descrambler() {}
- /** @hide */
- public boolean addPid(@DemuxPidType int pidType, int pid, Filter filter) {
+ /**
+ * Add packets' PID to the descrambler for descrambling.
+ *
+ * The descrambler will start descrambling packets with this PID. Multiple PIDs can be added
+ * into one descrambler instance because descambling can happen simultaneously on packets
+ * from different PIDs.
+ *
+ * If the Descrambler previously contained a filter for the PID, the old filter is replaced
+ * by the specified filter.
+ *
+ * @param pidType the type of the PID.
+ * @param pid the PID of packets to start to be descrambled.
+ * @param filter an optional filter instance to identify upper stream.
+ * @return result status of the operation.
+ *
+ * @hide
+ */
+ public int addPid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
return nativeAddPid(pidType, pid, filter);
}
- /** @hide */
- public boolean removePid(@DemuxPidType int pidType, int pid, Filter filter) {
+ /**
+ * Remove packets' PID from the descrambler
+ *
+ * The descrambler will stop descrambling packets with this PID.
+ *
+ * @param pidType the type of the PID.
+ * @param pid the PID of packets to stop to be descrambled.
+ * @param filter an optional filter instance to identify upper stream.
+ * @return result status of the operation.
+ *
+ * @hide
+ */
+ public int removePid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
return nativeRemovePid(pidType, pid, filter);
}
+ /**
+ * Set a key token to link descrambler to a key slot
+ *
+ * A descrambler instance can have only one key slot to link, but a key slot can hold a few
+ * keys for different purposes.
+ *
+ * @param keyToken the token to be used to link the key slot.
+ * @return result status of the operation.
+ *
+ * @hide
+ */
+ public int setKeyToken(byte[] keyToken) {
+ return nativeSetKeyToken(keyToken);
+ }
+
+ /**
+ * Release the descrambler instance.
+ *
+ * @return result status of the operation.
+ *
+ * @hide
+ */
+ public int close() {
+ return nativeClose();
+ }
+
}
/**
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 4973b05..e79737a 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.hardware.tv.tuner.V1_0.Constants;
import java.lang.annotation.Retention;
@@ -75,6 +76,7 @@
public static final int DEMUX_T_PID = 1;
public static final int DEMUX_MMPT_PID = 2;
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({FRONTEND_SETTINGS_ANALOG, FRONTEND_SETTINGS_ATSC, FRONTEND_SETTINGS_ATSC3,
FRONTEND_SETTINGS_DVBS, FRONTEND_SETTINGS_DVBC, FRONTEND_SETTINGS_DVBT,
FRONTEND_SETTINGS_ISDBS, FRONTEND_SETTINGS_ISDBS3, FRONTEND_SETTINGS_ISDBT})
@@ -127,12 +129,483 @@
public static final int FILTER_SUBTYPE_PTP = 16;
@Retention(RetentionPolicy.SOURCE)
+ @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
+ public @interface FrontendScanType {}
+
+ public static final int FRONTEND_SCAN_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+ public static final int FRONTEND_SCAN_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+ public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SCAN_MESSAGE_TYPE_LOCKED, SCAN_MESSAGE_TYPE_END, SCAN_MESSAGE_TYPE_PROGRESS_PERCENT,
+ SCAN_MESSAGE_TYPE_FREQUENCY, SCAN_MESSAGE_TYPE_SYMBOL_RATE, SCAN_MESSAGE_TYPE_PLP_IDS,
+ SCAN_MESSAGE_TYPE_GROUP_IDS, SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS,
+ SCAN_MESSAGE_TYPE_STANDARD, SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO})
+ public @interface ScanMessageType {}
+
+ public static final int SCAN_MESSAGE_TYPE_LOCKED = Constants.FrontendScanMessageType.LOCKED;
+ public static final int SCAN_MESSAGE_TYPE_END = Constants.FrontendScanMessageType.END;
+ public static final int SCAN_MESSAGE_TYPE_PROGRESS_PERCENT =
+ Constants.FrontendScanMessageType.PROGRESS_PERCENT;
+ public static final int SCAN_MESSAGE_TYPE_FREQUENCY =
+ Constants.FrontendScanMessageType.FREQUENCY;
+ public static final int SCAN_MESSAGE_TYPE_SYMBOL_RATE =
+ Constants.FrontendScanMessageType.SYMBOL_RATE;
+ public static final int SCAN_MESSAGE_TYPE_PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS;
+ public static final int SCAN_MESSAGE_TYPE_GROUP_IDS =
+ Constants.FrontendScanMessageType.GROUP_IDS;
+ public static final int SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS =
+ Constants.FrontendScanMessageType.INPUT_STREAM_IDS;
+ public static final int SCAN_MESSAGE_TYPE_STANDARD =
+ Constants.FrontendScanMessageType.STANDARD;
+ public static final int SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO =
+ Constants.FrontendScanMessageType.ATSC3_PLP_INFO;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
+ FRONTEND_STATUS_TYPE_PER, FRONTEND_STATUS_TYPE_PRE_BER,
+ FRONTEND_STATUS_TYPE_SIGNAL_QUALITY, FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH,
+ FRONTEND_STATUS_TYPE_SYMBOL_RATE, FRONTEND_STATUS_TYPE_FEC,
+ FRONTEND_STATUS_TYPE_MODULATION, FRONTEND_STATUS_TYPE_SPECTRAL,
+ FRONTEND_STATUS_TYPE_LNB_VOLTAGE, FRONTEND_STATUS_TYPE_PLP_ID,
+ FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
+ FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_VBER_CN,
+ FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
+ FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
+ FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
+ public @interface FrontendStatusType {}
+
+ /**
+ * Lock status for Demod.
+ */
+ public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
+ Constants.FrontendStatusType.DEMOD_LOCK;
+ /**
+ * Signal to Noise Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
+ /**
+ * Bit Error Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
+ /**
+ * Packages Error Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
+ /**
+ * Bit Error Ratio before FEC.
+ */
+ public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
+ /**
+ * Signal Quality (0..100). Good data over total data in percent can be
+ * used as a way to present Signal Quality.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
+ Constants.FrontendStatusType.SIGNAL_QUALITY;
+ /**
+ * Signal Strength.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
+ Constants.FrontendStatusType.SIGNAL_STRENGTH;
+ /**
+ * Symbol Rate.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
+ Constants.FrontendStatusType.SYMBOL_RATE;
+ /**
+ * Forward Error Correction Type.
+ */
+ public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
+ /**
+ * Modulation Type.
+ */
+ public static final int FRONTEND_STATUS_TYPE_MODULATION =
+ Constants.FrontendStatusType.MODULATION;
+ /**
+ * Spectral Inversion Type.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
+ /**
+ * LNB Voltage.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
+ Constants.FrontendStatusType.LNB_VOLTAGE;
+ /**
+ * Physical Layer Pipe ID.
+ */
+ public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
+ /**
+ * Status for Emergency Warning Broadcasting System.
+ */
+ public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
+ /**
+ * Automatic Gain Control.
+ */
+ public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
+ /**
+ * Low Noise Amplifier.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
+ /**
+ * Error status by layer.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
+ Constants.FrontendStatusType.LAYER_ERROR;
+ /**
+ * CN value by VBER.
+ */
+ public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
+ /**
+ * CN value by LBER.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
+ /**
+ * CN value by XER.
+ */
+ public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
+ /**
+ * Moduration Error Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
+ /**
+ * Difference between tuning frequency and actual locked frequency.
+ */
+ public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
+ Constants.FrontendStatusType.FREQ_OFFSET;
+ /**
+ * Hierarchy for DVBT.
+ */
+ public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
+ /**
+ * Lock status for RF.
+ */
+ public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
+ /**
+ * PLP information in a frequency band for ATSC3.0 frontend.
+ */
+ public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
+ Constants.FrontendStatusType.ATSC3_PLP_INFO;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
+ FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8,
+ FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20,
+ FEC_11_45, FEC_13_18, FEC_13_45, FEC_14_45, FEC_23_36, FEC_25_36, FEC_26_45, FEC_28_45,
+ FEC_29_45, FEC_31_45, FEC_32_45, FEC_77_90})
+ public @interface FrontendInnerFec {}
+
+ /**
+ * FEC not defined
+ */
+ public static final long FEC_UNDEFINED = Constants.FrontendInnerFec.FEC_UNDEFINED;
+ /**
+ * hardware is able to detect and set FEC automatically
+ */
+ public static final long FEC_AUTO = Constants.FrontendInnerFec.AUTO;
+ /**
+ * 1/2 conv. code rate
+ */
+ public static final long FEC_1_2 = Constants.FrontendInnerFec.FEC_1_2;
+ /**
+ * 1/3 conv. code rate
+ */
+ public static final long FEC_1_3 = Constants.FrontendInnerFec.FEC_1_3;
+ /**
+ * 1/4 conv. code rate
+ */
+ public static final long FEC_1_4 = Constants.FrontendInnerFec.FEC_1_4;
+ /**
+ * 1/5 conv. code rate
+ */
+ public static final long FEC_1_5 = Constants.FrontendInnerFec.FEC_1_5;
+ /**
+ * 2/3 conv. code rate
+ */
+ public static final long FEC_2_3 = Constants.FrontendInnerFec.FEC_2_3;
+ /**
+ * 2/5 conv. code rate
+ */
+ public static final long FEC_2_5 = Constants.FrontendInnerFec.FEC_2_5;
+ /**
+ * 2/9 conv. code rate
+ */
+ public static final long FEC_2_9 = Constants.FrontendInnerFec.FEC_2_9;
+ /**
+ * 3/4 conv. code rate
+ */
+ public static final long FEC_3_4 = Constants.FrontendInnerFec.FEC_3_4;
+ /**
+ * 3/5 conv. code rate
+ */
+ public static final long FEC_3_5 = Constants.FrontendInnerFec.FEC_3_5;
+ /**
+ * 4/5 conv. code rate
+ */
+ public static final long FEC_4_5 = Constants.FrontendInnerFec.FEC_4_5;
+ /**
+ * 4/15 conv. code rate
+ */
+ public static final long FEC_4_15 = Constants.FrontendInnerFec.FEC_4_15;
+ /**
+ * 5/6 conv. code rate
+ */
+ public static final long FEC_5_6 = Constants.FrontendInnerFec.FEC_5_6;
+ /**
+ * 5/9 conv. code rate
+ */
+ public static final long FEC_5_9 = Constants.FrontendInnerFec.FEC_5_9;
+ /**
+ * 6/7 conv. code rate
+ */
+ public static final long FEC_6_7 = Constants.FrontendInnerFec.FEC_6_7;
+ /**
+ * 7/8 conv. code rate
+ */
+ public static final long FEC_7_8 = Constants.FrontendInnerFec.FEC_7_8;
+ /**
+ * 7/9 conv. code rate
+ */
+ public static final long FEC_7_9 = Constants.FrontendInnerFec.FEC_7_9;
+ /**
+ * 7/15 conv. code rate
+ */
+ public static final long FEC_7_15 = Constants.FrontendInnerFec.FEC_7_15;
+ /**
+ * 8/9 conv. code rate
+ */
+ public static final long FEC_8_9 = Constants.FrontendInnerFec.FEC_8_9;
+ /**
+ * 8/15 conv. code rate
+ */
+ public static final long FEC_8_15 = Constants.FrontendInnerFec.FEC_8_15;
+ /**
+ * 9/10 conv. code rate
+ */
+ public static final long FEC_9_10 = Constants.FrontendInnerFec.FEC_9_10;
+ /**
+ * 9/20 conv. code rate
+ */
+ public static final long FEC_9_20 = Constants.FrontendInnerFec.FEC_9_20;
+ /**
+ * 11/15 conv. code rate
+ */
+ public static final long FEC_11_15 = Constants.FrontendInnerFec.FEC_11_15;
+ /**
+ * 11/20 conv. code rate
+ */
+ public static final long FEC_11_20 = Constants.FrontendInnerFec.FEC_11_20;
+ /**
+ * 11/45 conv. code rate
+ */
+ public static final long FEC_11_45 = Constants.FrontendInnerFec.FEC_11_45;
+ /**
+ * 13/18 conv. code rate
+ */
+ public static final long FEC_13_18 = Constants.FrontendInnerFec.FEC_13_18;
+ /**
+ * 13/45 conv. code rate
+ */
+ public static final long FEC_13_45 = Constants.FrontendInnerFec.FEC_13_45;
+ /**
+ * 14/45 conv. code rate
+ */
+ public static final long FEC_14_45 = Constants.FrontendInnerFec.FEC_14_45;
+ /**
+ * 23/36 conv. code rate
+ */
+ public static final long FEC_23_36 = Constants.FrontendInnerFec.FEC_23_36;
+ /**
+ * 25/36 conv. code rate
+ */
+ public static final long FEC_25_36 = Constants.FrontendInnerFec.FEC_25_36;
+ /**
+ * 26/45 conv. code rate
+ */
+ public static final long FEC_26_45 = Constants.FrontendInnerFec.FEC_26_45;
+ /**
+ * 28/45 conv. code rate
+ */
+ public static final long FEC_28_45 = Constants.FrontendInnerFec.FEC_28_45;
+ /**
+ * 29/45 conv. code rate
+ */
+ public static final long FEC_29_45 = Constants.FrontendInnerFec.FEC_29_45;
+ /**
+ * 31/45 conv. code rate
+ */
+ public static final long FEC_31_45 = Constants.FrontendInnerFec.FEC_31_45;
+ /**
+ * 32/45 conv. code rate
+ */
+ public static final long FEC_32_45 = Constants.FrontendInnerFec.FEC_32_45;
+ /**
+ * 77/90 conv. code rate
+ */
+ public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DVBC_MODULATION_UNDEFINED, DVBC_MODULATION_AUTO, DVBC_MODULATION_MOD_16QAM,
+ DVBC_MODULATION_MOD_32QAM, DVBC_MODULATION_MOD_64QAM, DVBC_MODULATION_MOD_128QAM,
+ DVBC_MODULATION_MOD_256QAM, DVBS_MODULATION_UNDEFINED, DVBS_MODULATION_AUTO,
+ DVBS_MODULATION_MOD_QPSK, DVBS_MODULATION_MOD_8PSK, DVBS_MODULATION_MOD_16QAM,
+ DVBS_MODULATION_MOD_16PSK, DVBS_MODULATION_MOD_32PSK, DVBS_MODULATION_MOD_ACM,
+ DVBS_MODULATION_MOD_8APSK, DVBS_MODULATION_MOD_16APSK, DVBS_MODULATION_MOD_32APSK,
+ DVBS_MODULATION_MOD_64APSK, DVBS_MODULATION_MOD_128APSK, DVBS_MODULATION_MOD_256APSK,
+ DVBS_MODULATION_MOD_RESERVED, ISDBS_MODULATION_UNDEFINED, ISDBS_MODULATION_AUTO,
+ ISDBS_MODULATION_MOD_BPSK, ISDBS_MODULATION_MOD_QPSK, ISDBS_MODULATION_MOD_TC8PSK,
+ ISDBS3_MODULATION_UNDEFINED, ISDBS3_MODULATION_AUTO, ISDBS3_MODULATION_MOD_BPSK,
+ ISDBS3_MODULATION_MOD_QPSK, ISDBS3_MODULATION_MOD_8PSK, ISDBS3_MODULATION_MOD_16APSK,
+ ISDBS3_MODULATION_MOD_32APSK, ISDBT_MODULATION_UNDEFINED, ISDBT_MODULATION_AUTO,
+ ISDBT_MODULATION_MOD_DQPSK, ISDBT_MODULATION_MOD_QPSK, ISDBT_MODULATION_MOD_16QAM,
+ ISDBT_MODULATION_MOD_64QAM})
+ public @interface FrontendModulation {}
+
+ public static final int DVBC_MODULATION_UNDEFINED = Constants.FrontendDvbcModulation.UNDEFINED;
+ public static final int DVBC_MODULATION_AUTO = Constants.FrontendDvbcModulation.AUTO;
+ public static final int DVBC_MODULATION_MOD_16QAM = Constants.FrontendDvbcModulation.MOD_16QAM;
+ public static final int DVBC_MODULATION_MOD_32QAM = Constants.FrontendDvbcModulation.MOD_32QAM;
+ public static final int DVBC_MODULATION_MOD_64QAM = Constants.FrontendDvbcModulation.MOD_64QAM;
+ public static final int DVBC_MODULATION_MOD_128QAM =
+ Constants.FrontendDvbcModulation.MOD_128QAM;
+ public static final int DVBC_MODULATION_MOD_256QAM =
+ Constants.FrontendDvbcModulation.MOD_256QAM;
+ public static final int DVBS_MODULATION_UNDEFINED = Constants.FrontendDvbsModulation.UNDEFINED;
+ public static final int DVBS_MODULATION_AUTO = Constants.FrontendDvbsModulation.AUTO;
+ public static final int DVBS_MODULATION_MOD_QPSK = Constants.FrontendDvbsModulation.MOD_QPSK;
+ public static final int DVBS_MODULATION_MOD_8PSK = Constants.FrontendDvbsModulation.MOD_8PSK;
+ public static final int DVBS_MODULATION_MOD_16QAM = Constants.FrontendDvbsModulation.MOD_16QAM;
+ public static final int DVBS_MODULATION_MOD_16PSK = Constants.FrontendDvbsModulation.MOD_16PSK;
+ public static final int DVBS_MODULATION_MOD_32PSK = Constants.FrontendDvbsModulation.MOD_32PSK;
+ public static final int DVBS_MODULATION_MOD_ACM = Constants.FrontendDvbsModulation.MOD_ACM;
+ public static final int DVBS_MODULATION_MOD_8APSK = Constants.FrontendDvbsModulation.MOD_8APSK;
+ public static final int DVBS_MODULATION_MOD_16APSK =
+ Constants.FrontendDvbsModulation.MOD_16APSK;
+ public static final int DVBS_MODULATION_MOD_32APSK =
+ Constants.FrontendDvbsModulation.MOD_32APSK;
+ public static final int DVBS_MODULATION_MOD_64APSK =
+ Constants.FrontendDvbsModulation.MOD_64APSK;
+ public static final int DVBS_MODULATION_MOD_128APSK =
+ Constants.FrontendDvbsModulation.MOD_128APSK;
+ public static final int DVBS_MODULATION_MOD_256APSK =
+ Constants.FrontendDvbsModulation.MOD_256APSK;
+ public static final int DVBS_MODULATION_MOD_RESERVED =
+ Constants.FrontendDvbsModulation.MOD_RESERVED;
+ public static final int ISDBS_MODULATION_UNDEFINED =
+ Constants.FrontendIsdbsModulation.UNDEFINED;
+ public static final int ISDBS_MODULATION_AUTO = Constants.FrontendIsdbsModulation.AUTO;
+ public static final int ISDBS_MODULATION_MOD_BPSK = Constants.FrontendIsdbsModulation.MOD_BPSK;
+ public static final int ISDBS_MODULATION_MOD_QPSK = Constants.FrontendIsdbsModulation.MOD_QPSK;
+ public static final int ISDBS_MODULATION_MOD_TC8PSK =
+ Constants.FrontendIsdbsModulation.MOD_TC8PSK;
+ public static final int ISDBS3_MODULATION_UNDEFINED =
+ Constants.FrontendIsdbs3Modulation.UNDEFINED;
+ public static final int ISDBS3_MODULATION_AUTO = Constants.FrontendIsdbs3Modulation.AUTO;
+ public static final int ISDBS3_MODULATION_MOD_BPSK =
+ Constants.FrontendIsdbs3Modulation.MOD_BPSK;
+ public static final int ISDBS3_MODULATION_MOD_QPSK =
+ Constants.FrontendIsdbs3Modulation.MOD_QPSK;
+ public static final int ISDBS3_MODULATION_MOD_8PSK =
+ Constants.FrontendIsdbs3Modulation.MOD_8PSK;
+ public static final int ISDBS3_MODULATION_MOD_16APSK =
+ Constants.FrontendIsdbs3Modulation.MOD_16APSK;
+ public static final int ISDBS3_MODULATION_MOD_32APSK =
+ Constants.FrontendIsdbs3Modulation.MOD_32APSK;
+ public static final int ISDBT_MODULATION_UNDEFINED =
+ Constants.FrontendIsdbtModulation.UNDEFINED;
+ public static final int ISDBT_MODULATION_AUTO = Constants.FrontendIsdbtModulation.AUTO;
+ public static final int ISDBT_MODULATION_MOD_DQPSK =
+ Constants.FrontendIsdbtModulation.MOD_DQPSK;
+ public static final int ISDBT_MODULATION_MOD_QPSK = Constants.FrontendIsdbtModulation.MOD_QPSK;
+ public static final int ISDBT_MODULATION_MOD_16QAM =
+ Constants.FrontendIsdbtModulation.MOD_16QAM;
+ public static final int ISDBT_MODULATION_MOD_64QAM =
+ Constants.FrontendIsdbtModulation.MOD_64QAM;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SPECTRAL_INVERSION_UNDEFINED, SPECTRAL_INVERSION_NORMAL, SPECTRAL_INVERSION_INVERTED})
+ public @interface FrontendDvbcSpectralInversion {}
+
+ public static final int SPECTRAL_INVERSION_UNDEFINED =
+ Constants.FrontendDvbcSpectralInversion.UNDEFINED;
+ public static final int SPECTRAL_INVERSION_NORMAL =
+ Constants.FrontendDvbcSpectralInversion.NORMAL;
+ public static final int SPECTRAL_INVERSION_INVERTED =
+ Constants.FrontendDvbcSpectralInversion.INVERTED;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LNB_VOLTAGE_NONE, LNB_VOLTAGE_VOLTAGE_5V, LNB_VOLTAGE_VOLTAGE_11V,
+ LNB_VOLTAGE_VOLTAGE_12V, LNB_VOLTAGE_VOLTAGE_13V, LNB_VOLTAGE_VOLTAGE_14V,
+ LNB_VOLTAGE_VOLTAGE_15V, LNB_VOLTAGE_VOLTAGE_18V})
+ public @interface LnbVoltage {}
+
+ public static final int LNB_VOLTAGE_NONE = Constants.LnbVoltage.NONE;
+ public static final int LNB_VOLTAGE_VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V;
+ public static final int LNB_VOLTAGE_VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V;
+ public static final int LNB_VOLTAGE_VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V;
+ public static final int LNB_VOLTAGE_VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V;
+ public static final int LNB_VOLTAGE_VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V;
+ public static final int LNB_VOLTAGE_VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V;
+ public static final int LNB_VOLTAGE_VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({HIERARCHY_UNDEFINED, HIERARCHY_AUTO, HIERARCHY_NON_NATIVE, HIERARCHY_1_NATIVE,
+ HIERARCHY_2_NATIVE, HIERARCHY_4_NATIVE, HIERARCHY_NON_INDEPTH, HIERARCHY_1_INDEPTH,
+ HIERARCHY_2_INDEPTH, HIERARCHY_4_INDEPTH})
+ public @interface FrontendDvbtHierarchy {}
+
+ public static final int HIERARCHY_UNDEFINED = Constants.FrontendDvbtHierarchy.UNDEFINED;
+ public static final int HIERARCHY_AUTO = Constants.FrontendDvbtHierarchy.AUTO;
+ public static final int HIERARCHY_NON_NATIVE =
+ Constants.FrontendDvbtHierarchy.HIERARCHY_NON_NATIVE;
+ public static final int HIERARCHY_1_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_1_NATIVE;
+ public static final int HIERARCHY_2_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_2_NATIVE;
+ public static final int HIERARCHY_4_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_4_NATIVE;
+ public static final int HIERARCHY_NON_INDEPTH =
+ Constants.FrontendDvbtHierarchy.HIERARCHY_NON_INDEPTH;
+ public static final int HIERARCHY_1_INDEPTH =
+ Constants.FrontendDvbtHierarchy.HIERARCHY_1_INDEPTH;
+ public static final int HIERARCHY_2_INDEPTH =
+ Constants.FrontendDvbtHierarchy.HIERARCHY_2_INDEPTH;
+ public static final int HIERARCHY_4_INDEPTH =
+ Constants.FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV,
+ FILTER_SETTINGS_ALP})
+ public @interface FilterSettingsType {}
+
+ public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS;
+ public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP;
+ public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP;
+ public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
+ public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
+
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({DVR_SETTINGS_RECORD, DVR_SETTINGS_PLAYBACK})
public @interface DvrSettingsType {}
public static final int DVR_SETTINGS_RECORD = Constants.DvrType.RECORD;
public static final int DVR_SETTINGS_PLAYBACK = Constants.DvrType.PLAYBACK;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
+ RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
+ public @interface Result {}
+
+ public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
+ public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
+ public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
+ public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
+ public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
+ public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
+ public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
+
private TunerConstants() {
}
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 4a2baed..3fd14de 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -312,6 +312,15 @@
return (int)result;
}
+int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType) {
+ if (mFe == NULL) {
+ ALOGE("frontend is not initialized");
+ return (int)Result::INVALID_STATE;
+ }
+ Result result = mFe->scan(settings, scanType);
+ return (int)result;
+}
+
bool JTuner::openDemux() {
if (mTuner == nullptr) {
return false;
@@ -604,6 +613,13 @@
return 0;
}
+static int android_media_tv_Tuner_scan(
+ JNIEnv *env, jobject thiz, jint settingsType, jobject settings, jint scanType) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->scan(getFrontendSettings(
+ env, settingsType, settings), static_cast<FrontendScanType>(scanType));
+}
+
static int android_media_tv_Tuner_set_lnb(JNIEnv, jobject, jint) {
return 0;
}
@@ -612,6 +628,10 @@
return 0;
}
+static jobjectArray android_media_tv_Tuner_get_frontend_status(JNIEnv, jobject, jintArray) {
+ return NULL;
+}
+
static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->getLnbIds();
@@ -769,7 +789,7 @@
return tuner->openDescrambler();
}
-static bool android_media_tv_Tuner_add_pid(
+static int android_media_tv_Tuner_add_pid(
JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
if (descramblerSp == NULL) {
@@ -777,10 +797,10 @@
}
sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), filterSp);
- return result == Result::SUCCESS;
+ return (int)result;
}
-static bool android_media_tv_Tuner_remove_pid(
+static int android_media_tv_Tuner_remove_pid(
JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
if (descramblerSp == NULL) {
@@ -788,7 +808,15 @@
}
sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), filterSp);
- return result == Result::SUCCESS;
+ return (int)result;
+}
+
+static int android_media_tv_Tuner_set_key_token(JNIEnv, jobject, jbyteArray) {
+ return 0;
+}
+
+static int android_media_tv_Tuner_close_descrambler(JNIEnv, jobject) {
+ return 0;
}
static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint type, jint bufferSize) {
@@ -863,8 +891,12 @@
{ "nativeTune", "(ILandroid/media/tv/tuner/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
+ { "nativeScan", "(ILandroid/media/tv/tuner/FrontendSettings;I)I",
+ (void *)android_media_tv_Tuner_scan },
{ "nativeSetLnb", "(I)I", (void *)android_media_tv_Tuner_set_lnb },
{ "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna },
+ { "nativeGetFrontendStatus", "([I)[Landroid/media/tv/tuner/FrontendStatus;",
+ (void *)android_media_tv_Tuner_get_frontend_status },
{ "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
(void *)android_media_tv_Tuner_open_filter },
{ "nativeGetLnbIds", "()Ljava/util/List;",
@@ -887,10 +919,12 @@
};
static const JNINativeMethod gDescramblerMethods[] = {
- { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+ { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
(void *)android_media_tv_Tuner_add_pid },
- { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+ { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
(void *)android_media_tv_Tuner_remove_pid },
+ { "nativeSetKeyToken", "([B)I", (void *)android_media_tv_Tuner_set_key_token },
+ { "nativeClose", "()V", (void *)android_media_tv_Tuner_close_descrambler },
};
static const JNINativeMethod gDvrMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 9f9fb27..d37a2d9 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -39,6 +39,7 @@
using ::android::hardware::tv::tuner::V1_0::FrontendId;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
+using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
using ::android::hardware::tv::tuner::V1_0::IDemux;
using ::android::hardware::tv::tuner::V1_0::IDescrambler;
@@ -122,6 +123,7 @@
jobject getFrontendIds();
jobject openFrontendById(int id);
int tune(const FrontendSettings& settings);
+ int scan(const FrontendSettings& settings, FrontendScanType scanType);
jobject getLnbIds();
jobject openLnbById(int id);
jobject openFilter(DemuxFilterType type, int bufferSize);
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index 6650f96..d04c9b2 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -16,12 +16,17 @@
package com.android.mediarouteprovider.example;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_TV;
+
import android.content.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.RouteSessionInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.text.TextUtils;
import java.util.HashMap;
import java.util.Map;
@@ -33,6 +38,9 @@
public static final String ROUTE_NAME1 = "Sample Route 1";
public static final String ROUTE_ID2 = "route_id2";
public static final String ROUTE_NAME2 = "Sample Route 2";
+ public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
+ "route_id3_session_creation_failed";
+ public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
@@ -51,14 +59,22 @@
public static final String CATEGORY_SPECIAL =
"com.android.mediarouteprovider.CATEGORY_SPECIAL";
+ public static final int SESSION_ID_1 = 1000;
+
Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
private void initializeRoutes() {
MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
.addSupportedCategory(CATEGORY_SAMPLE)
+ .setDeviceType(DEVICE_TYPE_TV)
.build();
MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2)
.addSupportedCategory(CATEGORY_SAMPLE)
+ .setDeviceType(DEVICE_TYPE_SPEAKER)
+ .build();
+ MediaRoute2Info route3 = new MediaRoute2Info.Builder(
+ ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3)
+ .addSupportedCategory(CATEGORY_SAMPLE)
.build();
MediaRoute2Info routeSpecial =
new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY)
@@ -79,6 +95,7 @@
mRoutes.put(route1.getId(), route1);
mRoutes.put(route2.getId(), route2);
+ mRoutes.put(route3.getId(), route3);
mRoutes.put(routeSpecial.getId(), routeSpecial);
mRoutes.put(fixedVolumeRoute.getId(), fixedVolumeRoute);
mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
@@ -122,7 +139,8 @@
@Override
public void onControlRequest(String routeId, Intent request) {
- if (ACTION_REMOVE_ROUTE.equals(request.getAction())) {
+ String action = request.getAction();
+ if (ACTION_REMOVE_ROUTE.equals(action)) {
MediaRoute2Info route = mRoutes.get(routeId);
if (route != null) {
mRoutes.remove(routeId);
@@ -159,10 +177,61 @@
publishRoutes();
}
+ @Override
+ public void onCreateSession(String packageName, String routeId, String controlCategory,
+ int requestId) {
+ if (TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
+ // Tell the router that session cannot be created by passing null as sessionInfo.
+ notifySessionCreated(/* sessionInfo= */ null, requestId);
+ return;
+ }
+
+ RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ SESSION_ID_1, packageName, controlCategory)
+ .addSelectedRoute(routeId)
+ .build();
+ notifySessionCreated(sessionInfo, requestId);
+ }
+
+ @Override
+ public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) {}
+
+ @Override
+ public void onAddRoute(int sessionId, String routeId) {
+ RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+ //TODO: we may want to remove route if it belongs to another session
+ RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ .addSelectedRoute(routeId)
+ .build();
+ updateSessionInfo(newSessionInfo);
+ publishRoutes();
+ }
+
+ @Override
+ public void onRemoveRoute(int sessionId, String routeId) {
+ RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+ RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ .removeSelectedRoute(routeId)
+ .build();
+ updateSessionInfo(newSessionInfo);
+ publishRoutes();
+ }
+
+ @Override
+ public void onTransferRoute(int sessionId, String routeId) {
+ RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+ RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ .clearSelectedRoutes()
+ .addSelectedRoute(routeId)
+ .build();
+ updateSessionInfo(newSessionInfo);
+ publishRoutes();
+ }
+
void publishRoutes() {
MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
.addRoutes(mRoutes.values())
.build();
- setProviderInfo(info);
+ updateProviderInfo(info);
}
}
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 611b25a..5a0a50c 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -11,6 +11,7 @@
static_libs: [
"android-support-test",
"mockito-target-minus-junit4",
+ "testng"
],
platform_apis: true,
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 3266285..8fe28d9 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -16,19 +16,37 @@
package com.android.mediaroutertest;
+import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTED;
+import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTING;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_TV;
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+
import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_ALL;
import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_SPECIAL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SAMPLE;
+import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SPECIAL;
+import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1;
+import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_CATEGORY;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_VARIABLE_VOLUME;
import static com.android.mediaroutertest.MediaRouterManagerTest.SYSTEM_PROVIDER_ID;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
+import android.media.MediaRouter2.SessionCreationCallback;
+import android.media.RouteSessionController;
+import android.net.Uri;
+import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -39,6 +57,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -69,13 +88,6 @@
public void tearDown() throws Exception {
}
- @Test
- public void testGetSelectedRoute_afterCreation() throws Exception {
- MediaRouter2 router = MediaRouter2.getInstance(mContext);
- MediaRoute2Info initiallySelectedRoute = router.getSelectedRoute();
- assertNotNull(initiallySelectedRoute);
- }
-
/**
* Tests if we get proper routes for application that has special control category.
*/
@@ -88,6 +100,90 @@
}
@Test
+ public void testRouteInfoEquality() {
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder("id", "name")
+ .setDescription("description")
+ .setClientPackageName("com.android.mediaroutertest")
+ .setConnectionState(CONNECTION_STATE_CONNECTING)
+ .setIconUri(new Uri.Builder().path("icon").build())
+ .setVolume(5)
+ .setVolumeMax(20)
+ .addSupportedCategory(CATEGORY_SAMPLE)
+ .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
+ .setDeviceType(DEVICE_TYPE_SPEAKER)
+ .build();
+
+ MediaRoute2Info routeInfoRebuilt = new MediaRoute2Info.Builder(routeInfo).build();
+ assertEquals(routeInfo, routeInfoRebuilt);
+
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(routeInfo, 0);
+ parcel.setDataPosition(0);
+ MediaRoute2Info routeInfoFromParcel = parcel.readParcelable(null);
+
+ assertEquals(routeInfo, routeInfoFromParcel);
+ }
+
+ @Test
+ public void testRouteInfoInequality() {
+ MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name")
+ .setDescription("description")
+ .setClientPackageName("com.android.mediaroutertest")
+ .setConnectionState(CONNECTION_STATE_CONNECTING)
+ .setIconUri(new Uri.Builder().path("icon").build())
+ .addSupportedCategory(CATEGORY_SAMPLE)
+ .setVolume(5)
+ .setVolumeMax(20)
+ .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
+ .setDeviceType(DEVICE_TYPE_SPEAKER)
+ .build();
+
+ MediaRoute2Info routeId = new MediaRoute2Info.Builder(route)
+ .setId("another id").build();
+ assertNotEquals(route, routeId);
+
+ MediaRoute2Info routeName = new MediaRoute2Info.Builder(route)
+ .setName("another name").build();
+ assertNotEquals(route, routeName);
+
+ MediaRoute2Info routeDescription = new MediaRoute2Info.Builder(route)
+ .setDescription("another description").build();
+ assertNotEquals(route, routeDescription);
+
+ MediaRoute2Info routeConnectionState = new MediaRoute2Info.Builder(route)
+ .setConnectionState(CONNECTION_STATE_CONNECTED).build();
+ assertNotEquals(route, routeConnectionState);
+
+ MediaRoute2Info routeIcon = new MediaRoute2Info.Builder(route)
+ .setIconUri(new Uri.Builder().path("new icon").build()).build();
+ assertNotEquals(route, routeIcon);
+
+ MediaRoute2Info routeClient = new MediaRoute2Info.Builder(route)
+ .setClientPackageName("another.client.package").build();
+ assertNotEquals(route, routeClient);
+
+ MediaRoute2Info routeCategory = new MediaRoute2Info.Builder(route)
+ .addSupportedCategory(CATEGORY_SPECIAL).build();
+ assertNotEquals(route, routeCategory);
+
+ MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route)
+ .setVolume(10).build();
+ assertNotEquals(route, routeVolume);
+
+ MediaRoute2Info routeVolumeMax = new MediaRoute2Info.Builder(route)
+ .setVolumeMax(30).build();
+ assertNotEquals(route, routeVolumeMax);
+
+ MediaRoute2Info routeVolumeHandling = new MediaRoute2Info.Builder(route)
+ .setVolumeHandling(PLAYBACK_VOLUME_FIXED).build();
+ assertNotEquals(route, routeVolumeHandling);
+
+ MediaRoute2Info routeDeviceType = new MediaRoute2Info.Builder(route)
+ .setVolume(DEVICE_TYPE_TV).build();
+ assertNotEquals(route, routeDeviceType);
+ }
+
+ @Test
public void testControlVolumeWithRouter() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL);
@@ -108,6 +204,109 @@
(route -> route.getVolume() == originalVolume));
}
+ @Test
+ public void testRequestCreateSessionWithInvalidArguments() {
+ MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build();
+ String controlCategory = "controlCategory";
+ Executor executor = mExecutor;
+ MediaRouter2.SessionCreationCallback callback = new MediaRouter2.SessionCreationCallback();
+
+ // Tests null route
+ assertThrows(NullPointerException.class,
+ () -> mRouter2.requestCreateSession(null, controlCategory, executor, callback));
+
+ // Tests null or empty control category
+ assertThrows(IllegalArgumentException.class,
+ () -> mRouter2.requestCreateSession(route, null, executor, callback));
+ assertThrows(IllegalArgumentException.class,
+ () -> mRouter2.requestCreateSession(route, "", executor, callback));
+
+ // Tests null executor
+ assertThrows(NullPointerException.class,
+ () -> mRouter2.requestCreateSession(route, controlCategory, null, callback));
+
+ // Tests null callback
+ assertThrows(NullPointerException.class,
+ () -> mRouter2.requestCreateSession(route, controlCategory, executor, null));
+ }
+
+ @Test
+ public void testRequestCreateSessionSuccess() throws Exception {
+ final List<String> sampleControlCategory = new ArrayList<>();
+ sampleControlCategory.add(CATEGORY_SAMPLE);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+ MediaRoute2Info route = routes.get(ROUTE_ID1);
+ assertNotNull(route);
+
+ final CountDownLatch successLatch = new CountDownLatch(1);
+ final CountDownLatch failureLatch = new CountDownLatch(1);
+
+ // Create session with this route
+ SessionCreationCallback callback = new SessionCreationCallback() {
+ @Override
+ public void onSessionCreated(RouteSessionController controller) {
+ assertNotNull(controller);
+ assertTrue(controller.getSelectedRoutes().contains(ROUTE_ID1));
+ assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getCategory()));
+ successLatch.countDown();
+ }
+
+ @Override
+ public void onSessionCreationFailed() {
+ failureLatch.countDown();
+ }
+ };
+
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ mRouter2.registerCallback(mExecutor, new MediaRouter2.RouteCallback());
+
+ mRouter2.requestCreateSession(route, CATEGORY_SAMPLE, mExecutor, callback);
+ assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // onSessionCreationFailed should not be called.
+ assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testRequestCreateSessionFailure() throws Exception {
+ final List<String> sampleControlCategory = new ArrayList<>();
+ sampleControlCategory.add(CATEGORY_SAMPLE);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+ MediaRoute2Info route = routes.get(ROUTE_ID3_SESSION_CREATION_FAILED);
+ assertNotNull(route);
+
+ final CountDownLatch successLatch = new CountDownLatch(1);
+ final CountDownLatch failureLatch = new CountDownLatch(1);
+
+ // Create session with this route
+ SessionCreationCallback callback = new SessionCreationCallback() {
+ @Override
+ public void onSessionCreated(RouteSessionController controller) {
+ successLatch.countDown();
+ }
+
+ @Override
+ public void onSessionCreationFailed() {
+ failureLatch.countDown();
+ }
+ };
+
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ mRouter2.registerCallback(mExecutor, new MediaRouter2.RouteCallback());
+
+ mRouter2.requestCreateSession(route, CATEGORY_SAMPLE, mExecutor, callback);
+ assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // onSessionCreated should not be called.
+ assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testRequestCreateSessionMultipleSessions() throws Exception {
+ // TODO: Test creating multiple sessions (Check the ID of each controller)
+ }
// Helper for getting routes easily
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
@@ -124,7 +323,8 @@
CountDownLatch latch = new CountDownLatch(1);
// A dummy callback is required to send control category info.
- MediaRouter2.Callback routerCallback = new MediaRouter2.Callback() {
+ MediaRouter2.RouteCallback
+ routeCallback = new MediaRouter2.RouteCallback() {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
for (int i = 0; i < routes.size(); i++) {
@@ -137,19 +337,20 @@
};
mRouter2.setControlCategories(controlCategories);
- mRouter2.registerCallback(mExecutor, routerCallback);
+ mRouter2.registerCallback(mExecutor, routeCallback);
try {
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
return createRouteMap(mRouter2.getRoutes());
} finally {
- mRouter2.unregisterCallback(routerCallback);
+ mRouter2.unregisterCallback(routeCallback);
}
}
void awaitOnRouteChanged(Runnable task, String routeId,
Predicate<MediaRoute2Info> predicate) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- MediaRouter2.Callback callback = new MediaRouter2.Callback() {
+ MediaRouter2.RouteCallback
+ routeCallback = new MediaRouter2.RouteCallback() {
@Override
public void onRoutesChanged(List<MediaRoute2Info> changed) {
MediaRoute2Info route = createRouteMap(changed).get(routeId);
@@ -158,12 +359,12 @@
}
}
};
- mRouter2.registerCallback(mExecutor, callback);
+ mRouter2.registerCallback(mExecutor, routeCallback);
try {
task.run();
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
- mRouter2.unregisterCallback(callback);
+ mRouter2.unregisterCallback(routeCallback);
}
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 2772aa4..9761e6d 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -28,8 +28,8 @@
import android.content.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
+import android.media.MediaRouter2.RouteCallback;
import android.media.MediaRouter2Manager;
-import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -60,6 +60,9 @@
public static final String ROUTE_NAME1 = "Sample Route 1";
public static final String ROUTE_ID2 = "route_id2";
public static final String ROUTE_NAME2 = "Sample Route 2";
+ public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
+ "route_id3_session_creation_failed";
+ public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
@@ -92,7 +95,8 @@
private String mPackageName;
private final List<MediaRouter2Manager.Callback> mManagerCallbacks = new ArrayList<>();
- private final List<MediaRouter2.Callback> mRouterCallbacks = new ArrayList<>();
+ private final List<RouteCallback> mRouteCallbacks =
+ new ArrayList<>();
public static final List<String> CATEGORIES_ALL = new ArrayList();
public static final List<String> CATEGORIES_SPECIAL = new ArrayList();
@@ -164,7 +168,7 @@
CountDownLatch latch = new CountDownLatch(1);
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
- addRouterCallback(new MediaRouter2.Callback());
+ addRouterCallback(new RouteCallback());
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRoutesRemoved(List<MediaRoute2Info> routes) {
@@ -196,29 +200,30 @@
/**
* Tests if MR2.Callback.onRouteSelected is called when a route is selected from MR2Manager.
+ *
+ * TODO: Change this test so that this test check whether the route is added in a session.
+ * Until then, temporailiy removing @Test annotation.
*/
- @Test
public void testRouterOnRouteSelected() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
CountDownLatch latch = new CountDownLatch(1);
addManagerCallback(new MediaRouter2Manager.Callback());
- addRouterCallback(new MediaRouter2.Callback() {
- @Override
- public void onRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
- if (route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
- latch.countDown();
- }
- }
- });
+// addRouterCallback(new RouteDiscoveryCallback() {
+// @Override
+// public void onRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
+// if (route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
+// latch.countDown();
+// }
+// }
+// });
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
assertNotNull(routeToSelect);
try {
mManager.selectRoute(mPackageName, routeToSelect);
-
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mManager.unselectRoute(mPackageName);
@@ -234,7 +239,7 @@
CountDownLatch latch = new CountDownLatch(1);
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
- addRouterCallback(new MediaRouter2.Callback());
+ addRouterCallback(new RouteCallback());
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRouteSelected(String packageName, MediaRoute2Info route) {
@@ -262,7 +267,7 @@
CountDownLatch latch2 = new CountDownLatch(1);
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
- addRouterCallback(new MediaRouter2.Callback());
+ addRouterCallback(new RouteCallback());
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRouteSelected(String packageName, MediaRoute2Info route) {
@@ -293,7 +298,7 @@
@Test
public void testSingleProviderSelect() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
- addRouterCallback(new MediaRouter2.Callback());
+ addRouterCallback(new RouteCallback());
awaitOnRouteChangedManager(
() -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
@@ -347,7 +352,7 @@
CountDownLatch latch = new CountDownLatch(2);
// A dummy callback is required to send control category info.
- MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
+ RouteCallback routeCallback = new RouteCallback();
MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
@@ -362,19 +367,20 @@
@Override
public void onControlCategoriesChanged(String packageName, List<String> categories) {
- if (TextUtils.equals(mPackageName, packageName)) {
+ if (TextUtils.equals(mPackageName, packageName)
+ && controlCategories.equals(categories)) {
latch.countDown();
}
}
};
mManager.registerCallback(mExecutor, managerCallback);
mRouter2.setControlCategories(controlCategories);
- mRouter2.registerCallback(mExecutor, routerCallback);
+ mRouter2.registerCallback(mExecutor, routeCallback);
try {
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
return createRouteMap(mManager.getAvailableRoutes(mPackageName));
} finally {
- mRouter2.unregisterCallback(routerCallback);
+ mRouter2.unregisterCallback(routeCallback);
mManager.unregisterCallback(managerCallback);
}
}
@@ -415,9 +421,9 @@
mManager.registerCallback(mExecutor, callback);
}
- private void addRouterCallback(MediaRouter2.Callback callback) {
- mRouterCallbacks.add(callback);
- mRouter2.registerCallback(mExecutor, callback);
+ private void addRouterCallback(RouteCallback routeCallback) {
+ mRouteCallbacks.add(routeCallback);
+ mRouter2.registerCallback(mExecutor, routeCallback);
}
private void clearCallbacks() {
@@ -426,9 +432,9 @@
}
mManagerCallbacks.clear();
- for (MediaRouter2.Callback callback : mRouterCallbacks) {
- mRouter2.unregisterCallback(callback);
+ for (RouteCallback routeCallback : mRouteCallbacks) {
+ mRouter2.unregisterCallback(routeCallback);
}
- mRouterCallbacks.clear();
+ mRouteCallbacks.clear();
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index b862e95..cf4ee7d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -32,7 +32,7 @@
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.DividerModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -52,17 +52,14 @@
import com.android.systemui.volume.CarVolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogComponent;
-import java.util.Optional;
-
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Binds;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-@Module
+@Module(includes = {DividerModule.class})
abstract class CarSystemUIModule {
@Binds
@@ -85,12 +82,6 @@
@Singleton
@Provides
- static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
- return new Divider(context, recentsOptionalLazy);
- }
-
- @Singleton
- @Provides
static HeadsUpManagerPhone provideHeadsUpManagerPhone(Context context,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 1b171e8..5195f1a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.car;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -30,6 +31,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -57,9 +59,9 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.Dependency;
+import com.android.systemui.InitController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
@@ -68,12 +70,14 @@
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.car.CarNavigationBarController;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.car.CarQSFragment;
@@ -141,6 +145,7 @@
import java.io.PrintWriter;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
import javax.inject.Provider;
@@ -273,7 +278,7 @@
NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
- UiOffloadThread uiOffloadThread,
+ @UiBackground Executor uiBgExecutor,
NotificationMediaManager notificationMediaManager,
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
@@ -318,6 +323,9 @@
ShadeController shadeController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
+ InitController initController,
+ DarkIconDispatcher darkIconDispatcher,
+ @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
DismissCallbackRegistry dismissCallbackRegistry,
/* Car Settings injected components. */
CarServiceProvider carServiceProvider,
@@ -353,7 +361,7 @@
notificationAlertingManager,
displayMetrics,
metricsLogger,
- uiOffloadThread,
+ uiBgExecutor,
notificationMediaManager,
lockScreenUserManager,
remoteInputManager,
@@ -398,6 +406,9 @@
superStatusBarViewFactory,
statusBarKeyguardViewManager,
viewMediatorCallback,
+ initController,
+ darkIconDispatcher,
+ timeTickHandler,
dismissCallbackRegistry);
mScrimController = scrimController;
mLockscreenLockIconController = lockscreenLockIconController;
@@ -1043,6 +1054,12 @@
mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
}
+ @Override
+ public void onLocaleListChanged() {
+ connectNotificationsUI();
+ registerNavBarListeners();
+ }
+
/**
* Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
*/
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 7b21d9d..b4323cc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -17,25 +17,29 @@
package com.android.systemui.statusbar.car;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.content.Context;
+import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.UiOffloadThread;
+import com.android.systemui.InitController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.car.CarNavigationBarController;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
@@ -96,6 +100,7 @@
import com.android.systemui.volume.VolumeComponent;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
import javax.inject.Provider;
@@ -143,7 +148,7 @@
NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
- UiOffloadThread uiOffloadThread,
+ @UiBackground Executor uiBgExecutor,
NotificationMediaManager notificationMediaManager,
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
@@ -188,6 +193,9 @@
ShadeController shadeController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
+ InitController initController,
+ DarkIconDispatcher darkIconDispatcher,
+ @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
DismissCallbackRegistry dismissCallbackRegistry,
CarServiceProvider carServiceProvider,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
@@ -222,7 +230,7 @@
notificationAlertingManager,
displayMetrics,
metricsLogger,
- uiOffloadThread,
+ uiBgExecutor,
notificationMediaManager,
lockScreenUserManager,
remoteInputManager,
@@ -266,6 +274,9 @@
shadeController,
statusBarKeyguardViewManager,
viewMediatorCallback,
+ initController,
+ darkIconDispatcher,
+ timeTickHandler,
dismissCallbackRegistry,
carServiceProvider,
powerManagerHelperLazy,
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index b85eb97..462c60e 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -62,7 +62,7 @@
<string name="uninstalling_notification_channel" msgid="840153394325714653">"Désinstallations en cours"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Échec des désinstallations"</string>
<string name="uninstalling" msgid="8709566347688966845">"Désinstallation…"</string>
- <string name="uninstalling_app" msgid="8866082646836981397">"Désinstallation du package <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+ <string name="uninstalling_app" msgid="8866082646836981397">"Désinstallation de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
<string name="uninstall_done" msgid="439354138387969269">"Désinstallation terminée."</string>
<string name="uninstall_done_app" msgid="4588850984473605768">"Le package <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> a été désinstallé"</string>
<string name="uninstall_failed" msgid="1847750968168364332">"Échec de la désinstallation."</string>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index 1f38e3c..dd2ce5a 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -54,7 +54,7 @@
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Vyhledávací pole je skryto"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Přidat tiskárnu"</string>
<string name="print_select_printer" msgid="7388760939873368698">"Vybrat tiskárnu"</string>
- <string name="print_forget_printer" msgid="5035287497291910766">"Odstranit tiskárnu"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Zapomenout tiskárnu"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
<item quantity="few">Nalezené tiskárny: <xliff:g id="COUNT_1">%1$s</xliff:g></item>
<item quantity="many">Nalezené tiskárny: <xliff:g id="COUNT_1">%1$s</xliff:g></item>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index 22ef612..64db711 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -108,7 +108,7 @@
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"לא ניתן היה לכתוב לקובץ"</string>
<string name="print_error_default_message" msgid="8602678405502922346">"מצטערים, אך זה לא עבד. נסה שוב."</string>
- <string name="print_error_retry" msgid="1426421728784259538">"נסה שוב"</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"כדאי לנסות שוב"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"המדפסת הזו אינה זמינה כעת."</string>
<string name="print_cannot_load_page" msgid="6179560924492912009">"לא ניתן להציג תצוגה מקדימה"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"מכין תצוגה מקדימה…"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
index 5b9281cb..d84e57a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
@@ -40,8 +40,12 @@
}
@Override
- public void hidden(Context context, int category) {
- MetricsLogger.hidden(context, category);
+ public void hidden(Context context, int category, int visibleTime) {
+ final LogMaker logMaker = new LogMaker(category)
+ .setType(MetricsProto.MetricsEvent.TYPE_CLOSE)
+ .addTaggedData(MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
+ visibleTime);
+ MetricsLogger.action(logMaker);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
index 9d9c17f..d4ef3d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
@@ -31,7 +31,7 @@
/**
* Logs a visibility event when view becomes hidden.
*/
- void hidden(Context context, int category);
+ void hidden(Context context, int category, int visibleTime);
/**
* Logs an user action.
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index a82231a..c34c365 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -83,9 +83,15 @@
}
}
- public void hidden(Context context, int category) {
+ /**
+ * Logs an event when target page is hidden.
+ *
+ * @param category the target page id
+ * @param visibleTime the time spending on target page since being visible
+ */
+ public void hidden(Context context, int category, int visibleTime) {
for (LogWriter writer : mLoggerWriters) {
- writer.hidden(context, category);
+ writer.hidden(context, category, visibleTime);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index 0a1a122..61e47f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -40,7 +40,8 @@
private MetricsFeatureProvider mMetricsFeature;
private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
- private long mTimestamp;
+ private long mCreationTimestamp;
+ private long mVisibleTimestamp;
public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) {
mMetricsCategory = metricsCategory;
@@ -49,7 +50,7 @@
@Override
public void onAttach() {
- mTimestamp = SystemClock.elapsedRealtime();
+ mCreationTimestamp = SystemClock.elapsedRealtime();
}
@OnLifecycleEvent(Event.ON_RESUME)
@@ -57,8 +58,9 @@
if (mMetricsFeature == null || mMetricsCategory == METRICS_CATEGORY_UNKNOWN) {
return;
}
- if (mTimestamp != 0L) {
- final int elapse = (int) (SystemClock.elapsedRealtime() - mTimestamp);
+ mVisibleTimestamp = SystemClock.elapsedRealtime();
+ if (mCreationTimestamp != 0L) {
+ final int elapse = (int) (mVisibleTimestamp - mCreationTimestamp);
mMetricsFeature.visible(null /* context */, mSourceMetricsCategory,
mMetricsCategory, elapse);
} else {
@@ -69,9 +71,10 @@
@OnLifecycleEvent(Event.ON_PAUSE)
public void onPause() {
- mTimestamp = 0;
+ mCreationTimestamp = 0;
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
- mMetricsFeature.hidden(null /* context */, mMetricsCategory);
+ final int elapse = (int) (SystemClock.elapsedRealtime() - mVisibleTimestamp);
+ mMetricsFeature.hidden(null /* context */, mMetricsCategory, elapse);
}
}
@@ -84,7 +87,7 @@
if (mMetricsFeature == null || mMetricsCategory == METRICS_CATEGORY_UNKNOWN) {
return;
}
- final int elapse = (int) (SystemClock.elapsedRealtime() - mTimestamp);
+ final int elapse = (int) (SystemClock.elapsedRealtime() - mCreationTimestamp);
mMetricsFeature.action(METRICS_CATEGORY_UNKNOWN, action, mMetricsCategory, key, elapse);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index e600cb8..248b118 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -27,6 +27,11 @@
public static final String KEY_MEDIA_OUTPUT = "media_output";
/**
+ * Key for the Remote Media slice.
+ */
+ public static final String KEY_REMOTE_MEDIA = "remote_media";
+
+ /**
* Activity Action: Show a settings dialog containing {@link MediaDevice} to transfer media.
*/
public static final String ACTION_MEDIA_OUTPUT =
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index 23e29493..ebca962 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -46,7 +46,7 @@
}
final String[] mergedSubscriberIds = telephonyManager.createForSubscriptionId(subId)
- .getMergedSubscriberIdsFromGroup();
+ .getMergedImsisFromGroup();
if (ArrayUtils.isEmpty(mergedSubscriberIds)) {
Log.i(TAG, "mergedSubscriberIds is null.");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index f070a37..7de36e8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -88,7 +88,7 @@
mMixin.onPause();
verify(mMetricsFeature, times(1))
- .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC));
+ .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC), anyInt());
}
@Test
@@ -98,7 +98,7 @@
mMixin.onPause();
verify(mMetricsFeature, never())
- .hidden(nullable(Context.class), anyInt());
+ .hidden(nullable(Context.class), anyInt(), anyInt());
}
@Test
@@ -109,7 +109,7 @@
mMixin.onPause();
verify(mMetricsFeature, never())
- .hidden(nullable(Context.class), anyInt());
+ .hidden(nullable(Context.class), anyInt(), anyInt());
}
@Test
@@ -121,7 +121,7 @@
verify(testActivity.mMetricsFeatureProvider, times(1)).visible(any(), anyInt(), anyInt(),
anyInt());
ac.pause().stop().destroy();
- verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt());
+ verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt(), anyInt());
}
public static class TestActivity extends FragmentActivity {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
index 5cae611..d98f50b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -87,7 +87,7 @@
public void getMobileTemplate_groupUuidNull_returnMobileAll() {
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
when(mInfo1.getGroupUuid()).thenReturn(null);
- when(mTelephonyManager.getMergedSubscriberIdsFromGroup())
+ when(mTelephonyManager.getMergedImsisFromGroup())
.thenReturn(new String[] {SUBSCRIBER_ID});
final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
@@ -99,7 +99,7 @@
public void getMobileTemplate_groupUuidExist_returnMobileMerged() {
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid);
- when(mTelephonyManager.getMergedSubscriberIdsFromGroup())
+ when(mTelephonyManager.getMergedImsisFromGroup())
.thenReturn(new String[] {SUBSCRIBER_ID, SUBSCRIBER_ID_2});
final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
diff --git a/packages/SettingsProvider/res/values/blocked_settings.xml b/packages/SettingsProvider/res/values/blocked_settings.xml
new file mode 100644
index 0000000..b54b74e
--- /dev/null
+++ b/packages/SettingsProvider/res/values/blocked_settings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- These are arrays of settings which should not be restored to this device -->
+<resources>
+ <string-array name="restore_blocked_device_specific_settings" />
+ <string-array name="restore_blocked_global_settings" />
+ <string-array name="restore_blocked_secure_settings" />
+ <string-array name="restore_blocked_system_settings" />
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsProvider/res/values/overlayable.xml b/packages/SettingsProvider/res/values/overlayable.xml
new file mode 100644
index 0000000..dc41a77
--- /dev/null
+++ b/packages/SettingsProvider/res/values/overlayable.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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:android="http://schemas.android.com/apk/res/android">
+ <overlayable name="SettingsToNotRestore">
+ <policy type="product|system|vendor">
+ <item type="array" name="restore_blocked_device_specific_settings" />
+ <item type="array" name="restore_blocked_global_settings" />
+ <item type="array" name="restore_blocked_secure_settings" />
+ <item type="array" name="restore_blocked_system_settings" />
+ </policy>
+ </overlayable>
+</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index c19a340..fb558ab 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -66,6 +66,7 @@
import java.io.OutputStream;
import java.time.DateTimeException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
@@ -250,7 +251,13 @@
@Override
public void onRestore(BackupDataInput data, int appVersionCode,
- ParcelFileDescriptor newState) throws IOException {
+ ParcelFileDescriptor newState) {
+ throw new RuntimeException("SettingsBackupAgent has been migrated to use key exclusion");
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, long appVersionCode,
+ ParcelFileDescriptor newState, Set<String> dynamicBlockList) throws IOException {
if (DEBUG) {
Log.d(TAG, "onRestore(): appVersionCode: " + appVersionCode
@@ -266,7 +273,7 @@
}
// versionCode of com.android.providers.settings corresponds to SDK_INT
- mRestoredFromSdkInt = appVersionCode;
+ mRestoredFromSdkInt = (int) appVersionCode;
HashSet<String> movedToGlobal = new HashSet<String>();
Settings.System.getMovedToGlobalSettings(movedToGlobal);
@@ -292,16 +299,29 @@
switch (key) {
case KEY_SYSTEM :
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
- movedToSecure);
+ movedToSecure, R.array.restore_blocked_system_settings,
+ dynamicBlockList);
mSettingsHelper.applyAudioSettings();
break;
case KEY_SECURE :
- restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal, null);
+ restoreSettings(
+ data,
+ Settings.Secure.CONTENT_URI,
+ movedToGlobal,
+ null,
+ R.array.restore_blocked_secure_settings,
+ dynamicBlockList);
break;
case KEY_GLOBAL :
- restoreSettings(data, Settings.Global.CONTENT_URI, null, movedToSecure);
+ restoreSettings(
+ data,
+ Settings.Global.CONTENT_URI,
+ null,
+ movedToSecure,
+ R.array.restore_blocked_global_settings,
+ dynamicBlockList);
break;
case KEY_WIFI_SUPPLICANT :
@@ -345,7 +365,10 @@
case KEY_DEVICE_SPECIFIC_CONFIG:
byte[] restoredDeviceSpecificConfig = new byte[size];
data.readEntityData(restoredDeviceSpecificConfig, 0, size);
- restoreDeviceSpecificConfig(restoredDeviceSpecificConfig);
+ restoreDeviceSpecificConfig(
+ restoredDeviceSpecificConfig,
+ R.array.restore_blocked_device_specific_settings,
+ dynamicBlockList);
break;
default :
@@ -394,14 +417,22 @@
byte[] buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
- movedToSecure);
+ movedToSecure, R.array.restore_blocked_system_settings,
+ Collections.emptySet());
// secure settings
nBytes = in.readInt();
if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
- restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal, null);
+ restoreSettings(
+ buffer,
+ nBytes,
+ Settings.Secure.CONTENT_URI,
+ movedToGlobal,
+ null,
+ R.array.restore_blocked_secure_settings,
+ Collections.emptySet());
// Global only if sufficiently new
if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -411,7 +442,8 @@
in.readFully(buffer, 0, nBytes);
movedToGlobal.clear(); // no redirection; this *is* the global namespace
restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal,
- movedToSecure);
+ movedToSecure, R.array.restore_blocked_global_settings,
+ Collections.emptySet());
}
// locale
@@ -612,8 +644,13 @@
return baos.toByteArray();
}
- private void restoreSettings(BackupDataInput data, Uri contentUri,
- HashSet<String> movedToGlobal, Set<String> movedToSecure) {
+ private void restoreSettings(
+ BackupDataInput data,
+ Uri contentUri,
+ HashSet<String> movedToGlobal,
+ Set<String> movedToSecure,
+ int blockedSettingsArrayId,
+ Set<String> dynamicBlockList) {
byte[] settings = new byte[data.getDataSize()];
try {
data.readEntityData(settings, 0, settings.length);
@@ -621,16 +658,44 @@
Log.e(TAG, "Couldn't read entity data");
return;
}
- restoreSettings(settings, settings.length, contentUri, movedToGlobal, movedToSecure);
+ restoreSettings(
+ settings,
+ settings.length,
+ contentUri,
+ movedToGlobal,
+ movedToSecure,
+ blockedSettingsArrayId,
+ dynamicBlockList);
}
- private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
- HashSet<String> movedToGlobal, Set<String> movedToSecure) {
- restoreSettings(settings, 0, bytes, contentUri, movedToGlobal, movedToSecure);
+ private void restoreSettings(
+ byte[] settings,
+ int bytes,
+ Uri contentUri,
+ HashSet<String> movedToGlobal,
+ Set<String> movedToSecure,
+ int blockedSettingsArrayId,
+ Set<String> dynamicBlockList) {
+ restoreSettings(
+ settings,
+ 0,
+ bytes,
+ contentUri,
+ movedToGlobal,
+ movedToSecure,
+ blockedSettingsArrayId,
+ dynamicBlockList);
}
- private void restoreSettings(byte[] settings, int pos, int bytes, Uri contentUri,
- HashSet<String> movedToGlobal, Set<String> movedToSecure) {
+ private void restoreSettings(
+ byte[] settings,
+ int pos,
+ int bytes,
+ Uri contentUri,
+ HashSet<String> movedToGlobal,
+ Set<String> movedToSecure,
+ int blockedSettingsArrayId,
+ Set<String> dynamicBlockList) {
if (DEBUG) {
Log.i(TAG, "restoreSettings: " + contentUri);
}
@@ -662,9 +727,20 @@
SettingsHelper settingsHelper = mSettingsHelper;
ContentResolver cr = getContentResolver();
- final int whiteListSize = whitelist.length;
- for (int i = 0; i < whiteListSize; i++) {
- String key = whitelist[i];
+ Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
+
+ for (String key : whitelist) {
+ boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
+ if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) {
+ Log.i(
+ TAG,
+ "Key "
+ + key
+ + " removed from restore by "
+ + (isBlockedBySystem ? "system" : "dynamic")
+ + " block list");
+ continue;
+ }
String value = null;
boolean hasValueToRestore = false;
@@ -722,6 +798,19 @@
}
}
+ private boolean isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key) {
+ String contentKey = Uri.withAppendedPath(areaUri, key).toString();
+ return dynamicBlockList.contains(contentKey);
+ }
+
+ // There may be other sources of blocked settings, so I'm separating out this
+ // code to make it easy to modify in the future.
+ @VisibleForTesting
+ protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
+ String[] blockedSettings = getResources().getStringArray(blockedSettingsArrayId);
+ return new HashSet<>(Arrays.asList(blockedSettings));
+ }
+
private boolean isValidSettingValue(String key, String value,
Map<String, Validator> validators) {
if (key == null || validators == null) {
@@ -998,10 +1087,13 @@
* Restore the device specific settings.
*
* @param data The byte array holding a backed up version of another devices settings.
+ * @param blockedSettingsArrayId The string array resource holding the settings not to restore.
+ * @param dynamicBlocklist The dynamic list of settings not to restore fed into this agent.
* @return true if the restore succeeded, false if it was stopped.
*/
@VisibleForTesting
- boolean restoreDeviceSpecificConfig(byte[] data) {
+ boolean restoreDeviceSpecificConfig(byte[] data, int blockedSettingsArrayId,
+ Set<String> dynamicBlocklist) {
// We're using an AtomicInteger to wrap the position int and allow called methods to
// modify it.
AtomicInteger pos = new AtomicInteger(0);
@@ -1013,7 +1105,14 @@
int dataStart = pos.get();
restoreSettings(
- data, dataStart, data.length, Settings.Secure.CONTENT_URI, null, null);
+ data,
+ dataStart,
+ data.length,
+ Settings.Secure.CONTENT_URI,
+ null,
+ null,
+ blockedSettingsArrayId,
+ dynamicBlocklist);
updateWindowManagerIfNeeded(originalDensity);
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index 57e22db..e650882 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -37,14 +37,14 @@
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.annotations.VisibleForTesting;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -85,12 +85,33 @@
assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
- mAgentUnderTest.restoreDeviceSpecificConfig(settingsBackup);
+ mAgentUnderTest.restoreDeviceSpecificConfig(
+ settingsBackup,
+ R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet());
assertEquals("Not all values were restored.", TEST_VALUES, helper.mWrittenValues);
}
@Test
+ public void testRoundTripDeviceSpecificSettingsWithBlock() throws IOException {
+ TestSettingsHelper helper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = helper;
+
+ byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration();
+
+ assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
+ mAgentUnderTest.setBlockedSettings(TEST_VALUES.keySet().toArray(new String[0]));
+
+ mAgentUnderTest.restoreDeviceSpecificConfig(
+ settingsBackup,
+ R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet());
+
+ assertTrue("Not all values were blocked.", helper.mWrittenValues.isEmpty());
+ }
+
+ @Test
public void testGeneratedHeaderMatchesCurrentDevice() throws IOException {
mAgentUnderTest.mSettingsHelper = new TestSettingsHelper(mContext);
@@ -148,7 +169,10 @@
assertFalse(
"Blocking isSourceAcceptable did not stop restore",
- mAgentUnderTest.restoreDeviceSpecificConfig(data));
+ mAgentUnderTest.restoreDeviceSpecificConfig(
+ data,
+ R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet()));
}
private byte[] generateUncorruptedHeader() throws IOException {
@@ -184,14 +208,34 @@
}
}
+ private byte[] generateSingleKeyTestBackupData(String key, String value) throws IOException {
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ os.write(SettingsBackupAgent.toByteArray(key));
+ os.write(SettingsBackupAgent.toByteArray(value));
+ return os.toByteArray();
+ }
+ }
+
private static class TestFriendlySettingsBackupAgent extends SettingsBackupAgent {
private Boolean mForcedDeviceInfoRestoreAcceptability = null;
+ private String[] mBlockedSettings = null;
void setForcedDeviceInfoRestoreAcceptability(boolean value) {
mForcedDeviceInfoRestoreAcceptability = value;
}
- @VisibleForTesting
+ void setBlockedSettings(String... blockedSettings) {
+ mBlockedSettings = blockedSettings;
+ }
+
+ @Override
+ protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
+ return mBlockedSettings == null
+ ? super.getBlockedSettings(blockedSettingsArrayId)
+ : new HashSet<>(Arrays.asList(mBlockedSettings));
+ }
+
+ @Override
boolean isSourceAcceptable(byte[] data, AtomicInteger pos) {
return mForcedDeviceInfoRestoreAcceptability == null
? super.isSourceAcceptable(data, pos)
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index 28f3aec..f62d117 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -18,27 +18,27 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Оболонка"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Звіти про помилки"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Генерується повідомлення про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"Повідомлення про помилку <xliff:g id="ID">#%d</xliff:g> створено"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"Додаються деталі до повідомлення про помилку"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Створюється звіт про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"Звіт про помилку <xliff:g id="ID">#%d</xliff:g> створено"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"У звіт про помилку додаються деталі"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Зачекайте…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Звіт про помилку невдовзі з’явиться на телефоні"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Виберіть, щоб надіслати повідомлення про помилку"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Торкніться, щоб надіслати повідомлення про помилку"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Виберіть, щоб надіслати звіт про помилку"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Торкніться, щоб надіслати звіт про помилку"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Виберіть, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Торкніться, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Торкніться, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Торкніться, щоб надіслати звіт про помилку без знімка екрана, або зачекайте, доки буде збережено знімок"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Торкніться, щоб надіслати звіт про помилку без знімка екрана, або зачекайте, доки буде збережено знімок"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Звіти про помилки містять дані з різних файлів журналів системи, зокрема відомості, які ви вважаєте конфіденційними (як-от інформація про місцезнаходження та використання додатка). Діліться звітами про помилки лише з людьми та в додатках, яким довіряєте."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Більше не показувати"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Звіти про помилки"</string>
- <string name="bugreport_unreadable_text" msgid="586517851044535486">"Не вдалося прочитати звіт про помилки"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Не вдалося додати деталі повідомлення про помилку у файл .zip"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Не вдалося прочитати звіт про помилку"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Не вдалося додати деталі звіту про помилку у файл .zip"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"без назви"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Деталі"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Знімок екрана"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Знімок екрана зроблено."</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Не вдалося зробити знімок екрана."</string>
- <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Деталі повідомлення про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Деталі звіту про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"Назва файлу"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"Назва помилки"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Опис помилки"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a9ca04b..eecc54c 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -267,7 +267,6 @@
@Inject Lazy<KeyguardEnvironment> mKeyguardEnvironment;
@Inject Lazy<ShadeController> mShadeController;
@Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
- @Inject Lazy<InitController> mInitController;
@Inject Lazy<AppOpsController> mAppOpsController;
@Inject Lazy<NavigationBarController> mNavigationBarController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
@@ -456,8 +455,6 @@
mProviders.put(NotificationRemoteInputManager.Callback.class,
mNotificationRemoteInputManagerCallback::get);
- mProviders.put(InitController.class, mInitController::get);
-
mProviders.put(AppOpsController.class, mAppOpsController::get);
mProviders.put(NavigationBarController.class, mNavigationBarController::get);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index e08de39..1315152 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -56,6 +56,7 @@
private SystemUI[] mServices;
private boolean mServicesStarted;
private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
+ private SystemUIRootComponent mRootComponent;
public SystemUIApplication() {
super();
@@ -72,9 +73,9 @@
Trace.TRACE_TAG_APP);
log.traceBegin("DependencyInjection");
mContextAvailableCallback.onContextAvailable(this);
- SystemUIRootComponent root = SystemUIFactory.getInstance().getRootComponent();
- mComponentHelper = root.getContextComponentHelper();
- mBootCompleteCache = root.provideBootCacheImpl();
+ mRootComponent = SystemUIFactory.getInstance().getRootComponent();
+ mComponentHelper = mRootComponent.getContextComponentHelper();
+ mBootCompleteCache = mRootComponent.provideBootCacheImpl();
log.traceEnd();
// Set the application theme that is inherited by all services. Note that setting the
@@ -209,7 +210,7 @@
mServices[i].onBootCompleted();
}
}
- Dependency.get(InitController.class).executePostInitTasks();
+ mRootComponent.getInitController().executePostInitTasks();
log.traceEnd();
mServicesStarted = true;
@@ -218,11 +219,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (mServicesStarted) {
- SystemUIFactory
- .getInstance()
- .getRootComponent()
- .getConfigurationController()
- .onConfigurationChanged(newConfig);
+ mRootComponent.getConfigurationController().onConfigurationChanged(newConfig);
int len = mServices.length;
for (int i = 0; i < len; i++) {
if (mServices[i] != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
index a726b42..d5a46de 100644
--- a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
+++ b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui;
@@ -36,7 +36,7 @@
public UiOffloadThread() {
}
- public Future<?> submit(Runnable runnable) {
+ public Future<?> execute(Runnable runnable) {
return mExecutorService.submit(runnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index 20742d6..eb014ed 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -37,8 +37,8 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.analytics.DataCollector;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
@@ -46,6 +46,7 @@
import com.android.systemui.util.sensors.AsyncSensorManager;
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
/**
* When the phone is locked, listens to touch, sensor and phone events and sends them to
@@ -77,7 +78,7 @@
private final DataCollector mDataCollector;
private final HumanInteractionClassifier mHumanInteractionClassifier;
private final AccessibilityManager mAccessibilityManager;
- private final UiOffloadThread mUiOffloadThread;
+ private final Executor mUiBgExecutor;
private boolean mEnforceBouncer = false;
private boolean mBouncerOn = false;
@@ -137,13 +138,13 @@
}
};
- FalsingManagerImpl(Context context) {
+ FalsingManagerImpl(Context context, @UiBackground Executor uiBgExecutor) {
mContext = context;
mSensorManager = Dependency.get(AsyncSensorManager.class);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mDataCollector = DataCollector.getInstance(mContext);
mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
- mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ mUiBgExecutor = uiBgExecutor;
mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
mMetricsLogger = new MetricsLogger();
@@ -196,7 +197,7 @@
}
// This can be expensive, and doesn't need to happen on the main thread.
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
mSensorManager.unregisterListener(mSensorEventListener);
});
}
@@ -237,7 +238,7 @@
if (s != null) {
// This can be expensive, and doesn't need to happen on the main thread.
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
mSensorManager.registerListener(
mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME);
});
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 4663b1c..f475948 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -31,6 +31,7 @@
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
import com.android.systemui.classifier.brightline.FalsingDataProvider;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
@@ -39,6 +40,7 @@
import com.android.systemui.util.sensors.ProximitySensor;
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -58,13 +60,16 @@
private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
private final DeviceConfigProxy mDeviceConfig;
private boolean mBrightlineEnabled;
+ private Executor mUiBgExecutor;
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager,
@Main Handler handler,
ProximitySensor proximitySensor,
- DeviceConfigProxy deviceConfig) {
+ DeviceConfigProxy deviceConfig,
+ @UiBackground Executor uiBgExecutor) {
mProximitySensor = proximitySensor;
+ mUiBgExecutor = uiBgExecutor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
@@ -87,7 +92,7 @@
}
public void onPluginDisconnected(FalsingPlugin plugin) {
- mInternalFalsingManager = new FalsingManagerImpl(context);
+ mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
}
};
@@ -117,7 +122,7 @@
mInternalFalsingManager.cleanup();
}
if (!brightlineEnabled) {
- mInternalFalsingManager = new FalsingManagerImpl(context);
+ mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
new FalsingDataProvider(context.getResources().getDisplayMetrics()),
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 5fc789c..5c171e4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -30,7 +30,7 @@
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.DividerModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -44,13 +44,10 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import java.util.Optional;
-
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Binds;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -58,7 +55,7 @@
* A dagger module for injecting default implementations of components of System UI that may be
* overridden by the System UI implementation.
*/
-@Module
+@Module(includes = {DividerModule.class})
abstract class SystemUIDefaultModule {
@Singleton
@@ -95,12 +92,6 @@
@Singleton
@Provides
- static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
- return new Divider(context, recentsOptionalLazy);
- }
-
- @Singleton
- @Provides
static HeadsUpManagerPhone provideHeadsUpManagerPhone(Context context,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e50e0fe0..e14581f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -22,6 +22,7 @@
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.Dependency;
+import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.fragments.FragmentService;
@@ -77,6 +78,13 @@
@Singleton
FragmentService.FragmentCreator createFragmentCreator();
+
+ /**
+ * Creates a InitController.
+ */
+ @Singleton
+ InitController getInitController();
+
/**
* ViewCreator generates all Views that need injection.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/UiBackground.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/UiBackground.java
new file mode 100644
index 0000000..bf2237a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/UiBackground.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+
+/**
+ * An annotation for injecting instances related to UI operations off the main-thread.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface UiBackground {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
index 25ac8f8..f6f3b99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
@@ -11,16 +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.systemui.keyguard;
import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.systemui.Dependency;
-import com.android.systemui.UiOffloadThread;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -32,10 +32,12 @@
public class DismissCallbackRegistry {
private final ArrayList<DismissCallbackWrapper> mDismissCallbacks = new ArrayList<>();
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final Executor mUiBgExecutor;
@Inject
- public DismissCallbackRegistry() {}
+ public DismissCallbackRegistry(@UiBackground Executor uiBgExecutor) {
+ mUiBgExecutor = uiBgExecutor;
+ }
public void addCallback(IKeyguardDismissCallback callback) {
mDismissCallbacks.add(new DismissCallbackWrapper(callback));
@@ -44,7 +46,7 @@
public void notifyDismissCancelled() {
for (int i = mDismissCallbacks.size() - 1; i >= 0; i--) {
DismissCallbackWrapper callback = mDismissCallbacks.get(i);
- mUiOffloadThread.submit(callback::notifyDismissCancelled);
+ mUiBgExecutor.execute(callback::notifyDismissCancelled);
}
mDismissCallbacks.clear();
}
@@ -52,7 +54,7 @@
public void notifyDismissSucceeded() {
for (int i = mDismissCallbacks.size() - 1; i >= 0; i--) {
DismissCallbackWrapper callback = mDismissCallbacks.get(i);
- mUiOffloadThread.submit(callback::notifyDismissSucceeded);
+ mUiBgExecutor.execute(callback::notifyDismissSucceeded);
}
mDismissCallbacks.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index beba203..9fcf022 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.keyguard;
@@ -81,8 +81,8 @@
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -95,6 +95,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -211,7 +212,7 @@
private AudioManager mAudioManager;
private StatusBarManager mStatusBarManager;
private final StatusBarWindowController mStatusBarWindowController;
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final Executor mUiBgExecutor;
private boolean mSystemReady;
private boolean mBootCompleted;
@@ -689,7 +690,8 @@
BroadcastDispatcher broadcastDispatcher,
StatusBarWindowController statusBarWindowController,
Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManagerLazy,
- DismissCallbackRegistry dismissCallbackRegistry) {
+ DismissCallbackRegistry dismissCallbackRegistry,
+ @UiBackground Executor uiBgExecutor) {
super(context);
mFalsingManager = falsingManager;
mLockPatternUtils = lockPatternUtils;
@@ -697,6 +699,7 @@
mStatusBarWindowController = statusBarWindowController;
mStatusBarKeyguardViewManagerLazy = statusBarKeyguardViewManagerLazy;
mDismissCallbackRegistry = dismissCallbackRegistry;
+ mUiBgExecutor = uiBgExecutor;
}
public void userActivity() {
@@ -1662,7 +1665,7 @@
private void handleKeyguardDone() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDone");
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
@@ -1705,7 +1708,7 @@
final UserHandle currentUser = new UserHandle(currentUserId);
final UserManager um = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
}
@@ -1756,7 +1759,7 @@
mUiSoundsStreamType = mAudioManager.getUiSoundsStreamType();
}
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
// If the stream is muted, don't play the sound
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;
@@ -1775,7 +1778,7 @@
}
private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
if (DEBUG) {
Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
}
@@ -1854,7 +1857,7 @@
// Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager will be in
// order.
final int keyguardFlag = flags;
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
ActivityTaskManager.getService().keyguardGoingAway(keyguardFlag);
} catch (RemoteException e) {
@@ -2217,7 +2220,7 @@
}
});
updateInputRestrictedLocked();
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
mTrustManager.reportKeyguardShowingChanged();
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 0a89017..f39d1ec 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -100,7 +100,7 @@
mMenuController.onActivityPinned();
mAppOpsListener.onActivityPinned(packageName);
- Dependency.get(UiOffloadThread.class).submit(() -> {
+ Dependency.get(UiOffloadThread.class).execute(() -> {
WindowManagerWrapper.getInstance().setPipVisibility(true);
});
}
@@ -114,7 +114,7 @@
mTouchHandler.onActivityUnpinned(topActivity);
mAppOpsListener.onActivityUnpinned();
- Dependency.get(UiOffloadThread.class).submit(() -> {
+ Dependency.get(UiOffloadThread.class).execute(() -> {
WindowManagerWrapper.getInstance().setPipVisibility(topActivity != null);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 696db68..1d92375 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -750,7 +750,7 @@
}
private void updatePipVisibility(final boolean visible) {
- Dependency.get(UiOffloadThread.class).submit(() -> {
+ Dependency.get(UiOffloadThread.class).execute(() -> {
WindowManagerWrapper.getInstance().setPipVisibility(visible);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
new file mode 100644
index 0000000..49f4d5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stackdivider;
+
+import android.content.Context;
+
+import com.android.systemui.recents.Recents;
+
+import java.util.Optional;
+
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module which provides a Divider.
+ */
+@Module
+public class DividerModule {
+ @Singleton
+ @Provides
+ static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
+ return new Divider(context, recentsOptionalLazy);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index e24a362..b846aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import java.util.stream.Stream;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 49bed15..93f5805 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -54,12 +54,13 @@
import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.UiOffloadThread;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.NotificationChannels;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -74,16 +75,18 @@
public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
private final Handler mHandler = new Handler();
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final Executor mUiBgExecutor;
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
private final CommandQueue mCommandQueue;
private boolean mDockedStackExists;
private KeyguardStateController mKeyguardStateController;
@Inject
- public InstantAppNotifier(Context context, CommandQueue commandQueue) {
+ public InstantAppNotifier(Context context, CommandQueue commandQueue,
+ @UiBackground Executor uiBgExecutor) {
super(context);
mCommandQueue = commandQueue;
+ mUiBgExecutor = uiBgExecutor;
}
@Override
@@ -151,7 +154,7 @@
private void updateForegroundInstantApps() {
NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
IPackageManager pm = AppGlobals.getPackageManager();
- mUiOffloadThread.submit(
+ mUiBgExecutor.execute(
() -> {
ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs);
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 31b7cb0..81833a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import android.app.Notification;
import android.service.notification.StatusBarNotification;
@@ -28,7 +28,7 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 0694920..f6b5583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -24,7 +24,7 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
/**
* Listener interface for changes sent by NotificationEntryManager.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 43b9fbc..b8afb78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -18,6 +18,8 @@
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_ERROR;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -45,8 +47,7 @@
import com.android.systemui.statusbar.notification.logging.NotifEvent;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -94,7 +95,7 @@
@Singleton
public class NotificationEntryManager implements
Dumpable,
- NotificationContentInflater.InflationCallback,
+ InflationCallback,
VisualStabilityManager.Callback {
private static final String TAG = "NotificationEntryMgr";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index de16ef5..dd3a3e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -61,8 +61,8 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 1c0a9d4..8afbc27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -18,7 +18,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
import android.content.Context;
@@ -258,7 +258,7 @@
row.setEntry(entry);
if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
- row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
+ row.setInflationFlags(FLAG_CONTENT_VIEW_HEADS_UP);
}
row.setNeedsRedaction(
Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 77ccf19..3e1b5bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.statusbar.notification.logging;
@@ -32,7 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.UiOffloadThread;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.NotificationListener;
@@ -47,6 +47,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -68,7 +69,7 @@
// Dependencies:
private final NotificationListenerService mNotificationListener;
- private final UiOffloadThread mUiOffloadThread;
+ private final Executor mUiBgExecutor;
private final NotificationEntryManager mEntryManager;
private HeadsUpManager mHeadsUpManager;
private final ExpansionStateLogger mExpansionStateLogger;
@@ -193,12 +194,12 @@
@Inject
public NotificationLogger(NotificationListener notificationListener,
- UiOffloadThread uiOffloadThread,
+ @UiBackground Executor uiBgExecutor,
NotificationEntryManager entryManager,
StatusBarStateController statusBarStateController,
ExpansionStateLogger expansionStateLogger) {
mNotificationListener = notificationListener;
- mUiOffloadThread = uiOffloadThread;
+ mUiBgExecutor = uiBgExecutor;
mEntryManager = entryManager;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -319,7 +320,7 @@
final NotificationVisibility[] newlyVisibleAr = cloneVisibilitiesAsArr(newlyVisible);
final NotificationVisibility[] noLongerVisibleAr = cloneVisibilitiesAsArr(noLongerVisible);
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
} catch (RemoteException e) {
@@ -429,13 +430,13 @@
* Notification key -> last logged expansion state, should be accessed in UI thread only.
*/
private final Map<String, Boolean> mLoggedExpansionState = new ArrayMap<>();
- private final UiOffloadThread mUiOffloadThread;
+ private final Executor mUiBgExecutor;
@VisibleForTesting
IStatusBarService mBarService;
@Inject
- public ExpansionStateLogger(UiOffloadThread uiOffloadThread) {
- mUiOffloadThread = uiOffloadThread;
+ public ExpansionStateLogger(@UiBackground Executor uiBgExecutor) {
+ mUiBgExecutor = uiBgExecutor;
mBarService =
IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -513,7 +514,7 @@
}
mLoggedExpansionState.put(key, state.mIsExpanded);
final State stateToBeLogged = new State(state);
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
mBarService.onNotificationExpansionChanged(key, stateToBeLogged.mIsUserAction,
stateToBeLogged.mIsExpanded, stateToBeLogged.mLocation.ordinal());
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 65423e9..3c247df 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
@@ -17,11 +17,12 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -88,7 +89,9 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -124,8 +127,18 @@
private static final String TAG = "ExpandableNotifRow";
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
+
+ /**
+ * Content views that must be inflated at all times.
+ */
+ @InflationFlag
+ static final int REQUIRED_INFLATION_FLAGS =
+ FLAG_CONTENT_VIEW_CONTRACTED
+ | FLAG_CONTENT_VIEW_EXPANDED;
+
private boolean mUpdateBackgroundOnUpdate;
private boolean mNotificationTranslationFinished = false;
+
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
*/
@@ -232,6 +245,10 @@
private ExpandableNotificationRow mNotificationParent;
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnAppOpsClickListener;
+ private InflationCallback mInflationCallback;
+ private boolean mIsChildInGroup;
+ private @InflationFlag int mInflationFlags = REQUIRED_INFLATION_FLAGS;
+ private final BindParams mBindParams = new BindParams();
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -447,7 +464,8 @@
* Inflate views based off the inflation flags set. Inflation happens asynchronously.
*/
public void inflateViews() {
- mNotificationInflater.inflateNotificationViews();
+ mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
+ false /* forceInflate */, mInflationCallback);
}
/**
@@ -458,9 +476,9 @@
*/
public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
// View should not be reinflated in the future
- updateInflationFlag(inflationFlag, false);
- Runnable freeViewRunnable = () ->
- mNotificationInflater.freeNotificationView(inflationFlag);
+ clearInflationFlags(inflationFlag);
+ Runnable freeViewRunnable =
+ () -> mNotificationInflater.unbindContent(mEntry, this, inflationFlag);
switch (inflationFlag) {
case FLAG_CONTENT_VIEW_HEADS_UP:
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
@@ -475,13 +493,22 @@
}
/**
- * Update whether or not a content view should be inflated.
+ * Set flags for content views that should be inflated
*
- * @param flag the flag corresponding to the content view
- * @param shouldInflate true if it should be inflated, false if it should not
+ * @param flags flags to inflate
*/
- public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
- mNotificationInflater.updateInflationFlag(flag, shouldInflate);
+ public void setInflationFlags(@InflationFlag int flags) {
+ mInflationFlags |= flags;
+ }
+
+ /**
+ * Clear flags for content views that should not be inflated
+ *
+ * @param flags flags that should not be inflated
+ */
+ public void clearInflationFlags(@InflationFlag int flags) {
+ mInflationFlags &= ~flags;
+ mInflationFlags |= REQUIRED_INFLATION_FLAGS;
}
/**
@@ -491,7 +518,7 @@
* @return true if the flag is set, false otherwise
*/
public boolean isInflationFlagSet(@InflationFlag int flag) {
- return mNotificationInflater.isInflationFlagSet(flag);
+ return ((mInflationFlags & flag) != 0);
}
/**
@@ -820,7 +847,15 @@
}
mNotificationParent = isChildInGroup ? parent : null;
mPrivateLayout.setIsChildInGroup(isChildInGroup);
- mNotificationInflater.setIsChildInGroup(isChildInGroup);
+ mBindParams.isChildInGroup = isChildInGroup;
+ if (mIsChildInGroup != isChildInGroup) {
+ mIsChildInGroup = isChildInGroup;
+ if (mIsLowPriority) {
+ int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
+ mNotificationInflater.bindContent(mEntry, this, flags, mBindParams,
+ false /* forceInflate */, mInflationCallback);
+ }
+ }
resetBackgroundAlpha();
updateBackgroundForGroupState();
updateClickAndFocus();
@@ -1224,7 +1259,8 @@
l.reInflateViews();
}
mEntry.getSbn().clearPackageContext();
- mNotificationInflater.clearCachesAndReInflate();
+ mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
+ true /* forceInflate */, mInflationCallback);
}
@Override
@@ -1578,7 +1614,7 @@
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
mPrivateLayout.setIsLowPriority(isLowPriority);
- mNotificationInflater.setIsLowPriority(mIsLowPriority);
+ mBindParams.isLowPriority = mIsLowPriority;
if (mChildrenContainer != null) {
mChildrenContainer.setIsLowPriority(isLowPriority);
}
@@ -1590,28 +1626,36 @@
public void setUseIncreasedCollapsedHeight(boolean use) {
mUseIncreasedCollapsedHeight = use;
- mNotificationInflater.setUsesIncreasedHeight(use);
+ mBindParams.usesIncreasedHeight = use;
}
public void setUseIncreasedHeadsUpHeight(boolean use) {
mUseIncreasedHeadsUpHeight = use;
- mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
+ mBindParams.usesIncreasedHeadsUpHeight = use;
}
public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
}
+ /**
+ * Set callback for notification content inflation
+ *
+ * @param callback inflation callback
+ */
public void setInflationCallback(InflationCallback callback) {
- mNotificationInflater.setInflationCallback(callback);
+ mInflationCallback = callback;
}
public void setNeedsRedaction(boolean needsRedaction) {
if (mNeedsRedaction != needsRedaction) {
mNeedsRedaction = needsRedaction;
- updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, needsRedaction /* shouldInflate */);
- mNotificationInflater.updateNeedsRedaction(needsRedaction);
- if (!needsRedaction) {
+ if (needsRedaction) {
+ setInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
+ mNotificationInflater.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
+ mBindParams, false /* forceInflate */, mInflationCallback);
+ } else {
+ clearInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
freeContentViewWhenSafe(FLAG_CONTENT_VIEW_PUBLIC);
}
}
@@ -1628,7 +1672,7 @@
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
- mNotificationInflater = new NotificationContentInflater(this);
+ mNotificationInflater = new NotificationContentInflater();
mMenuRow = new NotificationMenuRow(mContext);
mImageResolver = new NotificationInlineImageResolver(context,
new NotificationInlineImageCache());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 54dee8c..172b72e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -19,7 +19,7 @@
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
@@ -47,205 +47,55 @@
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.util.Assert;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
/**
- * A utility that inflates the right kind of contentView based on the state
+ * {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
+ * asynchronously building the content's {@link RemoteViews} and applying it to the row.
*/
-public class NotificationContentInflater {
+public class NotificationContentInflater implements NotificationRowContentBinder {
public static final String TAG = "NotifContentInflater";
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- prefix = {"FLAG_CONTENT_VIEW_"},
- value = {
- FLAG_CONTENT_VIEW_CONTRACTED,
- FLAG_CONTENT_VIEW_EXPANDED,
- FLAG_CONTENT_VIEW_HEADS_UP,
- FLAG_CONTENT_VIEW_PUBLIC,
- FLAG_CONTENT_VIEW_ALL})
- public @interface InflationFlag {}
- /**
- * The default, contracted view. Seen when the shade is pulled down and in the lock screen
- * if there is no worry about content sensitivity.
- */
- public static final int FLAG_CONTENT_VIEW_CONTRACTED = 1;
-
- /**
- * The expanded view. Seen when the user expands a notification.
- */
- public static final int FLAG_CONTENT_VIEW_EXPANDED = 1 << 1;
-
- /**
- * The heads up view. Seen when a high priority notification peeks in from the top.
- */
- public static final int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2;
-
- /**
- * The public view. This is a version of the contracted view that hides sensitive
- * information and is used on the lock screen if we determine that the notification's
- * content should be hidden.
- */
- public static final int FLAG_CONTENT_VIEW_PUBLIC = 1 << 3;
-
- public static final int FLAG_CONTENT_VIEW_ALL = ~0;
-
- /**
- * Content views that must be inflated at all times.
- */
- @InflationFlag
- private static final int REQUIRED_INFLATION_FLAGS =
- FLAG_CONTENT_VIEW_CONTRACTED
- | FLAG_CONTENT_VIEW_EXPANDED;
-
- /**
- * The set of content views to inflate.
- */
- @InflationFlag
- private int mInflationFlags = REQUIRED_INFLATION_FLAGS;
-
- private final ExpandableNotificationRow mRow;
- private boolean mIsLowPriority;
- private boolean mUsesIncreasedHeight;
- private boolean mUsesIncreasedHeadsUpHeight;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
- private boolean mIsChildInGroup;
- private InflationCallback mCallback;
private boolean mInflateSynchronously = false;
private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
- public NotificationContentInflater(ExpandableNotificationRow row) {
- mRow = row;
- }
-
- public void setIsLowPriority(boolean isLowPriority) {
- mIsLowPriority = isLowPriority;
- }
-
- /**
- * Set whether the notification is a child in a group
- *
- * @return whether the view was re-inflated
- */
- public void setIsChildInGroup(boolean childInGroup) {
- if (childInGroup != mIsChildInGroup) {
- mIsChildInGroup = childInGroup;
- if (mIsLowPriority) {
- int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
- inflateNotificationViews(flags);
- }
- }
- }
-
- public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
- mUsesIncreasedHeight = usesIncreasedHeight;
- }
-
- public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
- mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
- }
-
- public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
- mRemoteViewClickHandler = remoteViewClickHandler;
- }
-
- /**
- * Update whether or not the notification is redacted on the lock screen. If the notification
- * is now redacted, we should inflate the public contracted view to now show on the lock screen.
- *
- * @param needsRedaction true if the notification should now be redacted on the lock screen
- */
- public void updateNeedsRedaction(boolean needsRedaction) {
- if (mRow.getEntry() == null) {
- return;
- }
- if (needsRedaction) {
- int flags = FLAG_CONTENT_VIEW_PUBLIC;
- inflateNotificationViews(flags);
- }
- }
-
- /**
- * Set whether or not a particular content view is needed and whether or not it should be
- * inflated. These flags will be used when we inflate or reinflate.
- *
- * @param flag the {@link InflationFlag} corresponding to the view that should/should not be
- * inflated
- * @param shouldInflate true if the view should be inflated, false otherwise
- */
- public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
- if (shouldInflate) {
- mInflationFlags |= flag;
- } else if ((REQUIRED_INFLATION_FLAGS & flag) == 0) {
- mInflationFlags &= ~flag;
- }
- }
-
- /**
- * Convenience method for setting multiple flags at once.
- *
- * @param flags a set of {@link InflationFlag} corresponding to content views that should be
- * inflated
- */
- @VisibleForTesting
- public void addInflationFlags(@InflationFlag int flags) {
- mInflationFlags |= flags;
- }
-
- /**
- * Whether or not the view corresponding to the flag is set to be inflated currently.
- *
- * @param flag the {@link InflationFlag} corresponding to the view
- * @return true if the flag is set and view will be inflated, false o/w
- */
- public boolean isInflationFlagSet(@InflationFlag int flag) {
- return ((mInflationFlags & flag) != 0);
- }
-
- /**
- * Inflate views for set flags on a background thread. This is asynchronous and will
- * notify the callback once it's finished.
- */
- public void inflateNotificationViews() {
- inflateNotificationViews(mInflationFlags);
- }
-
- /**
- * Inflate all views for the specified flags on a background thread. This is asynchronous and
- * will notify the callback once it's finished. If the content view is already inflated, this
- * will reinflate it.
- *
- * @param reInflateFlags flags which views should be inflated. Should be a subset of
- * {@link #mInflationFlags} as only those will be inflated/reinflated.
- */
- private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
- if (mRow.isRemoved()) {
+ @Override
+ public void bindContent(
+ NotificationEntry entry,
+ ExpandableNotificationRow row,
+ @InflationFlag int contentToBind,
+ BindParams bindParams,
+ boolean forceInflate,
+ @Nullable InflationCallback callback) {
+ if (row.isRemoved()) {
// We don't want to reinflate anything for removed notifications. Otherwise views might
// be readded to the stack, leading to leaks. This may happen with low-priority groups
// where the removal of already removed children can lead to a reinflation.
return;
}
- // Only inflate the ones that are set.
- reInflateFlags &= mInflationFlags;
- StatusBarNotification sbn = mRow.getEntry().getSbn();
+
+ StatusBarNotification sbn = row.getEntry().getSbn();
// To check if the notification has inline image and preload inline image if necessary.
- mRow.getImageResolver().preloadImages(sbn.getNotification());
+ row.getImageResolver().preloadImages(sbn.getNotification());
+
+ if (forceInflate) {
+ mCachedContentViews.clear();
+ }
AsyncInflationTask task = new AsyncInflationTask(
sbn,
mInflateSynchronously,
- reInflateFlags,
+ contentToBind,
mCachedContentViews,
- mRow,
- mIsLowPriority,
- mIsChildInGroup,
- mUsesIncreasedHeight,
- mUsesIncreasedHeadsUpHeight,
- mCallback,
+ row,
+ bindParams.isLowPriority,
+ bindParams.isChildInGroup,
+ bindParams.usesIncreasedHeight,
+ bindParams.usesIncreasedHeadsUpHeight,
+ callback,
mRemoteViewClickHandler);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
@@ -256,49 +106,84 @@
@VisibleForTesting
InflationProgress inflateNotificationViews(
+ NotificationEntry entry,
+ ExpandableNotificationRow row,
+ BindParams bindParams,
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
Notification.Builder builder,
Context packageContext) {
- InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
- mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
+ InflationProgress result = createRemoteViews(reInflateFlags,
+ builder,
+ bindParams.isLowPriority,
+ bindParams.isChildInGroup,
+ bindParams.usesIncreasedHeight,
+ bindParams.usesIncreasedHeadsUpHeight,
packageContext);
- result = inflateSmartReplyViews(result, reInflateFlags, mRow.getEntry(),
- mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
- mRow.getExistingSmartRepliesAndActions());
+ result = inflateSmartReplyViews(result, reInflateFlags, entry,
+ row.getContext(), packageContext, row.getHeadsUpManager(),
+ row.getExistingSmartRepliesAndActions());
apply(
inflateSynchronously,
result,
reInflateFlags,
mCachedContentViews,
- mRow,
+ row,
mRemoteViewClickHandler,
null);
return result;
}
+ @Override
+ public void cancelBind(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row) {
+ entry.abortTask();
+ }
+
+ @Override
+ public void unbindContent(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row,
+ @InflationFlag int contentToUnbind) {
+ int curFlag = 1;
+ while (contentToUnbind != 0) {
+ if ((contentToUnbind & curFlag) != 0) {
+ freeNotificationView(row, curFlag);
+ }
+ contentToUnbind &= ~curFlag;
+ curFlag = curFlag << 1;
+ }
+ }
+
+ /**
+ * Set click handler for notification remote views
+ *
+ * @param remoteViewClickHandler click handler for remote views
+ */
+ public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
+ mRemoteViewClickHandler = remoteViewClickHandler;
+ }
+
/**
* Frees the content view associated with the inflation flag. Will only succeed if the
* view is safe to remove.
*
* @param inflateFlag the flag corresponding to the content view which should be freed
*/
- public void freeNotificationView(@InflationFlag int inflateFlag) {
- if ((mInflationFlags & inflateFlag) != 0) {
- // The view should still be inflated.
- return;
- }
+ private void freeNotificationView(ExpandableNotificationRow row,
+ @InflationFlag int inflateFlag) {
switch (inflateFlag) {
case FLAG_CONTENT_VIEW_HEADS_UP:
- if (mRow.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
- mRow.getPrivateLayout().setHeadsUpChild(null);
+ if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
+ row.getPrivateLayout().setHeadsUpChild(null);
mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
- mRow.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
+ row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
}
break;
case FLAG_CONTENT_VIEW_PUBLIC:
- if (mRow.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
- mRow.getPublicLayout().setContractedChild(null);
+ if (row.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
+ row.getPublicLayout().setContractedChild(null);
mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC);
}
break;
@@ -718,33 +603,12 @@
&& !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED));
}
- public void setInflationCallback(InflationCallback callback) {
- mCallback = callback;
- }
-
- public interface InflationCallback {
- void handleInflationException(StatusBarNotification notification, Exception e);
-
- /**
- * Callback for after the content views finish inflating.
- *
- * @param entry the entry with the content views set
- * @param inflatedFlags the flags associated with the content views that were inflated
- */
- void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags);
- }
-
- public void clearCachesAndReInflate() {
- mCachedContentViews.clear();
- inflateNotificationViews();
- }
-
/**
* Sets whether to perform inflation on the same thread as the caller. This method should only
* be used in tests, not in production.
*/
@VisibleForTesting
- void setInflateSynchronously(boolean inflateSynchronously) {
+ public void setInflateSynchronously(boolean inflateSynchronously) {
mInflateSynchronously = inflateSynchronously;
}
@@ -842,8 +706,10 @@
final String ident = sbn.getPackageName() + "/0x"
+ Integer.toHexString(sbn.getId());
Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
- mCallback.handleInflationException(sbn,
- new InflationException("Couldn't inflate contentViews" + e));
+ if (mCallback != null) {
+ mCallback.handleInflationException(sbn,
+ new InflationException("Couldn't inflate contentViews" + e));
+ }
}
@Override
@@ -872,7 +738,9 @@
@InflationFlag int inflatedFlags) {
mRow.getEntry().onInflationTaskFinished();
mRow.onNotificationUpdated();
- mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
+ if (mCallback != null) {
+ mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
+ }
// Notify the resolver that the inflation task has finished,
// try to purge unnecessary cached entries.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
new file mode 100644
index 0000000..2fe54c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.notification.row;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Binder that takes a notifications {@link ExpandableNotificationRow} and binds the appropriate
+ * content to it based off the bind parameters passed to it.
+ */
+public interface NotificationRowContentBinder {
+
+ /**
+ * Inflate notification content views and bind to the row.
+ *
+ * @param entry notification
+ * @param row notification row to bind views to
+ * @param contentToBind content views that should be inflated and bound
+ * @param bindParams parameters for binding content views
+ * @param forceInflate true to force reinflation even if views are cached
+ * @param callback callback after inflation is finished
+ */
+ void bindContent(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row,
+ @InflationFlag int contentToBind,
+ BindParams bindParams,
+ boolean forceInflate,
+ @Nullable InflationCallback callback);
+
+ /**
+ * Cancel any on-going bind operation.
+ *
+ * @param entry notification
+ * @param row notification row to cancel bind on
+ */
+ void cancelBind(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row);
+
+ /**
+ * Unbind content views from the row.
+ *
+ * @param entry notification
+ * @param row notification row to unbind content views from
+ * @param contentToUnbind content views that should be unbound
+ */
+ void unbindContent(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row,
+ @InflationFlag int contentToUnbind);
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true,
+ prefix = {"FLAG_CONTENT_VIEW_"},
+ value = {
+ FLAG_CONTENT_VIEW_CONTRACTED,
+ FLAG_CONTENT_VIEW_EXPANDED,
+ FLAG_CONTENT_VIEW_HEADS_UP,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ FLAG_CONTENT_VIEW_ALL})
+ @interface InflationFlag {}
+ /**
+ * The default, contracted view. Seen when the shade is pulled down and in the lock screen
+ * if there is no worry about content sensitivity.
+ */
+ int FLAG_CONTENT_VIEW_CONTRACTED = 1;
+ /**
+ * The expanded view. Seen when the user expands a notification.
+ */
+ int FLAG_CONTENT_VIEW_EXPANDED = 1 << 1;
+ /**
+ * The heads up view. Seen when a high priority notification peeks in from the top.
+ */
+ int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2;
+ /**
+ * The public view. This is a version of the contracted view that hides sensitive
+ * information and is used on the lock screen if we determine that the notification's
+ * content should be hidden.
+ */
+ int FLAG_CONTENT_VIEW_PUBLIC = 1 << 3;
+
+ int FLAG_CONTENT_VIEW_ALL = ~0;
+
+ /**
+ * Parameters for content view binding
+ */
+ class BindParams {
+
+ /**
+ * Bind a low priority version of the content views.
+ */
+ public boolean isLowPriority;
+
+ /**
+ * Bind child version of content views.
+ */
+ public boolean isChildInGroup;
+
+ /**
+ * Use increased height when binding contracted view.
+ */
+ public boolean usesIncreasedHeight;
+
+ /**
+ * Use increased height when binding heads up views.
+ */
+ public boolean usesIncreasedHeadsUpHeight;
+ }
+
+ /**
+ * Callback for inflation finishing
+ */
+ interface InflationCallback {
+
+ /**
+ * Callback for when there is an inflation exception
+ *
+ * @param notification notification which failed to inflate content
+ * @param e exception
+ */
+ void handleInflationException(StatusBarNotification notification, Exception e);
+
+ /**
+ * Callback for after the content views finish inflating.
+ *
+ * @param entry the entry with the content views set
+ * @param inflatedFlags the flags associated with the content views that were inflated
+ */
+ void onAsyncInflationFinished(NotificationEntry entry,
+ @InflationFlag int inflatedFlags);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 90ea6e3..0a1a2fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.statusbar.notification.row.wrapper;
@@ -244,17 +244,18 @@
mUiOffloadThread = Dependency.get(UiOffloadThread.class);
}
if (view.isAttachedToWindow()) {
- mUiOffloadThread.submit(() -> pendingIntent.registerCancelListener(listener));
+ mUiOffloadThread.execute(() -> pendingIntent.registerCancelListener(listener));
}
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
- mUiOffloadThread.submit(() -> pendingIntent.registerCancelListener(listener));
+ mUiOffloadThread.execute(() -> pendingIntent.registerCancelListener(listener));
}
@Override
public void onViewDetachedFromWindow(View v) {
- mUiOffloadThread.submit(() -> pendingIntent.unregisterCancelListener(listener));
+ mUiOffloadThread.execute(
+ () -> pendingIntent.unregisterCancelListener(listener));
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 1df9411..d6336ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -53,7 +53,7 @@
import android.util.SparseBooleanArray;
import com.android.systemui.Dumpable;
-import com.android.systemui.UiOffloadThread;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -61,6 +61,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -82,7 +83,7 @@
private Context mCurrentUserContext;
private final IOverlayManager mOverlayManager;
private final DeviceProvisionedController mDeviceProvisionedController;
- private final UiOffloadThread mUiOffloadThread;
+ private final Executor mUiBgExecutor;
private SparseBooleanArray mRestoreGesturalNavBarMode = new SparseBooleanArray();
@@ -146,12 +147,12 @@
@Inject
public NavigationModeController(Context context,
DeviceProvisionedController deviceProvisionedController,
- UiOffloadThread uiOffloadThread) {
+ @UiBackground Executor uiBgExecutor) {
mContext = context;
mCurrentUserContext = context;
mOverlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
- mUiOffloadThread = uiOffloadThread;
+ mUiBgExecutor = uiBgExecutor;
mDeviceProvisionedController = deviceProvisionedController;
mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
@@ -242,7 +243,7 @@
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
mMode = mode;
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode));
});
@@ -379,7 +380,7 @@
}
public void setModeOverlay(String overlayPkg, int userId) {
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
mOverlayManager.setEnabledExclusiveInCategory(overlayPkg, userId);
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 2798285..fe0739f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -33,7 +33,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -395,7 +395,7 @@
@InflationFlag int contentFlag = alertManager.getContentFlag();
if (!entry.getRow().isInflationFlagSet(contentFlag)) {
mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
- entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
+ entry.getRow().setInflationFlags(contentFlag);
entry.getRow().inflateViews();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 2f39c13..5b34aa7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -40,8 +40,8 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.statusbar.CommandQueue;
@@ -63,6 +63,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import java.util.Locale;
+import java.util.concurrent.Executor;
/**
* This class contains all of the policy about which icons are installed in the status bar at boot
@@ -114,7 +115,7 @@
private final DeviceProvisionedController mProvisionedController;
private final KeyguardStateController mKeyguardStateController;
private final LocationController mLocationController;
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
// Assume it's all good unless we hear otherwise. We don't always seem
@@ -131,7 +132,8 @@
private AlarmManager.AlarmClockInfo mNextAlarm;
public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController,
- CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher) {
+ CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher,
+ @UiBackground Executor uiBgExecutor) {
mContext = context;
mIconController = iconController;
mCast = Dependency.get(CastController.class);
@@ -148,6 +150,7 @@
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mLocationController = Dependency.get(LocationController.class);
mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
+ mUiBgExecutor = uiBgExecutor;
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -452,7 +455,7 @@
// getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in
// some cases. Since it doesn't really matter here whether it's updated in this frame
// or in the next one, we call this method from our UI offload thread.
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
final int userId;
try {
userId = ActivityTaskManager.getService().getLastResumedActivityUserId();
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 312f9c1..e96e258 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -30,6 +30,7 @@
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -136,13 +137,13 @@
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -232,6 +233,7 @@
import java.io.StringWriter;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
import javax.inject.Provider;
@@ -383,6 +385,8 @@
private final ShadeController mShadeController;
private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private final LightsOutNotifController mLightsOutNotifController;
+ private final InitController mInitController;
+ private final DarkIconDispatcher mDarkIconDispatcher;
private final DismissCallbackRegistry mDismissCallbackRegistry;
// expanded notifications
@@ -471,7 +475,7 @@
private ViewMediatorCallback mKeyguardViewMediatorCallback;
private final ScrimController mScrimController;
protected DozeScrimController mDozeScrimController;
- private final UiOffloadThread mUiOffloadThread;
+ private final Executor mUiBgExecutor;
protected boolean mDozing;
@@ -634,7 +638,7 @@
NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
- UiOffloadThread uiOffloadThread,
+ @UiBackground Executor uiBgExecutor,
NotificationMediaManager notificationMediaManager,
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
@@ -680,6 +684,9 @@
SuperStatusBarViewFactory superStatusBarViewFactory,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
+ InitController initController,
+ DarkIconDispatcher darkIconDispatcher,
+ @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
DismissCallbackRegistry dismissCallbackRegistry) {
super(context);
mFeatureFlags = featureFlags;
@@ -708,7 +715,7 @@
mNotificationAlertingManager = notificationAlertingManager;
mDisplayMetrics = displayMetrics;
mMetricsLogger = metricsLogger;
- mUiOffloadThread = uiOffloadThread;
+ mUiBgExecutor = uiBgExecutor;
mMediaManager = notificationMediaManager;
mLockscreenUserManager = lockScreenUserManager;
mRemoteInputManager = remoteInputManager;
@@ -753,6 +760,8 @@
mLightsOutNotifController = lightsOutNotifController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardViewMediatorCallback = viewMediatorCallback;
+ mInitController = initController;
+ mDarkIconDispatcher = darkIconDispatcher;
mDismissCallbackRegistry = dismissCallbackRegistry;
mBubbleExpandListener =
@@ -760,6 +769,9 @@
mEntryManager.updateNotifications("onBubbleExpandChanged");
updateScrimController();
};
+
+
+ DateTimeView.setReceiverHandler(timeTickHandler);
}
@Override
@@ -794,8 +806,6 @@
mVibrateOnOpening = mContext.getResources().getBoolean(
R.bool.config_vibrateOnIconAnimation);
- DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
-
// start old BaseStatusBar.start().
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
@@ -885,7 +895,7 @@
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController, mCommandQueue,
- mBroadcastDispatcher);
+ mBroadcastDispatcher, mUiBgExecutor);
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
mKeyguardStateController.addCallback(this);
@@ -902,7 +912,7 @@
// set the initial view visibility
int disabledFlags1 = result.mDisabledFlags1;
int disabledFlags2 = result.mDisabledFlags2;
- Dependency.get(InitController.class).addPostInitTask(
+ mInitController.addPostInitTask(
() -> setUpDisableFlags(disabledFlags1, disabledFlags2));
mPluginManager.addPluginListener(
@@ -983,7 +993,7 @@
mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons);
mNotificationPanel.addExpansionListener(mWakeUpCoordinator);
- Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
+ mDarkIconDispatcher.addDarkReceiver(mNotificationIconAreaController);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
Dependency.get(PluginDependencyProvider.class)
.allowPluginDependency(DarkIconDispatcher.class);
@@ -1231,7 +1241,7 @@
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
mNotificationAlertingManager, rowBinder, mKeyguardStateController,
mKeyguardIndicationController,
- this /* statusBar */, mShadeController, mCommandQueue);
+ this /* statusBar */, mShadeController, mCommandQueue, mInitController);
mNotificationListController =
new NotificationListController(
@@ -1331,7 +1341,7 @@
// TODO: Bring these out of StatusBar.
((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
.onDensityOrFontScaleChanged();
- Dependency.get(UserSwitcherController.class).onDensityOrFontScaleChanged();
+ mUserSwitcherController.onDensityOrFontScaleChanged();
if (mKeyguardUserSwitcher != null) {
mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
}
@@ -2840,7 +2850,7 @@
notificationLoad = 1;
}
final int finalNotificationLoad = notificationLoad;
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
mBarService.onPanelRevealed(clearNotificationEffects,
finalNotificationLoad);
@@ -2849,7 +2859,7 @@
}
});
} else {
- mUiOffloadThread.submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
mBarService.onPanelHidden();
} catch (RemoteException ex) {
@@ -3423,8 +3433,7 @@
// ringing.
// Other transitions are covered in handleVisibleToUserChanged().
if (mVisible && (newState == StatusBarState.SHADE_LOCKED
- || (((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class))
- .goingToFullShade()))) {
+ || mStatusBarStateController.goingToFullShade())) {
clearNotificationEffects();
}
if (newState == StatusBarState.KEYGUARD) {
@@ -4019,7 +4028,7 @@
}
void awakenDreams() {
- Dependency.get(UiOffloadThread.class).submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
mDreamManager.awaken();
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index e31c53a..84a763e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -17,8 +17,10 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.content.Context;
+import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
@@ -27,15 +29,17 @@
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.UiOffloadThread;
+import com.android.systemui.InitController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
@@ -75,6 +79,7 @@
import com.android.systemui.volume.VolumeComponent;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
import javax.inject.Provider;
@@ -122,7 +127,7 @@
NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
- UiOffloadThread uiOffloadThread,
+ @UiBackground Executor uiBgExecutor,
NotificationMediaManager notificationMediaManager,
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
@@ -168,6 +173,9 @@
SuperStatusBarViewFactory superStatusBarViewFactory,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
+ InitController initController,
+ DarkIconDispatcher darkIconDispatcher,
+ @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
DismissCallbackRegistry dismissCallbackRegistry) {
return new StatusBar(
context,
@@ -197,7 +205,7 @@
notificationAlertingManager,
displayMetrics,
metricsLogger,
- uiOffloadThread,
+ uiBgExecutor,
notificationMediaManager,
lockScreenUserManager,
remoteInputManager,
@@ -242,6 +250,9 @@
superStatusBarViewFactory,
statusBarKeyguardViewManager,
viewMediatorCallback,
+ initController,
+ darkIconDispatcher,
+ timeTickHandler,
dismissCallbackRegistry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index fa584d2..661a7b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -47,13 +47,12 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -74,6 +73,8 @@
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -115,6 +116,7 @@
private final Handler mBackgroundHandler;
private final ActivityIntentHelper mActivityIntentHelper;
private final BubbleController mBubbleController;
+ private final Executor mUiBgExecutor;
private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -133,7 +135,7 @@
KeyguardStateController keyguardStateController,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils,
- Handler mainThreadHandler, Handler backgroundHandler,
+ Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor,
ActivityIntentHelper activityIntentHelper, BubbleController bubbleController) {
mContext = context;
mNotificationPanel = panel;
@@ -160,6 +162,7 @@
mGroupManager = groupManager;
mLockPatternUtils = lockPatternUtils;
mBackgroundHandler = backgroundHandler;
+ mUiBgExecutor = uiBgExecutor;
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
@@ -418,7 +421,7 @@
} else {
// Stop screensaver if the notification has a fullscreen intent.
// (like an incoming phone call)
- Dependency.get(UiOffloadThread.class).submit(() -> {
+ mUiBgExecutor.execute(() -> {
try {
mDreamManager.awaken();
} catch (RemoteException e) {
@@ -521,6 +524,7 @@
private final LockPatternUtils mLockPatternUtils;
private final Handler mMainThreadHandler;
private final Handler mBackgroundHandler;
+ private final Executor mUiBgExecutor;
private final ActivityIntentHelper mActivityIntentHelper;
private final BubbleController mBubbleController;
private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@@ -551,6 +555,7 @@
LockPatternUtils lockPatternUtils,
@Main Handler mainThreadHandler,
@Background Handler backgroundHandler,
+ @UiBackground Executor uiBgExecutor,
ActivityIntentHelper activityIntentHelper,
BubbleController bubbleController,
ShadeController shadeController,
@@ -576,6 +581,7 @@
mLockPatternUtils = lockPatternUtils;
mMainThreadHandler = mainThreadHandler;
mBackgroundHandler = backgroundHandler;
+ mUiBgExecutor = uiBgExecutor;
mActivityIntentHelper = activityIntentHelper;
mBubbleController = bubbleController;
mShadeController = shadeController;
@@ -624,6 +630,7 @@
mLockPatternUtils,
mMainThreadHandler,
mBackgroundHandler,
+ mUiBgExecutor,
mActivityIntentHelper,
mBubbleController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 8fc624d..beb4579 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -146,7 +146,8 @@
KeyguardIndicationController keyguardIndicationController,
StatusBar statusBar,
ShadeController shadeController,
- CommandQueue commandQueue) {
+ CommandQueue commandQueue,
+ InitController initController) {
mContext = context;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
@@ -193,7 +194,7 @@
Dependency.get(StatusBarWindowController.class));
NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
- Dependency.get(InitController.class).addPostInitTask(() -> {
+ initController.addPostInitTask(() -> {
NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
@Override
public void onEntryRemoved(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 6828c32..66a1d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -33,7 +33,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
index 1c5d979..7cdba86 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -24,8 +24,10 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import javax.inject.Singleton;
@@ -128,4 +130,16 @@
public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
return new ExecutorImpl(new Handler(looper));
}
+
+ /**
+ * Provide an Executor specifically for running UI operations on a separate thread.
+ *
+ * Keep submitted runnables short and to the point, just as with any other UI code.
+ */
+ @Provides
+ @Singleton
+ @UiBackground
+ public static Executor provideUiBackgroundExecutor() {
+ return Executors.newSingleThreadExecutor();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java b/packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java
index 4316df1..6fef59f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/time/SystemClock.java
@@ -37,10 +37,4 @@
/** @see android.os.SystemClock#currentThreadTimeMillis() */
long currentThreadTimeMillis();
-
- /** @see android.os.SystemClock#currentThreadTimeMicro() */
- long currentThreadTimeMicro();
-
- /** @see android.os.SystemClock#currentTimeMicro() */
- long currentTimeMicro();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java b/packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java
index 532ea05..f0c7014 100644
--- a/packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/time/SystemClockImpl.java
@@ -42,14 +42,4 @@
public long currentThreadTimeMillis() {
return android.os.SystemClock.currentThreadTimeMillis();
}
-
- @Override
- public long currentThreadTimeMicro() {
- return android.os.SystemClock.currentThreadTimeMicro();
- }
-
- @Override
- public long currentTimeMicro() {
- return android.os.SystemClock.currentTimeMicro();
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 819a7f6..c85d600 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -117,7 +117,7 @@
}
protected void waitForUiOffloadThread() {
- Future<?> future = Dependency.get(UiOffloadThread.class).submit(() -> {});
+ Future<?> future = Dependency.get(UiOffloadThread.class).execute(() -> { });
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index 3561e34..a19d1df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -34,7 +34,9 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.After;
import org.junit.Before;
@@ -55,6 +57,7 @@
private FalsingManagerProxy mProxy;
private DeviceConfigProxy mDeviceConfig;
private TestableLooper mTestableLooper;
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
@@ -77,7 +80,7 @@
@Test
public void test_brightLineFalsingManagerDisabled() {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
- mDeviceConfig);
+ mDeviceConfig, mUiBgExecutor);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -87,14 +90,14 @@
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
mTestableLooper.processAllMessages();
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
- mDeviceConfig);
+ mDeviceConfig, mUiBgExecutor);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@Test
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
- mDeviceConfig);
+ mDeviceConfig, mUiBgExecutor);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/DismissCallbackRegistryTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/DismissCallbackRegistryTest.java
index 7fa1dbe..a00cabc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/DismissCallbackRegistryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/DismissCallbackRegistryTest.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.keyguard;
@@ -24,6 +24,8 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -41,10 +43,11 @@
private DismissCallbackRegistry mDismissCallbackRegistry;
private @Mock IKeyguardDismissCallback mMockCallback;
private @Mock IKeyguardDismissCallback mMockCallback2;
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
- mDismissCallbackRegistry = new DismissCallbackRegistry();
+ mDismissCallbackRegistry = new DismissCallbackRegistry(mUiBgExecutor);
MockitoAnnotations.initMocks(this);
}
@@ -52,7 +55,7 @@
public void testCancelled() throws Exception {
mDismissCallbackRegistry.addCallback(mMockCallback);
mDismissCallbackRegistry.notifyDismissCancelled();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mMockCallback).onDismissCancelled();
}
@@ -61,7 +64,7 @@
mDismissCallbackRegistry.addCallback(mMockCallback);
mDismissCallbackRegistry.addCallback(mMockCallback2);
mDismissCallbackRegistry.notifyDismissCancelled();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mMockCallback).onDismissCancelled();
verify(mMockCallback2).onDismissCancelled();
}
@@ -70,7 +73,7 @@
public void testSucceeded() throws Exception {
mDismissCallbackRegistry.addCallback(mMockCallback);
mDismissCallbackRegistry.notifyDismissSucceeded();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mMockCallback).onDismissSucceeded();
}
@@ -79,7 +82,7 @@
mDismissCallbackRegistry.addCallback(mMockCallback);
mDismissCallbackRegistry.addCallback(mMockCallback2);
mDismissCallbackRegistry.notifyDismissSucceeded();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mMockCallback).onDismissSucceeded();
verify(mMockCallback2).onDismissSucceeded();
}
@@ -89,7 +92,7 @@
mDismissCallbackRegistry.addCallback(mMockCallback);
mDismissCallbackRegistry.notifyDismissSucceeded();
mDismissCallbackRegistry.notifyDismissSucceeded();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mMockCallback, times(1)).onDismissSucceeded();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index a8a2b33..64fbc1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -38,6 +38,8 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -58,6 +60,7 @@
private @Mock StatusBarWindowController mStatusBarWindowController;
private @Mock BroadcastDispatcher mBroadcastDispatcher;
private @Mock DismissCallbackRegistry mDismissCallbackRegistry;
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private FalsingManagerFake mFalsingManager;
@@ -75,7 +78,7 @@
mViewMediator = new KeyguardViewMediator(
mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher,
mStatusBarWindowController, () -> mStatusBarKeyguardViewManager,
- mDismissCallbackRegistry);
+ mDismissCallbackRegistry, mUiBgExecutor);
});
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index a5395e8..402a99d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index e67aa69..bb9c14b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -26,7 +26,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
-import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -86,7 +85,6 @@
Dependency.get(NotificationLockscreenUserManager.class);
NotificationViewHierarchyManager viewHierarchyManager =
Dependency.get(NotificationViewHierarchyManager.class);
- Dependency.get(InitController.class).executePostInitTasks();
entryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
entryManager.addNotificationEntryListener(mEntryListener);
gutsManager.setUpWithPresenter(mPresenter, mListContainer,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index c7810be..40b0ba9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -22,6 +22,7 @@
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import android.annotation.Nullable;
@@ -46,14 +47,17 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.tests.R;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* A helper class to create {@link ExpandableNotificationRow} (for both individual and group
* notifications).
@@ -325,10 +329,8 @@
entry.setRow(row);
entry.createIcons(mContext, entry.getSbn());
row.setEntry(entry);
- row.getNotificationInflater().addInflationFlags(extraInflationFlags);
- NotificationContentInflaterTest.runThenWaitForInflation(
- () -> row.inflateViews(),
- row.getNotificationInflater());
+ row.setInflationFlags(extraInflationFlags);
+ inflateAndWait(row);
// This would be done as part of onAsyncInflationFinished, but we skip large amounts of
// the callback chain, so we need to make up for not adding it to the group manager
@@ -337,6 +339,28 @@
return row;
}
+ private static void inflateAndWait(ExpandableNotificationRow row) throws Exception {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ row.getNotificationInflater().setInflateSynchronously(true);
+ NotificationContentInflater.InflationCallback callback =
+ new NotificationContentInflater.InflationCallback() {
+ @Override
+ public void handleInflationException(StatusBarNotification notification,
+ Exception e) {
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onAsyncInflationFinished(NotificationEntry entry,
+ int inflatedFlags) {
+ countDownLatch.countDown();
+ }
+ };
+ row.setInflationCallback(callback);
+ row.inflateViews();
+ assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
+ }
+
private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent) {
Intent target = new Intent(mContext, BubblesTestActivity.class);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 1b05216..c97813d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -38,8 +38,6 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.Dependency;
-import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -107,7 +105,6 @@
mock(KeyguardBypassController.class),
mock(BubbleController.class),
mock(DynamicPrivacyController.class));
- Dependency.get(InitController.class).executePostInitTasks();
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 7f5105e..f55ea4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -62,7 +62,6 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -233,7 +232,6 @@
mock(PeopleNotificationIdentifier.class)),
mEnvironment
);
- Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
index 9f90396..6e9c2c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
@@ -727,7 +727,7 @@
mListBuilder.addPreGroupFilter(filter3);
// GIVEN the SystemClock is set to a particular time:
- mSystemClock.setUptimeMillis(47);
+ mSystemClock.setUptimeMillis(10047);
// WHEN the pipeline is kicked off on a list of notifs
addNotif(0, PACKAGE_1);
@@ -735,12 +735,12 @@
dispatchBuild();
// THEN the value of `now` is the same for all calls to shouldFilterOut
- verify(filter1).shouldFilterOut(mEntrySet.get(0), 47);
- verify(filter2).shouldFilterOut(mEntrySet.get(0), 47);
- verify(filter3).shouldFilterOut(mEntrySet.get(0), 47);
- verify(filter1).shouldFilterOut(mEntrySet.get(1), 47);
- verify(filter2).shouldFilterOut(mEntrySet.get(1), 47);
- verify(filter3).shouldFilterOut(mEntrySet.get(1), 47);
+ verify(filter1).shouldFilterOut(mEntrySet.get(0), 10047);
+ verify(filter2).shouldFilterOut(mEntrySet.get(0), 10047);
+ verify(filter3).shouldFilterOut(mEntrySet.get(0), 10047);
+ verify(filter1).shouldFilterOut(mEntrySet.get(1), 10047);
+ verify(filter2).shouldFilterOut(mEntrySet.get(1), 10047);
+ verify(filter3).shouldFilterOut(mEntrySet.get(1), 10047);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
index 4f1ffbe..2662c80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
@@ -29,10 +29,10 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -52,12 +52,12 @@
private NotificationLogger.ExpansionStateLogger mLogger;
@Mock
private IStatusBarService mBarService;
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mLogger = new NotificationLogger.ExpansionStateLogger(
- Dependency.get(UiOffloadThread.class));
+ mLogger = new NotificationLogger.ExpansionStateLogger(mUiBgExecutor);
mLogger.mBarService = mBarService;
}
@@ -66,7 +66,7 @@
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, Mockito.never()).onNotificationExpansionChanged(
eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean(), anyInt());
@@ -76,7 +76,7 @@
public void testExpanded() throws RemoteException {
mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, Mockito.never()).onNotificationExpansionChanged(
eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean(), anyInt());
@@ -89,7 +89,7 @@
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, Mockito.never()).onNotificationExpansionChanged(
eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean(), anyInt());
@@ -102,7 +102,7 @@
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService).onNotificationExpansionChanged(
NOTIFICATION_KEY, true, true,
@@ -117,7 +117,7 @@
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true,
NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA)),
Collections.emptyList());
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService).onNotificationExpansionChanged(
NOTIFICATION_KEY, false, true,
@@ -133,7 +133,7 @@
Collections.emptyList());
mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService).onNotificationExpansionChanged(
NOTIFICATION_KEY, false, true,
@@ -150,7 +150,7 @@
NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
mLogger.onExpansionChanged(NOTIFICATION_KEY, false, true,
NotificationVisibility.NotificationLocation.LOCATION_UNKNOWN);
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService).onNotificationExpansionChanged(
NOTIFICATION_KEY, false, true,
@@ -164,7 +164,7 @@
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService).onNotificationExpansionChanged(
NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN);
@@ -172,7 +172,7 @@
mLogger.onVisibilityChanged(
Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)),
Collections.emptyList());
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
// onNotificationExpansionChanged is called the second time.
verify(mBarService, times(2)).onNotificationExpansionChanged(
NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index e23d0ae..d826ce1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.statusbar.notification.logging;
@@ -36,9 +36,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -47,6 +45,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.google.android.collect.Lists;
@@ -60,6 +60,7 @@
import org.mockito.MockitoAnnotations;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -82,6 +83,7 @@
private TestableNotificationLogger mLogger;
private NotificationEntryListener mNotificationEntryListener;
private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() {
@@ -98,7 +100,7 @@
.build();
mEntry.setRow(mRow);
- mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class),
+ mLogger = new TestableNotificationLogger(mListener, mUiBgExecutor,
mEntryManager, mock(StatusBarStateControllerImpl.class), mBarService,
mExpansionStateLogger);
mLogger.setUpWithContainer(mListContainer);
@@ -130,7 +132,7 @@
when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
if(!mErrorQueue.isEmpty()) {
throw mErrorQueue.poll();
@@ -140,7 +142,7 @@
Mockito.reset(mBarService);
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, never()).onNotificationVisibilityChanged(any(), any());
}
@@ -152,11 +154,11 @@
when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
Mockito.reset(mBarService);
mLogger.stopNotificationLogging();
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
// The visibility objects are recycled by NotificationLogger, so we can't use specific
// matchers here.
verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
@@ -165,12 +167,12 @@
private class TestableNotificationLogger extends NotificationLogger {
TestableNotificationLogger(NotificationListener notificationListener,
- UiOffloadThread uiOffloadThread,
+ Executor uiBgExecutor,
NotificationEntryManager entryManager,
StatusBarStateControllerImpl statusBarStateController,
IStatusBarService barService,
ExpansionStateLogger expansionStateLogger) {
- super(notificationListener, uiOffloadThread, entryManager, statusBarStateController,
+ super(notificationListener, uiBgExecutor, entryManager, statusBarStateController,
expansionStateLogger);
mBarService = barService;
// Make this on the current thread so we can wait for it during tests.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index d17c573..3d79ce1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -19,9 +19,9 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -152,7 +152,7 @@
row.setNeedsRedaction(true);
- assertTrue(row.getNotificationInflater().isInflationFlagSet(FLAG_CONTENT_VIEW_PUBLIC));
+ assertTrue(row.isInflationFlagSet(FLAG_CONTENT_VIEW_PUBLIC));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index c7e59ef..dc4e498 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.notification.row;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -48,7 +48,9 @@
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.tests.R;
import org.junit.Assert;
@@ -82,25 +84,17 @@
ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
mBuilder.build());
mRow = spy(row);
- mNotificationInflater = new NotificationContentInflater(mRow);
- mNotificationInflater.setInflationCallback(new InflationCallback() {
- @Override
- public void handleInflationException(StatusBarNotification notification,
- Exception e) {
- }
-
- @Override
- public void onAsyncInflationFinished(NotificationEntry entry,
- @NotificationContentInflater.InflationFlag int inflatedFlags) {
- }
- });
+ mNotificationInflater = new NotificationContentInflater();
}
@Test
public void testIncreasedHeadsUpBeingUsed() {
- mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
+ BindParams params = new BindParams();
+ params.usesIncreasedHeadsUpHeight = true;
Notification.Builder builder = spy(mBuilder);
- mNotificationInflater.inflateNotificationViews(
+ mNotificationInflater.inflateNotificationViews(mRow.getEntry(),
+ mRow,
+ params,
true /* inflateSynchronously */,
FLAG_CONTENT_VIEW_ALL,
builder,
@@ -110,9 +104,12 @@
@Test
public void testIncreasedHeightBeingUsed() {
- mNotificationInflater.setUsesIncreasedHeight(true);
+ BindParams params = new BindParams();
+ params.usesIncreasedHeight = true;
Notification.Builder builder = spy(mBuilder);
- mNotificationInflater.inflateNotificationViews(
+ mNotificationInflater.inflateNotificationViews(mRow.getEntry(),
+ mRow,
+ params,
true /* inflateSynchronously */,
FLAG_CONTENT_VIEW_ALL,
builder,
@@ -122,17 +119,13 @@
@Test
public void testInflationCallsUpdated() throws Exception {
- runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
- mNotificationInflater);
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
verify(mRow).onNotificationUpdated();
}
@Test
public void testInflationOnlyInflatesSetFlags() throws Exception {
- mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP,
- true /* shouldInflate */);
- runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
- mNotificationInflater);
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, mRow);
assertNotNull(mRow.getPrivateLayout().getHeadsUpChild());
verify(mRow).onNotificationUpdated();
@@ -143,8 +136,8 @@
mRow.getPrivateLayout().removeAllViews();
mRow.getEntry().getSbn().getNotification().contentView
= new RemoteViews(mContext.getPackageName(), R.layout.status_bar);
- runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
- true /* expectingException */, mNotificationInflater);
+ inflateAndWait(true /* expectingException */, mNotificationInflater, FLAG_CONTENT_VIEW_ALL,
+ mRow);
assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
verify(mRow, times(0)).onNotificationUpdated();
}
@@ -152,8 +145,7 @@
@Test
public void testAsyncTaskRemoved() throws Exception {
mRow.getEntry().abortTask();
- runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
- mNotificationInflater);
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
verify(mRow).onNotificationUpdated();
}
@@ -161,7 +153,13 @@
public void testRemovedNotInflated() throws Exception {
mRow.setRemoved();
mNotificationInflater.setInflateSynchronously(true);
- mNotificationInflater.inflateNotificationViews();
+ mNotificationInflater.bindContent(
+ mRow.getEntry(),
+ mRow,
+ FLAG_CONTENT_VIEW_ALL,
+ new BindParams(),
+ false /* forceInflate */,
+ null /* callback */);
Assert.assertNull(mRow.getEntry().getRunningTask());
}
@@ -189,7 +187,7 @@
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
- @NotificationContentInflater.InflationFlag int inflatedFlags) {
+ @InflationFlag int inflatedFlags) {
countDownLatch.countDown();
}
}, mRow.getPrivateLayout(), null, null, new HashMap<>(),
@@ -207,26 +205,25 @@
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
}
- @Test
- public void testUpdateNeedsRedactionReinflatesChangedContentViews() {
- mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, true);
- mNotificationInflater.updateNeedsRedaction(true);
-
- NotificationContentInflater.AsyncInflationTask asyncInflationTask =
- (NotificationContentInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
- assertEquals(FLAG_CONTENT_VIEW_PUBLIC, asyncInflationTask.getReInflateFlags());
- asyncInflationTask.abort();
- }
-
/* Cancelling requires us to be on the UI thread otherwise we might have a race */
@Test
public void testSupersedesExistingTask() {
- mNotificationInflater.addInflationFlags(FLAG_CONTENT_VIEW_ALL);
- mNotificationInflater.inflateNotificationViews();
+ mNotificationInflater.bindContent(
+ mRow.getEntry(),
+ mRow,
+ FLAG_CONTENT_VIEW_ALL,
+ new BindParams(),
+ false /* forceInflate */,
+ null /* callback */);
- // Trigger inflation of content and expanded only.
- mNotificationInflater.setIsLowPriority(true);
- mNotificationInflater.setIsChildInGroup(true);
+ // Trigger inflation of contracted only.
+ mNotificationInflater.bindContent(
+ mRow.getEntry(),
+ mRow,
+ FLAG_CONTENT_VIEW_CONTRACTED,
+ new BindParams(),
+ false /* forceInflate */,
+ null /* callback */);
InflationTask runningTask = mRow.getEntry().getRunningTask();
NotificationContentInflater.AsyncInflationTask asyncInflationTask =
@@ -248,17 +245,21 @@
NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
}
- public static void runThenWaitForInflation(Runnable block,
- NotificationContentInflater inflater) throws Exception {
- runThenWaitForInflation(block, false /* expectingException */, inflater);
+ private static void inflateAndWait(NotificationContentInflater inflater,
+ @InflationFlag int contentToInflate,
+ ExpandableNotificationRow row)
+ throws Exception {
+ inflateAndWait(false /* expectingException */, inflater, contentToInflate, row);
}
- private static void runThenWaitForInflation(Runnable block, boolean expectingException,
- NotificationContentInflater inflater) throws Exception {
+ private static void inflateAndWait(boolean expectingException,
+ NotificationContentInflater inflater,
+ @InflationFlag int contentToInflate,
+ ExpandableNotificationRow row) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
final ExceptionHolder exceptionHolder = new ExceptionHolder();
inflater.setInflateSynchronously(true);
- inflater.setInflationCallback(new InflationCallback() {
+ InflationCallback callback = new InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
Exception e) {
@@ -270,15 +271,21 @@
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
- @NotificationContentInflater.InflationFlag int inflatedFlags) {
+ @InflationFlag int inflatedFlags) {
if (expectingException) {
exceptionHolder.setException(new RuntimeException(
"Inflation finished even though there should be an error"));
}
countDownLatch.countDown();
}
- });
- block.run();
+ };
+ inflater.bindContent(
+ row.getEntry(),
+ row,
+ contentToInflate,
+ new BindParams(),
+ false /* forceInflate */,
+ callback /* callback */);
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
if (exceptionHolder.mException != null) {
throw exceptionHolder.mException;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index deca51f..77a6a26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -47,9 +47,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.ActivityStarterDelegate;
-import com.android.systemui.Dependency;
import com.android.systemui.ExpandHelper;
-import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
@@ -170,7 +168,6 @@
),
mock(NotificationEntryManager.KeyguardEnvironment.class));
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
- Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 532192b..86b2a44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -72,6 +72,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -126,6 +128,7 @@
private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@Mock
private NotificationPanelView mNotificationPanelView;
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private NotificationTestHelper mNotificationTestHelper;
private ExpandableNotificationRow mNotificationRow;
@@ -178,8 +181,9 @@
mock(NotificationLockscreenUserManager.class),
mKeyguardStateController,
mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
- mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper,
- mBubbleController, mShadeController, mSuperStatusBarViewFactory))
+ mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
+ mActivityIntentHelper, mBubbleController, mShadeController,
+ mSuperStatusBarViewFactory))
.setStatusBar(mStatusBar)
.setNotificationPresenter(mock(NotificationPresenter.class))
.setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index f6ed4e6..1296a97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -77,6 +77,7 @@
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
private StatusBar mStatusBar = mock(StatusBar.class);
+ private InitController mInitController = new InitController();
@Before
public void setup() {
@@ -100,7 +101,6 @@
mDependency.injectMockDependency(VisualStabilityManager.class);
mDependency.injectMockDependency(NotificationGutsManager.class);
mDependency.injectMockDependency(StatusBarWindowController.class);
- mDependency.injectMockDependency(InitController.class);
NotificationEntryManager entryManager =
mDependency.injectMockDependency(NotificationEntryManager.class);
when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
@@ -115,7 +115,7 @@
mock(NotificationAlertingManager.class),
mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class),
- mStatusBar, mock(ShadeControllerImpl.class), mCommandQueue);
+ mStatusBar, mock(ShadeControllerImpl.class), mCommandQueue, mInitController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d3ae7a7..61dd2aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -74,11 +74,9 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
@@ -89,6 +87,7 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
@@ -133,6 +132,8 @@
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
import org.junit.Before;
@@ -244,7 +245,10 @@
@Mock private LockscreenLockIconController mLockscreenLockIconController;
@Mock private StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
+ @Mock private DarkIconDispatcher mDarkIconDispatcher;
private ShadeController mShadeController;
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+ private InitController mInitController = new InitController();
@Before
public void setup() throws Exception {
@@ -266,7 +270,7 @@
mMetricsLogger = new FakeMetricsLogger();
NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
- Dependency.get(UiOffloadThread.class), mEntryManager, mStatusBarStateController,
+ mUiBgExecutor, mEntryManager, mStatusBarStateController,
mExpansionStateLogger);
notificationLogger.setVisibilityReporter(mock(Runnable.class));
@@ -349,7 +353,7 @@
mNotificationAlertingManager,
new DisplayMetrics(),
mMetricsLogger,
- Dependency.get(UiOffloadThread.class),
+ mUiBgExecutor,
mNotificationMediaManager,
mLockscreenUserManager,
mRemoteInputManager,
@@ -393,6 +397,9 @@
mSuperStatusBarViewFactory,
mStatusBarKeyguardViewManager,
mViewMediatorCallback,
+ mInitController,
+ mDarkIconDispatcher,
+ new Handler(TestableLooper.get(this).getLooper()),
mDismissCallbackRegistry);
when(mStatusBarWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
@@ -417,7 +424,7 @@
mStatusBar.mBarService = mBarService;
mStatusBar.mStackScroller = mStackScroller;
mStatusBar.startKeyguard();
- Dependency.get(InitController.class).executePostInitTasks();
+ mInitController.executePostInitTasks();
notificationLogger.setUpWithContainer(mStackScroller);
}
@@ -641,7 +648,7 @@
public void testLogHidden() {
try {
mStatusBar.handleVisibleToUserChanged(false);
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, times(1)).onPanelHidden();
verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
} catch (RemoteException e) {
@@ -659,7 +666,7 @@
try {
mStatusBar.handleVisibleToUserChanged(true);
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(false, 1);
} catch (RemoteException e) {
@@ -678,7 +685,7 @@
try {
mStatusBar.handleVisibleToUserChanged(true);
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(true, 5);
} catch (RemoteException e) {
@@ -696,7 +703,7 @@
try {
mStatusBar.handleVisibleToUserChanged(true);
- waitForUiOffloadThread();
+ mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(false, 5);
} catch (RemoteException e) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
index f3c0530..7c7ad53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
@@ -17,7 +17,6 @@
package com.android.systemui.util.concurrency;
import com.android.systemui.util.time.FakeSystemClock;
-import com.android.systemui.util.time.FakeSystemClock.ClockTickListener;
import java.util.Collections;
import java.util.PriorityQueue;
@@ -29,15 +28,6 @@
private PriorityQueue<QueuedRunnable> mQueuedRunnables = new PriorityQueue<>();
private boolean mIgnoreClockUpdates;
- private ClockTickListener mClockTickListener = new ClockTickListener() {
- @Override
- public void onUptimeMillis(long uptimeMillis) {
- if (!mIgnoreClockUpdates) {
- runAllReady();
- }
- }
- };
-
/**
* Initializes a fake executor.
*
@@ -47,7 +37,11 @@
*/
public FakeExecutor(FakeSystemClock clock) {
mClock = clock;
- mClock.addListener(mClockTickListener);
+ mClock.addListener(() -> {
+ if (!mIgnoreClockUpdates) {
+ runAllReady();
+ }
+ });
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
index bd64124..abc283f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -52,28 +52,28 @@
FakeExecutor fakeExecutor = new FakeExecutor(clock);
RunnableImpl runnable = new RunnableImpl();
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(0, runnable.mRunCount);
// Execute two runnables. They should not run and should be left pending.
fakeExecutor.execute(runnable);
assertEquals(0, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(1, fakeExecutor.numPending());
fakeExecutor.execute(runnable);
assertEquals(0, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(2, fakeExecutor.numPending());
// Run one pending runnable.
assertTrue(fakeExecutor.runNextReady());
assertEquals(1, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(1, fakeExecutor.numPending());
// Run a second pending runnable.
assertTrue(fakeExecutor.runNextReady());
assertEquals(2, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(0, fakeExecutor.numPending());
// No more runnables to run.
@@ -83,12 +83,12 @@
fakeExecutor.execute(runnable);
fakeExecutor.execute(runnable);
assertEquals(2, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(2, fakeExecutor.numPending());
// Execute all pending runnables in batch.
assertEquals(2, fakeExecutor.runAllReady());
assertEquals(4, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(0, fakeExecutor.runAllReady());
}
@@ -106,7 +106,7 @@
fakeExecutor.executeDelayed(runnable, 50);
fakeExecutor.executeDelayed(runnable, 100);
assertEquals(0, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(3, fakeExecutor.numPending());
// Delayed runnables should not advance the clock and therefore should not run.
assertFalse(fakeExecutor.runNextReady());
@@ -140,7 +140,7 @@
fakeExecutor.executeDelayed(runnable, 50);
fakeExecutor.executeDelayed(runnable, 100);
assertEquals(0, runnable.mRunCount);
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
assertEquals(3, fakeExecutor.numPending());
// Delayed runnables should not advance the clock and therefore should not run.
assertFalse(fakeExecutor.runNextReady());
@@ -150,24 +150,24 @@
// Advance the clock to the next runnable. Check that it is run.
assertEquals(1, fakeExecutor.advanceClockToNext());
assertEquals(1, fakeExecutor.runAllReady());
- assertEquals(1, clock.uptimeMillis());
+ assertEquals(10001, clock.uptimeMillis());
assertEquals(2, fakeExecutor.numPending());
assertEquals(1, runnable.mRunCount);
assertEquals(49, fakeExecutor.advanceClockToNext());
assertEquals(1, fakeExecutor.runAllReady());
- assertEquals(50, clock.uptimeMillis());
+ assertEquals(10050, clock.uptimeMillis());
assertEquals(1, fakeExecutor.numPending());
assertEquals(2, runnable.mRunCount);
assertEquals(50, fakeExecutor.advanceClockToNext());
assertEquals(1, fakeExecutor.runAllReady());
- assertEquals(100, clock.uptimeMillis());
+ assertEquals(10100, clock.uptimeMillis());
assertEquals(0, fakeExecutor.numPending());
assertEquals(3, runnable.mRunCount);
// Nothing left to do
assertEquals(0, fakeExecutor.advanceClockToNext());
assertEquals(0, fakeExecutor.runAllReady());
- assertEquals(100, clock.uptimeMillis());
+ assertEquals(10100, clock.uptimeMillis());
assertEquals(0, fakeExecutor.numPending());
assertEquals(3, runnable.mRunCount);
}
@@ -193,7 +193,7 @@
return null;
};
- assertEquals(0, clock.uptimeMillis());
+ assertEquals(10000, clock.uptimeMillis());
checkRunCounts.invoke(0, 0, 0, 0);
fakeExecutor.execute(runnableA);
@@ -227,8 +227,8 @@
fakeExecutor.execute(runnableA);
fakeExecutor.executeAtTime(runnableB, 0); // this is in the past!
- fakeExecutor.executeAtTime(runnableC, 1000);
- fakeExecutor.executeAtTime(runnableD, 500);
+ fakeExecutor.executeAtTime(runnableC, 11000);
+ fakeExecutor.executeAtTime(runnableD, 10500);
fakeExecutor.advanceClockToNext();
fakeExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index e94eaaf..601f88e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -16,94 +16,70 @@
package com.android.systemui.util.time;
+import com.android.systemui.util.concurrency.FakeExecutor;
+
import java.util.ArrayList;
import java.util.List;
+/**
+ * A fake {@link SystemClock} for use with {@link FakeExecutor}.
+ *
+ * Attempts to simulate the behavior of a real system clock. Time can be moved forward but not
+ * backwards. uptimeMillis, elapsedRealtime, and currentThreadTimeMillis are all kept in sync.
+ *
+ * Unless otherwise specified, uptimeMillis and elapsedRealtime will advance the same amount with
+ * every call to {@link #advanceTime(long)}. Thread time always lags by 50% of the uptime
+ * advancement to simulate time loss due to scheduling.
+ */
public class FakeSystemClock implements SystemClock {
- private long mUptimeMillis;
- private long mElapsedRealtime;
- private long mElapsedRealtimeNanos;
- private long mCurrentThreadTimeMillis;
- private long mCurrentThreadTimeMicro;
- private long mCurrentTimeMicro;
+ private long mUptimeMillis = 10000;
+ private long mElapsedRealtime = 10000;
+ private long mCurrentThreadTimeMillis = 10000;
- List<ClockTickListener> mListeners = new ArrayList<>();
+ private final List<ClockTickListener> mListeners = new ArrayList<>();
@Override
public long uptimeMillis() {
- long value = mUptimeMillis;
- return value;
+ return mUptimeMillis;
}
@Override
public long elapsedRealtime() {
- long value = mElapsedRealtime;
- return value;
+ return mElapsedRealtime;
}
@Override
public long elapsedRealtimeNanos() {
- long value = mElapsedRealtimeNanos;
- return value;
+ return mElapsedRealtime * 1000000 + 447;
}
@Override
public long currentThreadTimeMillis() {
- long value = mCurrentThreadTimeMillis;
- return value;
+ return mCurrentThreadTimeMillis;
}
- @Override
- public long currentThreadTimeMicro() {
- long value = mCurrentThreadTimeMicro;
- return value;
+ public void setUptimeMillis(long uptime) {
+ advanceTime(uptime - mUptimeMillis);
}
- @Override
- public long currentTimeMicro() {
- long value = mCurrentTimeMicro;
- return value;
+ public void advanceTime(long uptime) {
+ advanceTime(uptime, 0);
}
- public void setUptimeMillis(long uptimeMillis) {
- mUptimeMillis = uptimeMillis;
- for (ClockTickListener listener : mListeners) {
- listener.onUptimeMillis(mUptimeMillis);
+ public void advanceTime(long uptime, long sleepTime) {
+ if (uptime < 0 || sleepTime < 0) {
+ throw new IllegalArgumentException("Time cannot go backwards");
}
- }
- public void setElapsedRealtime(long elapsedRealtime) {
- mElapsedRealtime = elapsedRealtime;
- for (ClockTickListener listener : mListeners) {
- listener.onElapsedRealtime(mElapsedRealtime);
- }
- }
+ if (uptime > 0 || sleepTime > 0) {
+ mUptimeMillis += uptime;
+ mElapsedRealtime += uptime + sleepTime;
- public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
- mElapsedRealtimeNanos = elapsedRealtimeNanos;
- for (ClockTickListener listener : mListeners) {
- listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
- }
- }
+ mCurrentThreadTimeMillis += Math.ceil(uptime * 0.5);
- public void setCurrentThreadTimeMillis(long currentThreadTimeMillis) {
- mCurrentThreadTimeMillis = currentThreadTimeMillis;
- for (ClockTickListener listener : mListeners) {
- listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
- }
- }
-
- public void setCurrentThreadTimeMicro(long currentThreadTimeMicro) {
- mCurrentThreadTimeMicro = currentThreadTimeMicro;
- for (ClockTickListener listener : mListeners) {
- listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
- }
- }
-
- public void setCurrentTimeMicro(long currentTimeMicro) {
- mCurrentTimeMicro = currentTimeMicro;
- for (ClockTickListener listener : mListeners) {
- listener.onCurrentTimeMicro(mCurrentTimeMicro);
+ for (ClockTickListener listener : mListeners) {
+ listener.onClockTick();
+ }
}
}
@@ -115,30 +91,9 @@
mListeners.remove(listener);
}
- /** Alert all the listeners about the current time. */
- public void synchronizeListeners() {
- for (ClockTickListener listener : mListeners) {
- listener.onUptimeMillis(mUptimeMillis);
- listener.onElapsedRealtime(mElapsedRealtime);
- listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
- listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
- listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
- listener.onCurrentTimeMicro(mCurrentTimeMicro);
- }
- }
-
-
public interface ClockTickListener {
- default void onUptimeMillis(long uptimeMillis) {}
-
- default void onElapsedRealtime(long elapsedRealtime) {}
-
- default void onElapsedRealtimeNanos(long elapsedRealtimeNanos) {}
-
- default void onCurrentThreadTimeMillis(long currentThreadTimeMillis) {}
-
- default void onCurrentThreadTimeMicro(long currentThreadTimeMicro) {}
-
- default void onCurrentTimeMicro(long currentTimeMicro) {}
+ void onClockTick();
}
+
+ private static final long START_TIME = 10000;
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index edfe3ca..9305414 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -21,7 +21,6 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkState;
import android.net.RouteInfo;
import android.net.ip.IpServer;
import android.net.util.NetworkConstants;
@@ -72,7 +71,7 @@
private final LinkedList<Downstream> mActiveDownstreams;
private final byte[] mUniqueLocalPrefix;
private short mNextSubnetId;
- private NetworkState mUpstreamNetworkState;
+ private UpstreamNetworkState mUpstreamNetworkState;
public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
mNotifyList = notifyList;
@@ -115,11 +114,11 @@
}
/**
- * Call when upstream NetworkState may be changed.
- * If upstream has ipv6 for tethering, update this new NetworkState
+ * Call when UpstreamNetworkState may be changed.
+ * If upstream has ipv6 for tethering, update this new UpstreamNetworkState
* to IpServer. Otherwise stop ipv6 tethering on downstream interfaces.
*/
- public void updateUpstreamNetworkState(NetworkState ns) {
+ public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
@@ -144,18 +143,15 @@
}
}
- private void setUpstreamNetworkState(NetworkState ns) {
+ private void setUpstreamNetworkState(UpstreamNetworkState ns) {
if (ns == null) {
mUpstreamNetworkState = null;
} else {
// Make a deep copy of the parts we need.
- mUpstreamNetworkState = new NetworkState(
- null,
+ mUpstreamNetworkState = new UpstreamNetworkState(
new LinkProperties(ns.linkProperties),
new NetworkCapabilities(ns.networkCapabilities),
- new Network(ns.network),
- null,
- null);
+ new Network(ns.network));
}
mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
@@ -295,14 +291,11 @@
return in6addr;
}
- private static String toDebugString(NetworkState ns) {
+ private static String toDebugString(UpstreamNetworkState ns) {
if (ns == null) {
- return "NetworkState{null}";
+ return "UpstreamNetworkState{null}";
}
- return String.format("NetworkState{%s, %s, %s}",
- ns.network,
- ns.networkCapabilities,
- ns.linkProperties);
+ return ns.toString();
}
private static void stopIPv6TetheringOn(IpServer ipServer) {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index a68b9b2..3d414ee 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -70,7 +70,6 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
-import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.TetherStatesParcel;
import android.net.TetheringConfigurationParcel;
@@ -1152,7 +1151,7 @@
// Needed because the canonical source of upstream truth is just the
// upstream interface set, |mCurrentUpstreamIfaceSet|.
- private boolean pertainsToCurrentUpstream(NetworkState ns) {
+ private boolean pertainsToCurrentUpstream(UpstreamNetworkState ns) {
if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
@@ -1320,7 +1319,7 @@
maybeDunSettingChanged();
final TetheringConfiguration config = mConfig;
- final NetworkState ns = (config.chooseUpstreamAutomatically)
+ final UpstreamNetworkState ns = (config.chooseUpstreamAutomatically)
? mUpstreamNetworkMonitor.getCurrentPreferredUpstream()
: mUpstreamNetworkMonitor.selectPreferredUpstreamType(
config.preferredUpstreamIfaceTypes);
@@ -1341,7 +1340,7 @@
}
}
- protected void setUpstreamNetwork(NetworkState ns) {
+ protected void setUpstreamNetwork(UpstreamNetworkState ns) {
InterfaceSet ifaces = null;
if (ns != null) {
// Find the interface with the default IPv4 route. It may be the
@@ -1357,7 +1356,7 @@
}
notifyDownstreamsOfNewUpstreamIface(ifaces);
if (ns != null && pertainsToCurrentUpstream(ns)) {
- // If we already have NetworkState for this network update it immediately.
+ // If we already have UpstreamNetworkState for this network update it immediately.
handleNewUpstreamNetworkState(ns);
} else if (mCurrentUpstreamIfaceSet == null) {
// There are no available upstream networks.
@@ -1394,7 +1393,7 @@
}
}
- protected void handleNewUpstreamNetworkState(NetworkState ns) {
+ protected void handleNewUpstreamNetworkState(UpstreamNetworkState ns) {
mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
mOffload.updateUpstreamNetworkState(ns);
}
@@ -1458,7 +1457,7 @@
return;
}
- final NetworkState ns = (NetworkState) o;
+ final UpstreamNetworkState ns = (UpstreamNetworkState) o;
if (ns == null || !pertainsToCurrentUpstream(ns)) {
// TODO: In future, this is where upstream evaluation and selection
@@ -1728,7 +1727,7 @@
mOffloadController.stop();
}
- public void updateUpstreamNetworkState(NetworkState ns) {
+ public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
mOffloadController.setUpstreamLinkProperties(
(ns != null) ? ns.linkProperties : null);
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
index 0ef3805..6334c20 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
-import android.net.NetworkState;
import android.net.RouteInfo;
import android.net.util.InterfaceSet;
@@ -35,7 +34,7 @@
* Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
* @return null if there is no usable interface, or a set of at least one interface otherwise.
*/
- public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
+ public static @Nullable InterfaceSet getTetheringInterfaces(UpstreamNetworkState ns) {
if (ns == null) {
return null;
}
@@ -51,7 +50,7 @@
* Get the upstream interface for IPv6 tethering.
* @return null if there is no usable interface, or the interface name otherwise.
*/
- public static @Nullable String getIPv6Interface(NetworkState ns) {
+ public static @Nullable String getIPv6Interface(UpstreamNetworkState ns) {
// Broadly speaking:
//
// [1] does the upstream have an IPv6 default route?
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 9769596..dc38c49a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -32,7 +32,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.net.NetworkState;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -90,7 +89,7 @@
private final StateMachine mTarget;
private final Handler mHandler;
private final int mWhat;
- private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
+ private final HashMap<Network, UpstreamNetworkState> mNetworkMap = new HashMap<>();
private HashSet<IpPrefix> mLocalPrefixes;
private ConnectivityManager mCM;
private EntitlementManager mEntitlementMgr;
@@ -236,7 +235,7 @@
/**
* Select the first available network from |perferredTypes|.
*/
- public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
+ public UpstreamNetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
@@ -274,8 +273,8 @@
* preferred upstream would be DUN otherwise preferred upstream is the same as default network.
* Returns null if no current upstream is available.
*/
- public NetworkState getCurrentPreferredUpstream() {
- final NetworkState dfltState = (mDefaultInternetNetwork != null)
+ public UpstreamNetworkState getCurrentPreferredUpstream() {
+ final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
? mNetworkMap.get(mDefaultInternetNetwork)
: null;
if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
@@ -312,11 +311,11 @@
if (mNetworkMap.containsKey(network)) return;
if (VDBG) Log.d(TAG, "onAvailable for " + network);
- mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
+ mNetworkMap.put(network, new UpstreamNetworkState(null, null, network));
}
private void handleNetCap(Network network, NetworkCapabilities newNc) {
- final NetworkState prev = mNetworkMap.get(network);
+ final UpstreamNetworkState prev = mNetworkMap.get(network);
if (prev == null || newNc.equals(prev.networkCapabilities)) {
// Ignore notifications about networks for which we have not yet
// received onAvailable() (should never happen) and any duplicate
@@ -336,15 +335,15 @@
mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
}
- mNetworkMap.put(network, new NetworkState(
- null, prev.linkProperties, newNc, network, null, null));
+ mNetworkMap.put(network, new UpstreamNetworkState(
+ prev.linkProperties, newNc, network));
// TODO: If sufficient information is available to select a more
// preferable upstream, do so now and notify the target.
notifyTarget(EVENT_ON_CAPABILITIES, network);
}
private void handleLinkProp(Network network, LinkProperties newLp) {
- final NetworkState prev = mNetworkMap.get(network);
+ final UpstreamNetworkState prev = mNetworkMap.get(network);
if (prev == null || newLp.equals(prev.linkProperties)) {
// Ignore notifications about networks for which we have not yet
// received onAvailable() (should never happen) and any duplicate
@@ -357,8 +356,8 @@
network, newLp));
}
- mNetworkMap.put(network, new NetworkState(
- null, newLp, prev.networkCapabilities, network, null, null));
+ mNetworkMap.put(network, new UpstreamNetworkState(
+ newLp, prev.networkCapabilities, network));
// TODO: If sufficient information is available to select a more
// preferable upstream, do so now and notify the target.
notifyTarget(EVENT_ON_LINKPROPERTIES, network);
@@ -509,11 +508,11 @@
private static class TypeStatePair {
public int type = TYPE_NONE;
- public NetworkState ns = null;
+ public UpstreamNetworkState ns = null;
}
private static TypeStatePair findFirstAvailableUpstreamByType(
- Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes,
+ Iterable<UpstreamNetworkState> netStates, Iterable<Integer> preferredTypes,
boolean isCellularUpstreamPermitted) {
final TypeStatePair result = new TypeStatePair();
@@ -532,7 +531,7 @@
nc.setSingleUid(Process.myUid());
- for (NetworkState value : netStates) {
+ for (UpstreamNetworkState value : netStates) {
if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
continue;
}
@@ -546,10 +545,10 @@
return result;
}
- private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) {
+ private static HashSet<IpPrefix> allLocalPrefixes(Iterable<UpstreamNetworkState> netStates) {
final HashSet<IpPrefix> prefixSet = new HashSet<>();
- for (NetworkState ns : netStates) {
+ for (UpstreamNetworkState ns : netStates) {
final LinkProperties lp = ns.linkProperties;
if (lp == null) continue;
prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp));
@@ -563,7 +562,7 @@
return Integer.toString(nc.getSignalStrength());
}
- private static boolean isCellular(NetworkState ns) {
+ private static boolean isCellular(UpstreamNetworkState ns) {
return (ns != null) && isCellular(ns.networkCapabilities);
}
@@ -572,18 +571,19 @@
&& nc.hasCapability(NET_CAPABILITY_NOT_VPN);
}
- private static boolean hasCapability(NetworkState ns, int netCap) {
+ private static boolean hasCapability(UpstreamNetworkState ns, int netCap) {
return (ns != null) && (ns.networkCapabilities != null)
&& ns.networkCapabilities.hasCapability(netCap);
}
- private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
+ private static boolean isNetworkUsableAndNotCellular(UpstreamNetworkState ns) {
return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null)
&& !isCellular(ns.networkCapabilities);
}
- private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
- for (NetworkState ns : netStates) {
+ private static UpstreamNetworkState findFirstDunNetwork(
+ Iterable<UpstreamNetworkState> netStates) {
+ for (UpstreamNetworkState ns : netStates) {
if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java
new file mode 100644
index 0000000..68bb837
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Snapshot of tethering upstream network state.
+ */
+public class UpstreamNetworkState {
+ /** {@link LinkProperties}. */
+ public final LinkProperties linkProperties;
+ /** {@link NetworkCapabilities}. */
+ public final NetworkCapabilities networkCapabilities;
+ /** {@link Network}. */
+ public final Network network;
+
+ /** Constructs a new UpstreamNetworkState. */
+ public UpstreamNetworkState(LinkProperties linkProperties,
+ NetworkCapabilities networkCapabilities, Network network) {
+ this.linkProperties = linkProperties;
+ this.networkCapabilities = networkCapabilities;
+ this.network = network;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return String.format("UpstreamNetworkState{%s, %s, %s}",
+ network == null ? "null" : network,
+ networkCapabilities == null ? "null" : networkCapabilities,
+ linkProperties == null ? "null" : linkProperties);
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 31ed823..9d9ad10 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -27,7 +27,6 @@
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
@@ -82,7 +81,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
-import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.TetherStatesParcel;
@@ -360,10 +358,8 @@
}
}
- private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
- boolean with464xlat) {
- final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, 0, null, null);
- info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
+ boolean withIPv6, boolean with464xlat) {
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_MOBILE_IFNAME);
@@ -393,22 +389,22 @@
final NetworkCapabilities capabilities = new NetworkCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
+ return new UpstreamNetworkState(prop, capabilities, new Network(100));
}
- private static NetworkState buildMobileIPv4UpstreamState() {
+ private static UpstreamNetworkState buildMobileIPv4UpstreamState() {
return buildMobileUpstreamState(true, false, false);
}
- private static NetworkState buildMobileIPv6UpstreamState() {
+ private static UpstreamNetworkState buildMobileIPv6UpstreamState() {
return buildMobileUpstreamState(false, true, false);
}
- private static NetworkState buildMobileDualStackUpstreamState() {
+ private static UpstreamNetworkState buildMobileDualStackUpstreamState() {
return buildMobileUpstreamState(true, true, false);
}
- private static NetworkState buildMobile464xlatUpstreamState() {
+ private static UpstreamNetworkState buildMobile464xlatUpstreamState() {
return buildMobileUpstreamState(false, true, true);
}
@@ -562,7 +558,7 @@
verifyNoMoreInteractions(mWifiManager);
}
- private void prepareUsbTethering(NetworkState upstreamState) {
+ private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
@@ -577,7 +573,7 @@
@Test
public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
- NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
prepareUsbTethering(upstreamState);
// This should produce no activity of any kind.
@@ -657,14 +653,14 @@
/**
* Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator.
*/
- private void sendIPv6TetherUpdates(NetworkState upstreamState) {
+ private void sendIPv6TetherUpdates(UpstreamNetworkState upstreamState) {
// IPv6TetheringCoordinator must have been notified of downstream
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
eq(IpServer.STATE_TETHERED));
for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) {
- NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
+ UpstreamNetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIpv6Provisioned()
? ipv6OnlyState.linkProperties
@@ -673,7 +669,7 @@
mLooper.dispatchAll();
}
- private void runUsbTethering(NetworkState upstreamState) {
+ private void runUsbTethering(UpstreamNetworkState upstreamState) {
prepareUsbTethering(upstreamState);
sendUsbBroadcast(true, true, true);
mLooper.dispatchAll();
@@ -681,7 +677,7 @@
@Test
public void workingMobileUsbTethering_IPv4() throws Exception {
- NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -696,7 +692,7 @@
public void workingMobileUsbTethering_IPv4LegacyDhcp() {
Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
sendConfigurationChanged();
- final NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
sendIPv6TetherUpdates(upstreamState);
@@ -705,7 +701,7 @@
@Test
public void workingMobileUsbTethering_IPv6() throws Exception {
- NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -718,7 +714,7 @@
@Test
public void workingMobileUsbTethering_DualStack() throws Exception {
- NetworkState upstreamState = buildMobileDualStackUpstreamState();
+ UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -733,7 +729,7 @@
@Test
public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
- NetworkState upstreamState = buildMobile464xlatUpstreamState();
+ UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
@@ -751,7 +747,7 @@
@Test
public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
// Setup IPv6
- NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -789,7 +785,7 @@
sendConfigurationChanged();
// Setup IPv6
- final NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ final UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
runUsbTethering(upstreamState);
// UpstreamNetworkMonitor should choose upstream automatically
@@ -1172,7 +1168,7 @@
TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
assertEquals(tetherState, null);
// 2. Enable wifi tethering.
- NetworkState upstreamState = buildMobileDualStackUpstreamState();
+ UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c028d6d..c90abbb 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -50,7 +50,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.net.NetworkState;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Message;
@@ -539,7 +538,7 @@
mUNM.selectPreferredUpstreamType(preferredTypes));
verify(mEntitleMgr, times(1)).maybeRunProvisioning();
}
- private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
+ private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) {
if (legacyType == TYPE_NONE) {
assertTrue(ns == null);
return;
diff --git a/packages/VpnDialogs/res/values-am/strings.xml b/packages/VpnDialogs/res/values-am/strings.xml
index 103f101..ad9773b 100644
--- a/packages/VpnDialogs/res/values-am/strings.xml
+++ b/packages/VpnDialogs/res/values-am/strings.xml
@@ -30,7 +30,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"የቪፒኤን ቅንብሮችን ይቀይሩ"</string>
<string name="configure" msgid="4905518375574791375">"አዋቅር"</string>
- <string name="disconnect" msgid="971412338304200056">"አለያይ"</string>
+ <string name="disconnect" msgid="971412338304200056">"ግንኙነት አቋርጥ"</string>
<string name="open_app" msgid="3717639178595958667">"መተግበሪያን ክፈት"</string>
<string name="dismiss" msgid="6192859333764711227">"አሰናብት"</string>
</resources>
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index eecc101..8a3ac94 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -51,7 +51,8 @@
NavigationBarModeGesturalOverlay \
NavigationBarModeGesturalOverlayNarrowBack \
NavigationBarModeGesturalOverlayWideBack \
- NavigationBarModeGesturalOverlayExtraWideBack
+ NavigationBarModeGesturalOverlayExtraWideBack \
+ preinstalled-packages-platform-overlays.xml
include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
diff --git a/services/Android.bp b/services/Android.bp
index 501496d..ede4c3e 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -119,6 +119,12 @@
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
visibility: ["//visibility:private"],
+ check_api: {
+ current: {
+ api_file: "api/current.txt",
+ removed_api_file: "api/removed.txt",
+ },
+ },
}
java_library {
diff --git a/services/api/current.txt b/services/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/api/removed.txt b/services/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index 9db6254..36a4509 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -121,7 +121,12 @@
synchronized (mLock) {
if (mServiceConnection != null) {
if (sDebug) Slog.d(TAG, "reset(): unbinding service.");
- mContext.unbindService(mServiceConnection);
+ try {
+ mContext.unbindService(mServiceConnection);
+ } catch (IllegalArgumentException e) {
+ // no-op, just log the error message.
+ Slog.w(TAG, "reset(): " + e.getMessage());
+ }
mServiceConnection = null;
} else {
if (sDebug) Slog.d(TAG, "reset(): service is not bound. Do nothing.");
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 46ff718..c741fce 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1592,7 +1592,7 @@
TelephonyManager.DATA_UNKNOWN,
TelephonyManager.NETWORK_TYPE_UNKNOWN,
ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
- DataFailCause.NONE));
+ DataFailCause.NONE, null));
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -1609,7 +1609,6 @@
handleRemoveListLocked();
}
- broadcastDataConnectionFailed(apnType, subId);
}
@Override
@@ -1778,7 +1777,7 @@
TelephonyManager.DATA_UNKNOWN,
TelephonyManager.NETWORK_TYPE_UNKNOWN,
ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
- failCause));
+ failCause, null));
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -2260,13 +2259,6 @@
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- private void broadcastDataConnectionFailed(String apnType, int subId) {
- Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
- intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
- intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
if (checkNotifyPermission()) {
return;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index dab928a..154d16f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2960,7 +2960,9 @@
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service "
+ r.shortInstanceName, e);
+ needOomAdj = false;
serviceProcessGoneLocked(r);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 772f9b3..a1e1f29 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -282,20 +282,27 @@
"persist.device_config.runtime_native.use_app_image_startup_cache";
// Low Memory Killer Daemon command codes.
- // These must be kept in sync with the definitions in lmkd.c
+ // These must be kept in sync with lmk_cmd definitions in lmkd.h
//
// LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
// LMK_PROCPRIO <pid> <uid> <prio>
// LMK_PROCREMOVE <pid>
// LMK_PROCPURGE
// LMK_GETKILLCNT
+ // LMK_SUBSCRIBE
// LMK_PROCKILL
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
static final byte LMK_PROCPURGE = 3;
static final byte LMK_GETKILLCNT = 4;
- static final byte LMK_PROCKILL = 5; // Note: this is an unsolicated command
+ static final byte LMK_SUBSCRIBE = 5;
+ static final byte LMK_PROCKILL = 6; // Note: this is an unsolicated command
+
+ // Low Memory Killer Daemon command codes.
+ // These must be kept in sync with async_event_type definitions in lmkd.h
+ //
+ static final int LMK_ASYNC_EVENT_KILL = 0;
// lmkd reconnect delay in msecs
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
@@ -1354,6 +1361,11 @@
}
ostream.write(buf.array(), 0, buf.position());
}
+ // Subscribe for kill event notifications
+ buf = ByteBuffer.allocate(4 * 2);
+ buf.putInt(LMK_SUBSCRIBE);
+ buf.putInt(LMK_ASYNC_EVENT_KILL);
+ ostream.write(buf.array(), 0, buf.position());
} catch (IOException ex) {
return false;
}
@@ -2089,6 +2101,10 @@
*/
@GuardedBy("mService")
private boolean isProcessAliveLiteLocked(ProcessRecord app) {
+ // If somehow the pid is invalid, let's think it's dead.
+ if (app.pid <= 0) {
+ return false;
+ }
try {
Os.kill(app.pid, 0);
} catch (ErrnoException e) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 00fc6d0..75d9dd8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -77,6 +77,9 @@
// List of preferred devices for strategies
private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>();
+ // the wrapper for AudioSystem static methods, allows us to spy AudioSystem
+ private final @NonNull AudioSystemAdapter mAudioSystem;
+
private @NonNull AudioDeviceBroker mDeviceBroker;
// Monitoring of audio routes. Protected by mAudioRoutes.
@@ -86,12 +89,14 @@
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker;
+ mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
}
//-----------------------------------------------------------
- /** for mocking only */
- /*package*/ AudioDeviceInventory() {
+ /** for mocking only, allows to inject AudioSystem adapter */
+ /*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) {
mDeviceBroker = null;
+ mAudioSystem = audioSystem;
}
/*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) {
@@ -185,7 +190,7 @@
synchronized (mDevicesLock) {
//TODO iterate on mApmConnectedDevices instead once it handles all device types
for (DeviceInfo di : mConnectedDevices.values()) {
- AudioSystem.setDeviceConnectionState(
+ mAudioSystem.setDeviceConnectionState(
di.mDeviceType,
AudioSystem.DEVICE_STATE_AVAILABLE,
di.mDeviceAddress,
@@ -195,7 +200,7 @@
}
synchronized (mPreferredDevices) {
mPreferredDevices.forEach((strategy, device) -> {
- AudioSystem.setPreferredDeviceForStrategy(strategy, device); });
+ mAudioSystem.setPreferredDeviceForStrategy(strategy, device); });
}
}
@@ -355,7 +360,7 @@
mConnectedDevices.replace(key, di);
}
}
- final int res = AudioSystem.handleDeviceConfigChange(
+ final int res = mAudioSystem.handleDeviceConfigChange(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address,
BtHelper.getName(btDevice), a2dpCodec);
@@ -477,7 +482,7 @@
/*package*/ int setPreferredDeviceForStrategySync(int strategy,
@NonNull AudioDeviceAddress device) {
final long identity = Binder.clearCallingIdentity();
- final int status = AudioSystem.setPreferredDeviceForStrategy(strategy, device);
+ final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
@@ -488,7 +493,7 @@
/*package*/ int removePreferredDeviceForStrategySync(int strategy) {
final long identity = Binder.clearCallingIdentity();
- final int status = AudioSystem.removePreferredDeviceForStrategy(strategy);
+ final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
@@ -523,7 +528,7 @@
Slog.i(TAG, "deviceInfo:" + di + " is(already)Connected:" + isConnected);
}
if (connect && !isConnected) {
- final int res = AudioSystem.setDeviceConnectionState(device,
+ final int res = mAudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName,
AudioSystem.AUDIO_FORMAT_DEFAULT);
if (res != AudioSystem.AUDIO_STATUS_OK) {
@@ -536,7 +541,7 @@
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
return true;
} else if (!connect && isConnected) {
- AudioSystem.setDeviceConnectionState(device,
+ mAudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName,
AudioSystem.AUDIO_FORMAT_DEFAULT);
// always remove even if disconnection failed
@@ -713,7 +718,7 @@
mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource);
// at this point there could be another A2DP device already connected in APM, but it
// doesn't matter as this new one will overwrite the previous one
- final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
@@ -728,7 +733,7 @@
}
// Reset A2DP suspend state each time a new sink is connected
- AudioSystem.setParameters("A2dpSuspended=false");
+ mAudioSystem.setParameters("A2dpSuspended=false");
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
address, a2dpCodec);
@@ -761,7 +766,7 @@
// device to remove was visible by APM, update APM
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false);
- final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
@@ -783,7 +788,7 @@
private void makeA2dpDeviceUnavailableLater(String address, int delayMs) {
// prevent any activity on the A2DP audio output to avoid unwanted
// reconnection of the sink.
- AudioSystem.setParameters("A2dpSuspended=true");
+ mAudioSystem.setParameters("A2dpSuspended=true");
// retrieve DeviceInfo before removing device
final String deviceKey =
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
@@ -799,7 +804,7 @@
@GuardedBy("mDevicesLock")
private void makeA2dpSrcAvailable(String address) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
+ mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, "",
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
@@ -810,7 +815,7 @@
@GuardedBy("mDevicesLock")
private void makeA2dpSrcUnavailable(String address) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
+ mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
@@ -824,7 +829,7 @@
AudioSystem.DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
+ mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
@@ -839,7 +844,7 @@
@GuardedBy("mDevicesLock")
private void makeHearingAidDeviceUnavailable(String address) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
+ mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 21cecc2..114aac9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -415,8 +415,10 @@
public void onError(int error) {
switch (error) {
case AudioSystem.AUDIO_STATUS_SERVER_DIED:
- mRecordMonitor.onAudioServerDied();
-
+ // check for null in case error callback is called during instance creation
+ if (mRecordMonitor != null) {
+ mRecordMonitor.onAudioServerDied();
+ }
sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED,
SENDMSG_NOOP, 0, 0, null, 0);
sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
new file mode 100644
index 0000000..9d06b42
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.audio;
+
+import android.annotation.NonNull;
+import android.media.AudioDeviceAddress;
+import android.media.AudioSystem;
+import android.util.Log;
+
+/**
+ * Provides an adapter to access functionality of the android.media.AudioSystem class for device
+ * related functionality.
+ * Use the "real" AudioSystem through the default adapter.
+ * Use the "always ok" adapter to avoid dealing with the APM behaviors during a test.
+ */
+public class AudioSystemAdapter {
+
+ /**
+ * Create a wrapper around the {@link AudioSystem} static methods, all functions are directly
+ * forwarded to the AudioSystem class.
+ * @return an adapter around AudioSystem
+ */
+ static final @NonNull AudioSystemAdapter getDefaultAdapter() {
+ return new AudioSystemAdapter();
+ }
+
+ /**
+ * Create an adapter for AudioSystem that always succeeds, and does nothing.
+ * @return a no-op AudioSystem adapter
+ */
+ static final @NonNull AudioSystemAdapter getAlwaysOkAdapter() {
+ return new AudioSystemOkAdapter();
+ }
+
+ /**
+ * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)}
+ * @param device
+ * @param state
+ * @param deviceAddress
+ * @param deviceName
+ * @param codecFormat
+ * @return
+ */
+ public int setDeviceConnectionState(int device, int state, String deviceAddress,
+ String deviceName, int codecFormat) {
+ return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName,
+ codecFormat);
+ }
+
+ /**
+ * Same as {@link AudioSystem#getDeviceConnectionState(int, String)}
+ * @param device
+ * @param deviceAddress
+ * @return
+ */
+ public int getDeviceConnectionState(int device, String deviceAddress) {
+ return AudioSystem.getDeviceConnectionState(device, deviceAddress);
+ }
+
+ /**
+ * Same as {@link AudioSystem#handleDeviceConfigChange(int, String, String, int)}
+ * @param device
+ * @param deviceAddress
+ * @param deviceName
+ * @param codecFormat
+ * @return
+ */
+ public int handleDeviceConfigChange(int device, String deviceAddress,
+ String deviceName, int codecFormat) {
+ return AudioSystem.handleDeviceConfigChange(device, deviceAddress, deviceName,
+ codecFormat);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAddress)}
+ * @param strategy
+ * @param device
+ * @return
+ */
+ public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) {
+ return AudioSystem.setPreferredDeviceForStrategy(strategy, device);
+ }
+
+ /**
+ * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)}
+ * @param strategy
+ * @return
+ */
+ public int removePreferredDeviceForStrategy(int strategy) {
+ return AudioSystem.removePreferredDeviceForStrategy(strategy);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setParameters(String)}
+ * @param keyValuePairs
+ * @return
+ */
+ public int setParameters(String keyValuePairs) {
+ return AudioSystem.setParameters(keyValuePairs);
+ }
+
+ //--------------------------------------------------------------------
+ protected static class AudioSystemOkAdapter extends AudioSystemAdapter {
+ private static final String TAG = "ASA";
+
+ @Override
+ public int setDeviceConnectionState(int device, int state, String deviceAddress,
+ String deviceName, int codecFormat) {
+ Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %s, %s, 0x%s",
+ Integer.toHexString(device), state, deviceAddress, deviceName,
+ Integer.toHexString(codecFormat)));
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int getDeviceConnectionState(int device, String deviceAddress) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int handleDeviceConfigChange(int device, String deviceAddress,
+ String deviceName, int codecFormat) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int removePreferredDeviceForStrategy(int strategy) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int setParameters(String keyValuePairs) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 2321afb..5250a77 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -337,7 +337,6 @@
.collect(Collectors.toList()))
: useTls ? paramsParcel.servers // Opportunistic
: new String[0]; // Off
- paramsParcel.tlsFingerprints = new String[0];
// Prepare to track the validation status of the DNS servers in the
// resolver config when private DNS is in opportunistic or strict mode.
if (useTls) {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index d57ce53..e69a3b8b 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -152,6 +152,24 @@
public void setRequestedColorModeLocked(int colorMode) {
}
+ /**
+ * Sends the Auto Low Latency Mode (ALLM) signal over HDMI, or requests an internal display to
+ * switch to a low-latency mode.
+ *
+ * @param on Whether to set ALLM on or off.
+ */
+ public void setAutoLowLatencyModeLocked(boolean on) {
+ }
+
+ /**
+ * Sends a ContentType=Game signal over HDMI, or requests an internal display to switch to a
+ * game mode (generally lower latency).
+ *
+ * @param on Whether to send a ContentType=Game signal or not
+ */
+ public void setGameContentTypeLocked(boolean on) {
+ }
+
public void onOverlayChangedLocked() {
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 729ea17..ac41434 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -207,6 +207,16 @@
public Display.HdrCapabilities hdrCapabilities;
/**
+ * Indicates whether this display supports Auto Low Latency Mode.
+ */
+ public boolean allmSupported;
+
+ /**
+ * Indicates whether this display suppors Game content type.
+ */
+ public boolean gameContentTypeSupported;
+
+ /**
* The nominal apparent density of the display in DPI used for layout calculations.
* This density is sensitive to the viewing distance. A big TV and a tablet may have
* the same apparent density even though the pixels on the TV are much bigger than
@@ -337,6 +347,8 @@
|| !Arrays.equals(supportedModes, other.supportedModes)
|| !Arrays.equals(supportedColorModes, other.supportedColorModes)
|| !Objects.equals(hdrCapabilities, other.hdrCapabilities)
+ || allmSupported != other.allmSupported
+ || gameContentTypeSupported != other.gameContentTypeSupported
|| densityDpi != other.densityDpi
|| xDpi != other.xDpi
|| yDpi != other.yDpi
@@ -371,6 +383,8 @@
colorMode = other.colorMode;
supportedColorModes = other.supportedColorModes;
hdrCapabilities = other.hdrCapabilities;
+ allmSupported = other.allmSupported;
+ gameContentTypeSupported = other.gameContentTypeSupported;
densityDpi = other.densityDpi;
xDpi = other.xDpi;
yDpi = other.yDpi;
@@ -400,6 +414,8 @@
sb.append(", colorMode ").append(colorMode);
sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes));
sb.append(", HdrCapabilities ").append(hdrCapabilities);
+ sb.append(", allmSupported ").append(allmSupported);
+ sb.append(", gameContentTypeSupported ").append(gameContentTypeSupported);
sb.append(", density ").append(densityDpi);
sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ea03131..10386e7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1193,12 +1193,16 @@
}
private void setDisplayPropertiesInternal(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedModeId, boolean inTraversal) {
+ float requestedRefreshRate, int requestedModeId, boolean requestedMinimalPostProcessing,
+ boolean inTraversal) {
synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display == null) {
return;
}
+
+ boolean shouldScheduleTraversal = false;
+
if (display.hasContentLocked() != hasContent) {
if (DEBUG) {
Slog.d(TAG, "Display " + displayId + " hasContent flag changed: "
@@ -1206,7 +1210,7 @@
}
display.setHasContentLocked(hasContent);
- scheduleTraversalLocked(inTraversal);
+ shouldScheduleTraversal = true;
}
if (requestedModeId == 0 && requestedRefreshRate != 0) {
// Scan supported modes returned by display.getInfo() to find a mode with the same
@@ -1216,6 +1220,20 @@
}
mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
displayId, requestedModeId);
+
+
+ if (display.getDisplayInfoLocked().minimalPostProcessingSupported
+ && (display.getRequestedMinimalPostProcessingLocked()
+ != requestedMinimalPostProcessing)) {
+
+ display.setRequestedMinimalPostProcessingLocked(requestedMinimalPostProcessing);
+
+ shouldScheduleTraversal = true;
+ }
+
+ if (shouldScheduleTraversal) {
+ scheduleTraversalLocked(inTraversal);
+ }
}
}
@@ -2349,6 +2367,7 @@
}
private final class LocalService extends DisplayManagerInternal {
+
@Override
public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager) {
@@ -2437,9 +2456,10 @@
@Override
public void setDisplayProperties(int displayId, boolean hasContent,
- float requestedRefreshRate, int requestedMode, boolean inTraversal) {
+ float requestedRefreshRate, int requestedMode,
+ boolean requestedMinimalPostProcessing, boolean inTraversal) {
setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
- requestedMode, inTraversal);
+ requestedMode, requestedMinimalPostProcessing, inTraversal);
}
@Override
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index bf58efe..eebc738 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -180,6 +180,10 @@
private int mActiveColorMode;
private boolean mActiveColorModeInvalid;
private Display.HdrCapabilities mHdrCapabilities;
+ private boolean mAllmSupported;
+ private boolean mGameContentTypeSupported;
+ private boolean mAllmRequested;
+ private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
@@ -203,6 +207,8 @@
mBacklight = null;
}
mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
+ mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
+ mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
}
@Override
@@ -412,6 +418,8 @@
mInfo.defaultModeId = mDefaultModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
mInfo.colorMode = mActiveColorMode;
+ mInfo.allmSupported = mAllmSupported;
+ mInfo.gameContentTypeSupported = mGameContentTypeSupported;
mInfo.supportedColorModes =
new int[mSupportedColorModes.size()];
for (int i = 0; i < mSupportedColorModes.size(); i++) {
@@ -716,6 +724,40 @@
}
@Override
+ public void setAutoLowLatencyModeLocked(boolean on) {
+ if (mAllmRequested == on) {
+ return;
+ }
+
+ mAllmRequested = on;
+
+ if (!mAllmSupported) {
+ Slog.d(TAG, "Unable to set ALLM because the connected display "
+ + "does not support ALLM.");
+ return;
+ }
+
+ SurfaceControl.setAutoLowLatencyMode(getDisplayTokenLocked(), on);
+ }
+
+ @Override
+ public void setGameContentTypeLocked(boolean on) {
+ if (mGameContentTypeRequested == on) {
+ return;
+ }
+
+ mGameContentTypeRequested = on;
+
+ if (!mGameContentTypeSupported) {
+ Slog.d(TAG, "Unable to set game content type because the connected "
+ + "display does not support game content type.");
+ return;
+ }
+
+ SurfaceControl.setGameContentType(getDisplayTokenLocked(), on);
+ }
+
+ @Override
public void dumpLocked(PrintWriter pw) {
super.dumpLocked(pw);
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
@@ -728,6 +770,10 @@
pw.println("mState=" + Display.stateToString(mState));
pw.println("mBrightness=" + mBrightness);
pw.println("mBacklight=" + mBacklight);
+ pw.println("mAllmSupported=" + mAllmSupported);
+ pw.println("mAllmRequested=" + mAllmRequested);
+ pw.println("mGameContentTypeSupported" + mGameContentTypeSupported);
+ pw.println("mGameContentTypeRequested" + mGameContentTypeRequested);
pw.println("mDisplayInfos=");
for (int i = 0; i < mDisplayInfos.length; i++) {
pw.println(" " + mDisplayInfos[i]);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b649a50..0c9445a 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -88,6 +88,7 @@
private boolean mHasContent;
private int mRequestedColorMode;
+ private boolean mRequestedMinimalPostProcessing;
private DisplayModeDirector.DesiredDisplayModeSpecs mDesiredDisplayModeSpecs =
new DisplayModeDirector.DesiredDisplayModeSpecs();
@@ -284,6 +285,8 @@
deviceInfo.supportedColorModes,
deviceInfo.supportedColorModes.length);
mBaseDisplayInfo.hdrCapabilities = deviceInfo.hdrCapabilities;
+ mBaseDisplayInfo.minimalPostProcessingSupported =
+ deviceInfo.allmSupported || deviceInfo.gameContentTypeSupported;
mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
@@ -363,6 +366,9 @@
device.setRequestedColorModeLocked(0);
}
+ device.setAutoLowLatencyModeLocked(mRequestedMinimalPostProcessing);
+ device.setGameContentTypeLocked(mRequestedMinimalPostProcessing);
+
// Only grab the display info now as it may have been changed based on the requests above.
final DisplayInfo displayInfo = getDisplayInfoLocked();
final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
@@ -485,6 +491,23 @@
mRequestedColorMode = colorMode;
}
+ /**
+ * Returns the last requested minimal post processing setting.
+ */
+ public boolean getRequestedMinimalPostProcessingLocked() {
+ return mRequestedMinimalPostProcessing;
+ }
+
+ /**
+ * Instructs the connected display to do minimal post processing. This is implemented either
+ * via HDMI 2.1 ALLM or HDMI 1.4 ContentType=Game.
+ *
+ * @param on Whether to set minimal post processing on/off on the connected display.
+ */
+ public void setRequestedMinimalPostProcessingLocked(boolean on) {
+ mRequestedMinimalPostProcessing = on;
+ }
+
/** Returns the pending requested color mode. */
public int getRequestedColorModeLocked() {
return mRequestedColorMode;
@@ -542,5 +565,6 @@
mPrimaryDisplayDevice.getNameLocked() : "null"));
pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
+ pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
}
}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java
index 005fb69..3762ebb 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java
@@ -37,7 +37,7 @@
@Override
public void onStart() {
- mService = new AppIntegrityManagerServiceImpl(mContext);
- // TODO: define and publish a binder service.
+ mService = AppIntegrityManagerServiceImpl.create(mContext);
+ publishBinderService(Context.APP_INTEGRITY_SERVICE, mService);
}
}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 5c4479a..e5292a0 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -17,38 +17,107 @@
package com.android.server.integrity;
import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION;
+import static android.content.Intent.EXTRA_ORIGINATING_UID;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_VERSION_CODE;
+import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
+import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
+import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+import static com.android.server.integrity.IntegrityUtils.getHexDigest;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.IAppIntegrityManager;
+import android.content.integrity.Rule;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.Signature;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.integrity.engine.RuleEvaluationEngine;
+import com.android.server.integrity.model.IntegrityCheckResult;
+import com.android.server.integrity.model.RuleMetadata;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
/** Implementation of {@link AppIntegrityManagerService}. */
-class AppIntegrityManagerServiceImpl {
+public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private static final String TAG = "AppIntegrityManagerServiceImpl";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
+ private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
+ private static final String PACKAGE_INSTALLER = "com.google.android.packageinstaller";
+ private static final String BASE_APK_FILE = "base.apk";
+ private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
+ private static final String ALLOWED_INSTALLER_DELIMITER = ",";
+ private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
+ private static final String ADB_INSTALLER = "adb";
+ private static final String UNKNOWN_INSTALLER = "";
+ private static final String INSTALLER_CERT_NOT_APPLICABLE = "";
+
+ // Access to files inside mRulesDir is protected by mRulesLock;
private final Context mContext;
private final Handler mHandler;
private final PackageManagerInternal mPackageManagerInternal;
+ private final RuleEvaluationEngine mEvaluationEngine;
+ private final IntegrityFileManager mIntegrityFileManager;
- AppIntegrityManagerServiceImpl(Context context) {
- mContext = context;
-
+ /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
+ public static AppIntegrityManagerServiceImpl create(Context context) {
HandlerThread handlerThread = new HandlerThread("AppIntegrityManagerServiceHandler");
handlerThread.start();
- mHandler = handlerThread.getThreadHandler();
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ return new AppIntegrityManagerServiceImpl(
+ context,
+ LocalServices.getService(PackageManagerInternal.class),
+ RuleEvaluationEngine.getRuleEvaluationEngine(),
+ IntegrityFileManager.getInstance(),
+ handlerThread.getThreadHandler());
+ }
+
+ @VisibleForTesting
+ AppIntegrityManagerServiceImpl(
+ Context context,
+ PackageManagerInternal packageManagerInternal,
+ RuleEvaluationEngine evaluationEngine,
+ IntegrityFileManager integrityFileManager,
+ Handler handler) {
+ mContext = context;
+ mPackageManagerInternal = packageManagerInternal;
+ mEvaluationEngine = evaluationEngine;
+ mIntegrityFileManager = integrityFileManager;
+ mHandler = handler;
IntentFilter integrityVerificationFilter = new IntentFilter();
integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
@@ -74,14 +143,409 @@
mHandler);
}
- // protected broadcasts cannot be sent in the test.
- @VisibleForTesting
- void handleIntegrityVerification(Intent intent) {
+ @Override
+ public void updateRuleSet(
+ String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver)
+ throws RemoteException {
+ String ruleProvider = getCallerPackageNameOrThrow();
+
+ mHandler.post(
+ () -> {
+ boolean success = true;
+ try {
+ mIntegrityFileManager.writeRules(version, ruleProvider, rules.getList());
+ } catch (Exception e) {
+ Slog.e(TAG, "Error writing rules.", e);
+ success = false;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE);
+ try {
+ statusReceiver.sendIntent(
+ mContext,
+ /* code= */ 0,
+ intent,
+ /* onFinished= */ null,
+ /* handler= */ null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.e(TAG, "Error sending status feedback.", e);
+ }
+ });
+ }
+
+ @Override
+ public String getCurrentRuleSetVersion() throws RemoteException {
+ getCallerPackageNameOrThrow();
+
+ RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata();
+ return (ruleMetadata != null && ruleMetadata.getVersion() != null)
+ ? ruleMetadata.getVersion()
+ : "";
+ }
+
+ @Override
+ public String getCurrentRuleSetProvider() throws RemoteException {
+ getCallerPackageNameOrThrow();
+
+ RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata();
+ return (ruleMetadata != null && ruleMetadata.getRuleProvider() != null)
+ ? ruleMetadata.getRuleProvider()
+ : "";
+ }
+
+ private void handleIntegrityVerification(Intent intent) {
int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
- // TODO: implement this method.
- Slog.i(TAG, "Received integrity verification intent " + intent.toString());
- Slog.i(TAG, "Extras " + intent.getExtras());
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ try {
+ Slog.i(TAG, "Received integrity verification intent " + intent.toString());
+ Slog.i(TAG, "Extras " + intent.getExtras());
+
+ String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+
+ PackageInfo packageInfo = getPackageArchiveInfo(intent.getData());
+ if (packageInfo == null) {
+ Slog.w(TAG, "Cannot parse package " + packageName);
+ // We can't parse the package.
+ mPackageManagerInternal.setIntegrityVerificationResult(
+ verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ return;
+ }
+
+ String installerPackageName = getInstallerPackageName(intent);
+ String appCert = getCertificateFingerprint(packageInfo);
+
+ AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
+
+ builder.setPackageName(getPackageNameNormalized(packageName));
+ builder.setAppCertificate(appCert == null ? "" : appCert);
+ builder.setVersionCode(intent.getIntExtra(EXTRA_VERSION_CODE, -1));
+ builder.setInstallerName(getPackageNameNormalized(installerPackageName));
+ builder.setInstallerCertificate(
+ getInstallerCertificateFingerprint(installerPackageName));
+ builder.setIsPreInstalled(isSystemApp(packageName));
+
+ AppInstallMetadata appInstallMetadata = builder.build();
+
+ Slog.i(TAG, "To be verified: " + appInstallMetadata);
+ IntegrityCheckResult result =
+ mEvaluationEngine.evaluate(
+ appInstallMetadata, getAllowedInstallers(packageInfo));
+ Slog.i(
+ TAG,
+ "Integrity check result: "
+ + result.getEffect()
+ + " due to "
+ + result.getRule());
+ mPackageManagerInternal.setIntegrityVerificationResult(
+ verificationId,
+ result.getEffect() == IntegrityCheckResult.Effect.ALLOW
+ ? PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW
+ : PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
+ } catch (IllegalArgumentException e) {
+ // This exception indicates something is wrong with the input passed by package manager.
+ // e.g., someone trying to trick the system. We block installs in this case.
+ Slog.e(TAG, "Invalid input to integrity verification", e);
+ mPackageManagerInternal.setIntegrityVerificationResult(
+ verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
+ } catch (Exception e) {
+ // Other exceptions indicate an error within the integrity component implementation and
+ // we allow them.
+ Slog.e(TAG, "Error handling integrity verification", e);
+ mPackageManagerInternal.setIntegrityVerificationResult(
+ verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ }
+ }
+
+ /**
+ * Verify the UID and return the installer package name.
+ *
+ * @return the package name of the installer, or null if it cannot be determined or it is
+ * installed via adb.
+ */
+ @Nullable
+ private String getInstallerPackageName(Intent intent) {
+ String installer =
+ intent.getStringExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE);
+ if (installer == null) {
+ return ADB_INSTALLER;
+ }
+ int installerUid = intent.getIntExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, -1);
+ if (installerUid < 0) {
+ Slog.e(
+ TAG,
+ "Installer cannot be determined: installer: "
+ + installer
+ + " installer UID: "
+ + installerUid);
+ return UNKNOWN_INSTALLER;
+ }
+
+ try {
+ int actualInstallerUid =
+ mContext.getPackageManager().getPackageUid(installer, /* flags= */ 0);
+ if (actualInstallerUid != installerUid) {
+ // Installer package name can be faked but the installerUid cannot.
+ Slog.e(
+ TAG,
+ "Installer "
+ + installer
+ + " has UID "
+ + actualInstallerUid
+ + " which doesn't match alleged installer UID "
+ + installerUid);
+ return UNKNOWN_INSTALLER;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Installer package " + installer + " not found.");
+ return UNKNOWN_INSTALLER;
+ }
+
+ // At this time we can trust "installer".
+
+ // A common way for apps to install packages is to send an intent to PackageInstaller. In
+ // that case, the installer will always show up as PackageInstaller which is not what we
+ // want.
+ if (installer.equals(PACKAGE_INSTALLER)) {
+ int originatingUid = intent.getIntExtra(EXTRA_ORIGINATING_UID, -1);
+ if (originatingUid < 0) {
+ Slog.e(TAG, "Installer is package installer but originating UID not found.");
+ return UNKNOWN_INSTALLER;
+ }
+ String[] installerPackages =
+ mContext.getPackageManager().getPackagesForUid(originatingUid);
+ if (installerPackages == null || installerPackages.length == 0) {
+ Slog.e(TAG, "No package found associated with originating UID " + originatingUid);
+ return UNKNOWN_INSTALLER;
+ }
+ // In the case of multiple package sharing a UID, we just return the first one.
+ return installerPackages[0];
+ }
+
+ return installer;
+ }
+
+ /** We will use the SHA256 digest of a package name if it is more than 32 bytes long. */
+ private String getPackageNameNormalized(String packageName) {
+ if (packageName.length() <= 32) {
+ return packageName;
+ }
+
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ byte[] hashBytes = messageDigest.digest(packageName.getBytes(StandardCharsets.UTF_8));
+ return getHexDigest(hashBytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("SHA-256 algorithm not found", e);
+ }
+ }
+
+ private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) {
+ return getFingerprint(getSignature(packageInfo));
+ }
+
+ private String getInstallerCertificateFingerprint(String installer) {
+ if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) {
+ return INSTALLER_CERT_NOT_APPLICABLE;
+ }
+ try {
+ PackageInfo installerInfo =
+ mContext.getPackageManager()
+ .getPackageInfo(installer, PackageManager.GET_SIGNATURES);
+ return getCertificateFingerprint(installerInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.i(TAG, "Installer package " + installer + " not found.");
+ return "";
+ }
+ }
+
+ /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */
+ private Map<String, String> getAllowedInstallers(@NonNull PackageInfo packageInfo) {
+ Map<String, String> packageCertMap = new HashMap<>();
+ if (packageInfo.applicationInfo != null && packageInfo.applicationInfo.metaData != null) {
+ Bundle metaData = packageInfo.applicationInfo.metaData;
+ String allowedInstallers = metaData.getString(ALLOWED_INSTALLERS_METADATA_NAME);
+ if (allowedInstallers != null) {
+ // parse the metadata for certs.
+ String[] installerCertPairs = allowedInstallers.split(ALLOWED_INSTALLER_DELIMITER);
+ for (String packageCertPair : installerCertPairs) {
+ String[] packageAndCert =
+ packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER);
+ if (packageAndCert.length == 2) {
+ String packageName = packageAndCert[0];
+ String cert = packageAndCert[1];
+ packageCertMap.put(packageName, cert);
+ }
+ }
+ }
+ }
+
+ Slog.i("DEBUG", "allowed installers map " + packageCertMap);
+ return packageCertMap;
+ }
+
+ private boolean getPreInstalled(String packageName) {
+ try {
+ PackageInfo existingPackageInfo =
+ mContext.getPackageManager().getPackageInfo(packageName, 0);
+ return existingPackageInfo.applicationInfo != null
+ && existingPackageInfo.applicationInfo.isSystemApp();
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private static Signature getSignature(@NonNull PackageInfo packageInfo) {
+ if (packageInfo.signatures == null || packageInfo.signatures.length < 1) {
+ throw new IllegalArgumentException("Package signature not found in " + packageInfo);
+ }
+ // Only the first element is guaranteed to be present.
+ return packageInfo.signatures[0];
+ }
+
+ private static String getFingerprint(Signature cert) {
+ InputStream input = new ByteArrayInputStream(cert.toByteArray());
+
+ CertificateFactory factory;
+ try {
+ factory = CertificateFactory.getInstance("X509");
+ } catch (CertificateException e) {
+ throw new RuntimeException("Error getting CertificateFactory", e);
+ }
+ X509Certificate certificate = null;
+ try {
+ if (factory != null) {
+ certificate = (X509Certificate) factory.generateCertificate(input);
+ }
+ } catch (CertificateException e) {
+ throw new RuntimeException("Error getting X509Certificate", e);
+ }
+
+ if (certificate == null) {
+ throw new RuntimeException("X509 Certificate not found");
+ }
+
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] publicKey = digest.digest(certificate.getEncoded());
+ return getHexDigest(publicKey);
+ } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
+ throw new IllegalArgumentException("Error error computing fingerprint", e);
+ }
+ }
+
+ private PackageInfo getPackageArchiveInfo(Uri dataUri) {
+ File installationPath = getInstallationPath(dataUri);
+ if (installationPath == null) {
+ throw new IllegalArgumentException("Installation path is null, package not found");
+ }
+ PackageInfo packageInfo;
+ try {
+ // The installation path will be a directory for a multi-apk install on L+
+ if (installationPath.isDirectory()) {
+ packageInfo = getMultiApkInfo(installationPath);
+ } else {
+ packageInfo =
+ mContext.getPackageManager()
+ .getPackageArchiveInfo(
+ installationPath.getPath(),
+ PackageManager.GET_SIGNATURES
+ | PackageManager.GET_META_DATA);
+ }
+ return packageInfo;
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Exception reading " + dataUri, e);
+ }
+ }
+
+ private PackageInfo getMultiApkInfo(File multiApkDirectory) {
+ // The base apk will normally be called base.apk
+ File baseFile = new File(multiApkDirectory, BASE_APK_FILE);
+ PackageInfo basePackageInfo =
+ mContext.getPackageManager()
+ .getPackageArchiveInfo(
+ baseFile.getAbsolutePath(), PackageManager.GET_SIGNATURES);
+
+ if (basePackageInfo == null) {
+ for (File apkFile : multiApkDirectory.listFiles()) {
+ if (apkFile.isDirectory()) {
+ continue;
+ }
+
+ // If we didn't find a base.apk, then try to parse each apk until we find the one
+ // that succeeds.
+ basePackageInfo =
+ mContext.getPackageManager()
+ .getPackageArchiveInfo(
+ apkFile.getAbsolutePath(),
+ PackageManager.GET_SIGNING_CERTIFICATES);
+ if (basePackageInfo != null) {
+ Slog.i(TAG, "Found package info from " + apkFile);
+ break;
+ }
+ }
+ }
+
+ if (basePackageInfo == null) {
+ throw new IllegalArgumentException(
+ "Base package info cannot be found from installation directory");
+ }
+
+ return basePackageInfo;
+ }
+
+ private File getInstallationPath(Uri dataUri) {
+ if (dataUri == null) {
+ throw new IllegalArgumentException("Null data uri");
+ }
+
+ String scheme = dataUri.getScheme();
+ if (!"file".equalsIgnoreCase(scheme)) {
+ throw new IllegalArgumentException("Unsupported scheme for " + dataUri);
+ }
+
+ File installationPath = new File(dataUri.getPath());
+ if (!installationPath.exists()) {
+ throw new IllegalArgumentException("Cannot find file for " + dataUri);
+ }
+ if (!installationPath.canRead()) {
+ throw new IllegalArgumentException("Cannot read file for " + dataUri);
+ }
+ return installationPath;
+ }
+
+ private String getCallerPackageNameOrThrow() {
+ final String[] allowedRuleProviders =
+ mContext.getResources()
+ .getStringArray(R.array.config_integrityRuleProviderPackages);
+ for (String packageName : allowedRuleProviders) {
+ try {
+ // At least in tests, getPackageUid gives "NameNotFound" but getPackagesFromUid
+ // give the correct package name.
+ int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ if (uid == Binder.getCallingUid()) {
+ // Caller is allowed in the config.
+ if (isSystemApp(packageName)) {
+ return packageName;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore the exception. We don't expect the app to be necessarily installed.
+ Slog.i(TAG, "Rule provider package " + packageName + " not installed.");
+ }
+ }
+ throw new SecurityException(
+ "Only system packages specified in config_integrityRuleProviderPackages are"
+ + " allowed to call this method.");
+ }
+
+ private boolean isSystemApp(String packageName) {
+ try {
+ PackageInfo existingPackageInfo =
+ mContext.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
+ return existingPackageInfo.applicationInfo != null
+ && existingPackageInfo.applicationInfo.isSystemApp();
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
new file mode 100644
index 0000000..bdf0279
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.integrity;
+
+import android.annotation.Nullable;
+import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.Rule;
+import android.os.Environment;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.integrity.model.RuleMetadata;
+import com.android.server.integrity.parser.RuleBinaryParser;
+import com.android.server.integrity.parser.RuleMetadataParser;
+import com.android.server.integrity.parser.RuleParseException;
+import com.android.server.integrity.parser.RuleParser;
+import com.android.server.integrity.serializer.RuleBinarySerializer;
+import com.android.server.integrity.serializer.RuleMetadataSerializer;
+import com.android.server.integrity.serializer.RuleSerializeException;
+import com.android.server.integrity.serializer.RuleSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+/** Abstraction over the underlying storage of rules and other metadata. */
+public class IntegrityFileManager {
+ private static final String TAG = "IntegrityFileManager";
+
+ // TODO: this is a prototype implementation of this class. Thus no tests are included.
+ // Implementing rule indexing will likely overhaul this class and more tests should be included
+ // then.
+
+ private static final String METADATA_FILE = "metadata";
+ private static final String RULES_FILE = "rules";
+ private static final Object RULES_LOCK = new Object();
+
+ private static IntegrityFileManager sInstance = null;
+
+ private final RuleParser mRuleParser;
+ private final RuleSerializer mRuleSerializer;
+
+ // mRulesDir contains data of the actual rules currently stored.
+ private final File mRulesDir;
+ // mStagingDir is used to store the temporary rules / metadata during updating, since we want to
+ // update rules atomically.
+ private final File mStagingDir;
+
+ @Nullable private RuleMetadata mRuleMetadataCache;
+
+ /** Get the singleton instance of this class. */
+ public static synchronized IntegrityFileManager getInstance() {
+ if (sInstance == null) {
+ sInstance = new IntegrityFileManager();
+ }
+ return sInstance;
+ }
+
+ private IntegrityFileManager() {
+ this(
+ new RuleBinaryParser(),
+ new RuleBinarySerializer(),
+ Environment.getDataSystemDirectory());
+ }
+
+ @VisibleForTesting
+ IntegrityFileManager(RuleParser ruleParser, RuleSerializer ruleSerializer, File dataDir) {
+ mRuleParser = ruleParser;
+ mRuleSerializer = ruleSerializer;
+
+ mRulesDir = new File(dataDir, "integrity_rules");
+ mStagingDir = new File(dataDir, "integrity_staging");
+
+ if (!mStagingDir.mkdirs() && mRulesDir.mkdirs()) {
+ Slog.e(TAG, "Error creating staging and rules directory");
+ // TODO: maybe throw an exception?
+ }
+
+ File metadataFile = new File(mRulesDir, METADATA_FILE);
+ if (metadataFile.exists()) {
+ try (FileInputStream inputStream = new FileInputStream(metadataFile)) {
+ mRuleMetadataCache = RuleMetadataParser.parse(inputStream);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading metadata file.", e);
+ }
+ }
+ }
+
+ /** Write rules to persistent storage. */
+ public void writeRules(String version, String ruleProvider, List<Rule> rules)
+ throws IOException, RuleSerializeException {
+ try {
+ writeMetadata(mStagingDir, ruleProvider, version);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error writing metadata.", e);
+ // We don't consider this fatal so we continue execution.
+ }
+
+ try (FileOutputStream fileOutputStream =
+ new FileOutputStream(new File(mStagingDir, RULES_FILE))) {
+ mRuleSerializer.serialize(rules, Optional.empty(), fileOutputStream);
+ }
+
+ switchStagingRulesDir();
+ }
+
+ /**
+ * Read rules from persistent storage.
+ *
+ * @param appInstallMetadata information about the install used to select rules to read
+ */
+ public List<Rule> readRules(AppInstallMetadata appInstallMetadata)
+ throws IOException, RuleParseException {
+ // TODO: select rules by index
+ synchronized (RULES_LOCK) {
+ try (FileInputStream inputStream =
+ new FileInputStream(new File(mRulesDir, RULES_FILE))) {
+ List<Rule> rules = mRuleParser.parse(inputStream);
+ return rules;
+ }
+ }
+ }
+
+ /** Read the metadata of the current rules in storage. */
+ @Nullable
+ public RuleMetadata readMetadata() {
+ return mRuleMetadataCache;
+ }
+
+ private void switchStagingRulesDir() throws IOException {
+ synchronized (RULES_LOCK) {
+ File tmpDir = new File(Environment.getDataSystemDirectory(), "temp");
+
+ if (!(mRulesDir.renameTo(tmpDir)
+ && mStagingDir.renameTo(mRulesDir)
+ && tmpDir.renameTo(mStagingDir))) {
+ throw new IOException("Error switching staging/rules directory");
+ }
+ }
+ }
+
+ private void writeMetadata(File directory, String ruleProvider, String version)
+ throws IOException {
+ mRuleMetadataCache = new RuleMetadata(ruleProvider, version);
+
+ File metadataFile = new File(directory, METADATA_FILE);
+
+ try (FileOutputStream outputStream = new FileOutputStream(metadataFile)) {
+ RuleMetadataSerializer.serialize(mRuleMetadataCache, outputStream);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/IntegrityUtils.java b/services/core/java/com/android/server/integrity/IntegrityUtils.java
new file mode 100644
index 0000000..f49c675
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/IntegrityUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.integrity;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+/** Utils class for simple operations used in integrity module. */
+public class IntegrityUtils {
+
+ private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
+
+ /**
+ * Obtain the raw bytes from hex encoded string.
+ *
+ * @throws IllegalArgumentException if {@code hexDigest} is not a valid hex encoding of some
+ * bytes
+ */
+ public static byte[] getBytesFromHexDigest(String hexDigest) {
+ checkArgument(
+ hexDigest.length() % 2 == 0,
+ "Invalid hex encoding " + hexDigest + ": must have even length");
+
+ byte[] rawBytes = new byte[hexDigest.length() / 2];
+ for (int i = 0; i < rawBytes.length; i++) {
+ int upperNibble = hexDigest.charAt(2 * i);
+ int lowerNibble = hexDigest.charAt(2 * i + 1);
+ rawBytes[i] = (byte) ((hexToDec(upperNibble) << 4) | hexToDec(lowerNibble));
+ }
+ return rawBytes;
+ }
+
+ /** Obtain hex encoded string from raw bytes. */
+ public static String getHexDigest(byte[] rawBytes) {
+ char[] hexChars = new char[rawBytes.length * 2];
+
+ for (int i = 0; i < rawBytes.length; i++) {
+ int upperNibble = (rawBytes[i] >>> 4) & 0xF;
+ int lowerNibble = rawBytes[i] & 0xF;
+ hexChars[i * 2] = decToHex(upperNibble);
+ hexChars[i * 2 + 1] = decToHex(lowerNibble);
+ }
+ return new String(hexChars);
+ }
+
+ private static int hexToDec(int hexChar) {
+ if (hexChar >= '0' && hexChar <= '9') {
+ return hexChar - '0';
+ }
+ if (hexChar >= 'a' && hexChar <= 'f') {
+ return hexChar - 'a' + 10;
+ }
+ if (hexChar >= 'A' && hexChar <= 'F') {
+ return hexChar - 'A' + 10;
+ }
+ throw new IllegalArgumentException("Invalid hex char " + hexChar);
+ }
+
+ private static char decToHex(int dec) {
+ if (dec >= 0 && dec < HEX_CHARS.length) {
+ return HEX_CHARS[dec];
+ }
+
+ throw new IllegalArgumentException("Invalid dec value to be converted to hex digit " + dec);
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index b8202b6..0ea6efc 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -18,11 +18,14 @@
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.Rule;
+import android.util.Slog;
+import com.android.server.integrity.IntegrityFileManager;
import com.android.server.integrity.model.IntegrityCheckResult;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* The engine used to evaluate rules against app installs.
@@ -30,17 +33,23 @@
* <p>Every app install is evaluated against rules (pushed by the verifier) by the evaluation engine
* to allow/block that install.
*/
-public final class RuleEvaluationEngine {
+public class RuleEvaluationEngine {
private static final String TAG = "RuleEvaluation";
// The engine for loading rules, retrieving metadata for app installs, and evaluating app
// installs against rules.
private static RuleEvaluationEngine sRuleEvaluationEngine;
+ private final IntegrityFileManager mIntegrityFileManager;
+
+ private RuleEvaluationEngine(IntegrityFileManager integrityFileManager) {
+ mIntegrityFileManager = integrityFileManager;
+ }
+
/** Provide a singleton instance of the rule evaluation engine. */
public static synchronized RuleEvaluationEngine getRuleEvaluationEngine() {
if (sRuleEvaluationEngine == null) {
- return new RuleEvaluationEngine();
+ return new RuleEvaluationEngine(IntegrityFileManager.getInstance());
}
return sRuleEvaluationEngine;
}
@@ -52,13 +61,18 @@
* against.
* @return result of the integrity check
*/
- public IntegrityCheckResult evaluate(AppInstallMetadata appInstallMetadata) {
+ public IntegrityCheckResult evaluate(
+ AppInstallMetadata appInstallMetadata, Map<String, String> allowedInstallers) {
List<Rule> rules = loadRules(appInstallMetadata);
return RuleEvaluator.evaluateRules(rules, appInstallMetadata);
}
private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
- // TODO: Load rules
- return new ArrayList<>();
+ try {
+ return mIntegrityFileManager.readRules(appInstallMetadata);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error loading rules.", e);
+ return new ArrayList<>();
+ }
}
}
diff --git a/services/core/java/com/android/server/integrity/model/RuleMetadata.java b/services/core/java/com/android/server/integrity/model/RuleMetadata.java
new file mode 100644
index 0000000..6b582ae
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/RuleMetadata.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.integrity.model;
+
+import android.annotation.Nullable;
+
+/** Data class containing relevant metadata associated with a rule set. */
+public class RuleMetadata {
+
+ private final String mRuleProvider;
+ private final String mVersion;
+
+ public RuleMetadata(String ruleProvider, String version) {
+ mRuleProvider = ruleProvider;
+ mVersion = version;
+ }
+
+ @Nullable
+ public String getRuleProvider() {
+ return mRuleProvider;
+ }
+
+ @Nullable
+ public String getVersion() {
+ return mVersion;
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
new file mode 100644
index 0000000..28d2e69
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.integrity.parser;
+
+import android.annotation.Nullable;
+import android.util.Xml;
+
+import com.android.server.integrity.model.RuleMetadata;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/** Helper class for parsing rule metadata. */
+public class RuleMetadataParser {
+
+ public static final String RULE_PROVIDER_TAG = "P";
+ public static final String VERSION_TAG = "V";
+
+ /** Parse the rule metadata from an input stream. */
+ @Nullable
+ public static RuleMetadata parse(InputStream inputStream)
+ throws XmlPullParserException, IOException {
+
+ String ruleProvider = "";
+ String version = "";
+
+ XmlPullParser xmlPullParser = Xml.newPullParser();
+ xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ int eventType;
+ while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ String tag = xmlPullParser.getName();
+ switch (tag) {
+ case RULE_PROVIDER_TAG:
+ ruleProvider = xmlPullParser.nextText();
+ break;
+ case VERSION_TAG:
+ version = xmlPullParser.nextText();
+ break;
+ default:
+ throw new IllegalStateException("Unknown tag in metadata: " + tag);
+ }
+ }
+ }
+
+ return new RuleMetadata(ruleProvider, version);
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
new file mode 100644
index 0000000..5c51f31
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.integrity.serializer;
+
+import static com.android.server.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG;
+import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG;
+
+import android.util.Xml;
+
+import com.android.server.integrity.model.RuleMetadata;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+
+/** Helper class for writing rule metadata. */
+public class RuleMetadataSerializer {
+ /** Serialize the rule metadata to an output stream. */
+ public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream)
+ throws IOException {
+ XmlSerializer xmlSerializer = Xml.newSerializer();
+ xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
+
+ serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider());
+ serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion());
+
+ xmlSerializer.endDocument();
+ }
+
+ private static void serializeTaggedValue(XmlSerializer xmlSerializer, String tag, String value)
+ throws IOException {
+ xmlSerializer.startTag(/* namespace= */ null, tag);
+ xmlSerializer.text(value);
+ xmlSerializer.endTag(/* namespace= */ null, tag);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 51fcbb0..bcc4c1f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -27,8 +27,6 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
-import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT;
-import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -2582,23 +2580,12 @@
return type == PersistentData.TYPE_SP || type == PersistentData.TYPE_SP_WEAVER;
}
long handle = getSyntheticPasswordHandleLocked(userId);
- // This is a global setting
- long enabled = getLong(SYNTHETIC_PASSWORD_ENABLED_KEY,
- SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT, UserHandle.USER_SYSTEM);
- return enabled != 0 && handle != SyntheticPasswordManager.DEFAULT_HANDLE;
+ return handle != SyntheticPasswordManager.DEFAULT_HANDLE;
}
@VisibleForTesting
protected boolean shouldMigrateToSyntheticPasswordLocked(int userId) {
- long handle = getSyntheticPasswordHandleLocked(userId);
- // This is a global setting
- long enabled = getLong(SYNTHETIC_PASSWORD_ENABLED_KEY,
- SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT, UserHandle.USER_SYSTEM);
- return enabled != 0 && handle == SyntheticPasswordManager.DEFAULT_HANDLE;
- }
-
- private void enableSyntheticPasswordLocked() {
- setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
+ return true;
}
private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
@@ -2937,7 +2924,6 @@
private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback) {
if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId);
synchronized (mSpManager) {
- enableSyntheticPasswordLocked();
// Migrate to synthetic password based credentials if the user has no password,
// the token can then be activated immediately.
AuthenticationToken auth = null;
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index eb706d7..1d39177 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -240,8 +240,7 @@
if (!mPrevActiveAudioPlaybackConfigs.containsKey(
config.getPlayerInterfaceId())) {
if (DEBUG) {
- Log.d(TAG, "Found a new active media playback. "
- + AudioPlaybackConfiguration.toLogFriendlyString(config));
+ Log.d(TAG, "Found a new active media playback. " + config);
}
// New active audio playback.
int index = mSortedAudioPlaybackClientUids.indexOf(uid);
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 9a49c16..0483431 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.RouteSessionInfo;
import android.os.Bundle;
import java.util.Objects;
@@ -42,6 +43,8 @@
mCallback = callback;
}
+ public abstract void requestCreateSession(String packageName, String routeId,
+ String controlCategory, int requestId);
public abstract void requestSelectRoute(String packageName, String routeId, int seq);
public abstract void unselectRoute(String packageName, String routeId);
public abstract void sendControlRequest(MediaRoute2Info route, Intent request);
@@ -82,5 +85,7 @@
void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
@NonNull String clientPackageName, @NonNull MediaRoute2Info route,
@Nullable Bundle controlHints, int seq);
+ void onSessionCreated(@NonNull MediaRoute2Provider provider,
+ @Nullable RouteSessionInfo sessionInfo, int requestId);
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index a5abb18..893747b 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -17,6 +17,7 @@
package com.android.server.media;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +27,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.RouteSessionInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -74,6 +76,16 @@
}
@Override
+ public void requestCreateSession(String packageName, String routeId, String controlCategory,
+ int requestId) {
+ if (mConnectionReady) {
+ mActiveConnection.requestCreateSession(packageName, routeId, controlCategory,
+ requestId);
+ updateBinding();
+ }
+ }
+
+ @Override
public void requestSelectRoute(String packageName, String routeId, int seq) {
if (mConnectionReady) {
mActiveConnection.requestSelectRoute(packageName, routeId, seq);
@@ -268,6 +280,14 @@
mCallback.onRouteSelected(this, packageName, route, controlHints, seq);
}
+ private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo,
+ int requestId) {
+ if (mActiveConnection != connection) {
+ return;
+ }
+ mCallback.onSessionCreated(this, sessionInfo, requestId);
+ }
+
private void disconnect() {
if (mActiveConnection != null) {
mConnectionReady = false;
@@ -308,6 +328,15 @@
mClient.dispose();
}
+ public void requestCreateSession(String packageName, String routeId, String controlCategory,
+ int requestId) {
+ try {
+ mProvider.requestCreateSession(packageName, routeId, controlCategory, requestId);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to create a session.", ex);
+ }
+ }
+
public void requestSelectRoute(String packageName, String routeId, int seq) {
try {
mProvider.requestSelectRoute(packageName, routeId, seq);
@@ -361,6 +390,11 @@
mHandler.post(() -> onRouteSelected(Connection.this,
packageName, routeId, controlHints, seq));
}
+
+ void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+ mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo,
+ requestId));
+ }
}
private static final class ProviderClient extends IMediaRoute2ProviderClient.Stub {
@@ -391,5 +425,12 @@
}
}
+ @Override
+ public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+ Connection connection = mConnectionRef.get();
+ if (connection != null) {
+ connection.postSessionCreated(sessionInfo, requestId);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index e7b8860..a711863 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -29,6 +29,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRouter2;
+import android.media.RouteSessionInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -55,6 +56,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* TODO: Merge this to MediaRouterService once it's finished.
@@ -166,6 +168,24 @@
}
}
+ public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
+ String controlCategory, int requestId) {
+ Objects.requireNonNull(client, "client must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+ if (TextUtils.isEmpty(controlCategory)) {
+ throw new IllegalArgumentException("controlCategory must not be empty");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestCreateSessionLocked(client, route, controlCategory, requestId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void sendControlRequest(@NonNull IMediaRouter2Client client,
@NonNull MediaRoute2Info route, @NonNull Intent request) {
Objects.requireNonNull(client, "client must not be null");
@@ -198,18 +218,6 @@
}
}
- public void requestSelectRoute2(@NonNull IMediaRouter2Client client,
- @Nullable MediaRoute2Info route) {
- final long token = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- requestSelectRoute2Locked(mAllClientRecords.get(client.asBinder()), false, route);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
Objects.requireNonNull(client, "client must not be null");
Objects.requireNonNull(route, "route must not be null");
@@ -353,6 +361,19 @@
}
}
+ private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
+ @NonNull MediaRoute2Info route, @NonNull String controlCategory, int requestId) {
+ final IBinder binder = client.asBinder();
+ final Client2Record clientRecord = mAllClientRecords.get(binder);
+
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestCreateSessionOnHandler,
+ clientRecord.mUserRecord.mHandler,
+ clientRecord, route, controlCategory, requestId));
+ }
+ }
+
private void requestSelectRoute2Locked(Client2Record clientRecord, boolean selectedByManager,
MediaRoute2Info route) {
if (clientRecord != null) {
@@ -569,6 +590,7 @@
mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this);
}
+ // TODO: This assumes that only one client exists in a package. Is it true?
Client2Record findClientRecordLocked(String packageName) {
for (Client2Record clientRecord : mClientRecords) {
if (TextUtils.equals(clientRecord.mPackageName, packageName)) {
@@ -667,10 +689,12 @@
private final SystemMediaRoute2Provider mSystemProvider;
private final ArrayList<MediaRoute2Provider> mMediaProviders =
new ArrayList<>();
- private final List<MediaRoute2ProviderInfo> mProviderInfos = new ArrayList<>();
+
+ private final List<MediaRoute2ProviderInfo> mLastProviderInfos = new ArrayList<>();
+ private final CopyOnWriteArrayList<SessionCreationRequest> mSessionCreationRequests =
+ new CopyOnWriteArrayList<>();
private boolean mRunning;
- private boolean mProviderInfosUpdateScheduled;
UserHandler(MediaRouter2ServiceImpl service, UserRecord userRecord) {
super(Looper.getMainLooper(), null, true);
@@ -721,23 +745,30 @@
controlHints, seq));
}
+ @Override
+ public void onSessionCreated(@NonNull MediaRoute2Provider provider,
+ @Nullable RouteSessionInfo sessionInfo, int requestId) {
+ sendMessage(PooledLambda.obtainMessage(UserHandler::handleCreateSessionResultOnHandler,
+ this, provider, sessionInfo, requestId));
+ }
+
private void updateProvider(MediaRoute2Provider provider) {
int providerIndex = getProviderInfoIndex(provider.getUniqueId());
MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
MediaRoute2ProviderInfo prevInfo =
- (providerIndex < 0) ? null : mProviderInfos.get(providerIndex);
+ (providerIndex < 0) ? null : mLastProviderInfos.get(providerIndex);
if (Objects.equals(prevInfo, providerInfo)) return;
if (prevInfo == null) {
- mProviderInfos.add(providerInfo);
+ mLastProviderInfos.add(providerInfo);
Collection<MediaRoute2Info> addedRoutes = providerInfo.getRoutes();
if (addedRoutes.size() > 0) {
sendMessage(PooledLambda.obtainMessage(UserHandler::notifyRoutesAddedToClients,
this, getClients(), new ArrayList<>(addedRoutes)));
}
} else if (providerInfo == null) {
- mProviderInfos.remove(prevInfo);
+ mLastProviderInfos.remove(prevInfo);
Collection<MediaRoute2Info> removedRoutes = prevInfo.getRoutes();
if (removedRoutes.size() > 0) {
sendMessage(PooledLambda.obtainMessage(
@@ -745,7 +776,7 @@
this, getClients(), new ArrayList<>(removedRoutes)));
}
} else {
- mProviderInfos.set(providerIndex, providerInfo);
+ mLastProviderInfos.set(providerIndex, providerInfo);
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
@@ -794,8 +825,8 @@
}
private int getProviderInfoIndex(String providerId) {
- for (int i = 0; i < mProviderInfos.size(); i++) {
- MediaRoute2ProviderInfo providerInfo = mProviderInfos.get(i);
+ for (int i = 0; i < mLastProviderInfos.size(); i++) {
+ MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i);
if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) {
return i;
}
@@ -803,6 +834,94 @@
return -1;
}
+ private void requestCreateSessionOnHandler(Client2Record clientRecord,
+ MediaRoute2Info route, String controlCategory, int requestId) {
+
+ final MediaRoute2Provider provider = findProvider(route.getProviderId());
+ if (provider == null) {
+ Slog.w(TAG, "Ignoring session creation request since no provider found for"
+ + " given route=" + route);
+ notifySessionCreationFailed(clientRecord, requestId);
+ return;
+ }
+
+ if (!route.getSupportedCategories().contains(controlCategory)) {
+ Slog.w(TAG, "Ignoring session creation request since the given route=" + route
+ + " doesn't support the given category=" + controlCategory);
+ notifySessionCreationFailed(clientRecord, requestId);
+ return;
+ }
+
+ // TODO: Apply timeout for each request (How many seconds should we wait?)
+ SessionCreationRequest request = new SessionCreationRequest(
+ clientRecord, route, controlCategory, requestId);
+ mSessionCreationRequests.add(request);
+
+ provider.requestCreateSession(clientRecord.mPackageName, route.getId(),
+ controlCategory, requestId);
+ }
+
+ private void handleCreateSessionResultOnHandler(
+ @NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo,
+ int requestId) {
+ SessionCreationRequest matchingRequest = null;
+ for (SessionCreationRequest request : mSessionCreationRequests) {
+ if (request.mRequestId == requestId
+ && TextUtils.equals(
+ request.mRoute.getProviderId(), provider.getUniqueId())) {
+ matchingRequest = request;
+ break;
+ }
+ }
+
+ if (matchingRequest == null) {
+ Slog.w(TAG, "Ignoring session creation result for unknown request. "
+ + "requestId=" + requestId + ", sessionInfo=" + sessionInfo);
+ return;
+ }
+
+ if (sessionInfo == null) {
+ // Failed
+ notifySessionCreationFailed(matchingRequest.mClientRecord, requestId);
+ return;
+ }
+
+ String originalRouteId = matchingRequest.mRoute.getId();
+ String originalCategory = matchingRequest.mControlCategory;
+ if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
+ || !TextUtils.equals(originalCategory, sessionInfo.getControlCategory())) {
+ Slog.w(TAG, "Created session doesn't match the original request."
+ + " originalRouteId=" + originalRouteId
+ + ", originalCategory=" + originalCategory
+ + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo);
+ notifySessionCreationFailed(matchingRequest.mClientRecord, requestId);
+ return;
+ }
+
+ // Succeeded
+ notifySessionCreated(matchingRequest.mClientRecord, sessionInfo, requestId);
+ // TODO: Tell managers for the session creation
+ }
+
+ private void notifySessionCreated(Client2Record clientRecord, RouteSessionInfo sessionInfo,
+ int requestId) {
+ try {
+ clientRecord.mClient.notifySessionCreated(sessionInfo, requestId);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify client of the session creation."
+ + " Client probably died.", ex);
+ }
+ }
+
+ private void notifySessionCreationFailed(Client2Record clientRecord, int requestId) {
+ try {
+ clientRecord.mClient.notifySessionCreated(/* sessionInfo= */ null, requestId);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify client of the session creation failure."
+ + " Client probably died.", ex);
+ }
+ }
+
private void updateSelectedRoute(MediaRoute2ProviderProxy provider,
String clientPackageName, MediaRoute2Info selectedRoute, Bundle controlHints,
int seq) {
@@ -858,6 +977,12 @@
clientRecord = mUserRecord.findClientRecordLocked(clientPackageName);
}
+ if (clientRecord == null) {
+ Log.w(TAG, "The client has gone. packageName=" + clientPackageName
+ + " selectingRoute=" + selectingRoute);
+ return;
+ }
+
if (clientRecord.mSelectingRoute == null || !TextUtils.equals(
clientRecord.mSelectingRoute.getUniqueId(), selectingRoute.getUniqueId())) {
Log.w(TAG, "Ignoring invalid selectFallbackRoute call. "
@@ -951,7 +1076,7 @@
private void notifyRoutesToClient(IMediaRouter2Client client) {
List<MediaRoute2Info> routes = new ArrayList<>();
- for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) {
+ for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
routes.addAll(providerInfo.getRoutes());
}
if (routes.size() == 0) {
@@ -964,13 +1089,9 @@
}
}
+ // TODO: Remove notifyRouteSelected* methods
private void notifyRouteSelectedToClient(IMediaRouter2Client client,
MediaRoute2Info route, int reason, Bundle controlHints) {
- try {
- client.notifyRouteSelected(route, reason, controlHints);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify routes selected. Client probably died.", ex);
- }
}
private void notifyRoutesAddedToClients(List<IMediaRouter2Client> clients,
@@ -1008,7 +1129,7 @@
private void notifyRoutesToManager(IMediaRouter2Manager manager) {
List<MediaRoute2Info> routes = new ArrayList<>();
- for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) {
+ for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
routes.addAll(providerInfo.getRoutes());
}
if (routes.size() == 0) {
@@ -1085,5 +1206,21 @@
}
return null;
}
+
+ final class SessionCreationRequest {
+ public final Client2Record mClientRecord;
+ public final MediaRoute2Info mRoute;
+ public final String mControlCategory;
+ public final int mRequestId;
+
+ SessionCreationRequest(@NonNull Client2Record clientRecord,
+ @NonNull MediaRoute2Info route,
+ @NonNull String controlCategory, int requestId) {
+ mClientRecord = clientRecord;
+ mRoute = route;
+ mControlCategory = controlCategory;
+ mRequestId = requestId;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 9c99e8f..a280f91 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -457,8 +457,9 @@
// Binder call
@Override
- public void requestSelectRoute2(IMediaRouter2Client client, MediaRoute2Info route) {
- mService2.requestSelectRoute2(client, route);
+ public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
+ String controlCategory, int requestId) {
+ mService2.requestCreateSession(client, route, controlCategory, requestId);
}
// Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 4f64177..6c4c8d5 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -88,6 +88,12 @@
initializeRoutes();
}
+ @Override
+ public void requestCreateSession(String packageName, String routeId, String controlCategory,
+ int requestId) {
+ // Do nothing
+ }
+
//TODO: implement method
@Override
public void requestSelectRoute(@NonNull String packageName, @NonNull String routeId, int seq) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 83d0ecd..2ebca88 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1828,7 +1828,7 @@
}
String[] mergedSubscriberId = ArrayUtils.defeatNullable(
- tm.createForSubscriptionId(subId).getMergedSubscriberIdsFromGroup());
+ tm.createForSubscriptionId(subId).getMergedImsisFromGroup());
mergedSubscriberIdsList.add(mergedSubscriberId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 47a41f5..e2dfa12 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -217,6 +217,7 @@
public void systemReady() {
mAppOps = mContext.getSystemService(AppOpsManager.class);
+ mStagingManager.systemReady();
synchronized (mSessions) {
readSessionsLocked();
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 688c34f..6c3eb31 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -22,10 +22,12 @@
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
import android.apex.ApexSessionParams;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
@@ -774,6 +776,17 @@
}
}
+ void systemReady() {
+ // Register the receiver of boot completed intent for staging manager.
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context ctx, Intent intent) {
+ mPreRebootVerificationHandler.readyToStart();
+ ctx.unregisterReceiver(this);
+ }
+ }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+ }
+
private static class LocalIntentReceiverAsync {
final Consumer<Intent> mConsumer;
@@ -824,6 +837,9 @@
}
private final class PreRebootVerificationHandler extends Handler {
+ // Hold session ids before handler gets ready to do the verification.
+ private IntArray mPendingSessionIds;
+ private boolean mIsReady;
PreRebootVerificationHandler(Looper looper) {
super(looper);
@@ -876,8 +892,26 @@
}
}
+ // Notify the handler that system is ready, and reschedule the pre-reboot verifications.
+ private synchronized void readyToStart() {
+ mIsReady = true;
+ if (mPendingSessionIds != null) {
+ for (int i = 0; i < mPendingSessionIds.size(); i++) {
+ startPreRebootVerification(mPendingSessionIds.get(i));
+ }
+ mPendingSessionIds = null;
+ }
+ }
+
// Method for starting the pre-reboot verification
- private void startPreRebootVerification(int sessionId) {
+ private synchronized void startPreRebootVerification(int sessionId) {
+ if (!mIsReady) {
+ if (mPendingSessionIds == null) {
+ mPendingSessionIds = new IntArray();
+ }
+ mPendingSessionIds.add(sessionId);
+ return;
+ }
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
}
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 603b01a..6d6ec25 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -726,10 +726,12 @@
userId, STORAGE_PERMISSIONS);
// TextClassifier Service
- String textClassifierPackageName =
- mContext.getPackageManager().getSystemTextClassifierPackageName();
- if (!TextUtils.isEmpty(textClassifierPackageName)) {
- grantPermissionsToSystemPackage(textClassifierPackageName, userId,
+ final String[] packages = mContext.getPackageManager().getSystemTextClassifierPackages();
+ if (packages.length > 0) {
+ // We have a list of supported system TextClassifier package names, the first one
+ // package is the default system TextClassifier service. Grant permissions to default
+ // TextClassifier Service.
+ grantPermissionsToSystemPackage(packages[0], userId,
COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
@@ -998,7 +1000,7 @@
private void revokeRuntimePermissions(String packageName, Set<String> permissions,
boolean systemFixed, int userId) {
PackageInfo pkg = getSystemPackageInfo(packageName);
- if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
+ if (pkg == null || ArrayUtils.isEmpty(pkg.requestedPermissions)) {
return;
}
Set<String> revokablePermissions = new ArraySet<>(Arrays.asList(pkg.requestedPermissions));
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
new file mode 100644
index 0000000..acf3f79
--- /dev/null
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -0,0 +1,45 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBatterySavingTestCases",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.LargeTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.power"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.power"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {
+ "exclude-filter": "com.android.server.power.PowerManagerServiceTest#testWakefulnessAwake_ShouldWakeUpWhenPluggedIn"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsBatterySavingTestCases"
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.power"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.power"}
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
new file mode 100644
index 0000000..0bbb179
--- /dev/null
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.security;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.security.Credentials;
+import android.security.IFileIntegrityService;
+import android.security.KeyStore;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * A {@link SystemService} that provides file integrity related operations.
+ * @hide
+ */
+public class FileIntegrityService extends SystemService {
+ private static final String TAG = "FileIntegrityService";
+
+ private static CertificateFactory sCertFactory;
+
+ private Collection<X509Certificate> mTrustedCertificates = new ArrayList<X509Certificate>();
+
+ private final IBinder mService = new IFileIntegrityService.Stub() {
+ @Override
+ public boolean isApkVeritySupported() {
+ return SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
+ }
+
+ @Override
+ public boolean isAppSourceCertificateTrusted(byte[] certificateBytes) {
+ enforceAnyCallingPermissions(
+ android.Manifest.permission.REQUEST_INSTALL_PACKAGES,
+ android.Manifest.permission.INSTALL_PACKAGES);
+ try {
+ if (!isApkVeritySupported()) {
+ return false;
+ }
+
+ return mTrustedCertificates.contains(toCertificate(certificateBytes));
+ } catch (CertificateException e) {
+ Slog.e(TAG, "Failed to convert the certificate: " + e);
+ return false;
+ }
+ }
+
+ private void enforceAnyCallingPermissions(String ...permissions) {
+ for (String permission : permissions) {
+ if (getContext().checkCallingPermission(permission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+ throw new SecurityException("Insufficient permission");
+ }
+ };
+
+ public FileIntegrityService(final Context context) {
+ super(context);
+ try {
+ sCertFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ Slog.wtf(TAG, "Cannot get an instance of X.509 certificate factory");
+ }
+ }
+
+ @Override
+ public void onStart() {
+ loadAllCertificates();
+ publishBinderService(Context.FILE_INTEGRITY_SERVICE, mService);
+ }
+
+ private void loadAllCertificates() {
+ // A better alternative to load certificates would be to read from .fs-verity kernel
+ // keyring, which fsverity_init loads to during earlier boot time from the same sources
+ // below. But since the read operation from keyring is not provided in kernel, we need to
+ // duplicate the same loading logic here.
+
+ // Load certificates trusted by the device manufacturer.
+ loadCertificatesFromDirectory("/product/etc/security/fsverity");
+
+ // Load certificates trusted by the device owner.
+ loadCertificatesFromKeystore(KeyStore.getInstance());
+ }
+
+ private void loadCertificatesFromDirectory(String path) {
+ try {
+ File[] files = new File(path).listFiles();
+ if (files == null) {
+ return;
+ }
+
+ for (File cert : files) {
+ collectCertificate(Files.readAllBytes(cert.toPath()));
+ }
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to load fs-verity certificate from " + path, e);
+ }
+ }
+
+ private void loadCertificatesFromKeystore(KeyStore keystore) {
+ for (final String alias : keystore.list(Credentials.APP_SOURCE_CERTIFICATE,
+ Process.FSVERITY_CERT_UID)) {
+ byte[] certificateBytes = keystore.get(Credentials.APP_SOURCE_CERTIFICATE + alias,
+ Process.FSVERITY_CERT_UID, false /* suppressKeyNotFoundWarning */);
+ if (certificateBytes == null) {
+ Slog.w(TAG, "The retrieved fs-verity certificate is null, ignored " + alias);
+ continue;
+ }
+ collectCertificate(certificateBytes);
+ }
+ }
+
+ /**
+ * Tries to convert {@code bytes} into an X.509 certificate and store in memory.
+ * Errors need to be surpressed in order fo the next certificates to still be collected.
+ */
+ private void collectCertificate(@Nullable byte[] bytes) {
+ try {
+ mTrustedCertificates.add(toCertificate(bytes));
+ } catch (CertificateException | AssertionError e) {
+ Slog.e(TAG, "Invalid certificate, ignored: " + e);
+ }
+ }
+
+ /**
+ * Converts byte array into one X.509 certificate. If multiple certificate is defined, ignore
+ * the rest. The rational is to make it harder to smuggle.
+ */
+ @NonNull
+ private static X509Certificate toCertificate(@Nullable byte[] bytes)
+ throws CertificateException {
+ Certificate certificate = sCertFactory.generateCertificate(new ByteArrayInputStream(bytes));
+ if (!(certificate instanceof X509Certificate)) {
+ throw new CertificateException("Expected to contain an X.509 certificate");
+ }
+ return (X509Certificate) certificate;
+ }
+}
diff --git a/services/core/java/com/android/server/utils/quota/Categorizer.java b/services/core/java/com/android/server/utils/quota/Categorizer.java
index bf24991..0a45dab 100644
--- a/services/core/java/com/android/server/utils/quota/Categorizer.java
+++ b/services/core/java/com/android/server/utils/quota/Categorizer.java
@@ -25,6 +25,9 @@
* @see Uptc
*/
public interface Categorizer {
+ /** A {@link Categorizer} that always returns {@link Category.SINGLE_CATEGORY}. */
+ Categorizer SINGLE_CATEGORIZER = (userId, packageName, tag) -> Category.SINGLE_CATEGORY;
+
/**
* Return the {@link Category} that this UPTC belongs to.
*
diff --git a/services/core/java/com/android/server/utils/quota/Category.java b/services/core/java/com/android/server/utils/quota/Category.java
index d8459d7..933c149 100644
--- a/services/core/java/com/android/server/utils/quota/Category.java
+++ b/services/core/java/com/android/server/utils/quota/Category.java
@@ -28,6 +28,12 @@
* @see Uptc
*/
public final class Category {
+ /**
+ * A {@link Category} that can be used if every app should be treated the same (given the same
+ * category).
+ */
+ public static final Category SINGLE_CATEGORY = new Category("SINGLE");
+
@NonNull
private final String mName;
diff --git a/services/core/java/com/android/server/utils/quota/QuotaTracker.java b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
new file mode 100644
index 0000000..ef1f426
--- /dev/null
+++ b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.utils.quota;
+
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.utils.quota.Uptc.string;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.proto.ProtoOutputStream;
+import android.util.quota.QuotaTrackerProto;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemServiceManager;
+
+import java.util.PriorityQueue;
+
+/**
+ * Base class for trackers that track whether an app has exceeded a count quota.
+ *
+ * Quotas are applied per userId-package-tag combination (UPTC). Tags can be null.
+ *
+ * Count and duration limits can be applied at the same time. Each limit is evaluated and
+ * controlled independently. If a UPTC reaches one of the limits, it will be considered out
+ * of quota until it is below that limit again. Limits are applied according to the category
+ * the UPTC is placed in. Categories are basic constructs to apply different limits to
+ * different groups of UPTCs. For example, standby buckets can be a set of categories, or
+ * foreground & background could be two categories. If every UPTC should have the limits
+ * applied, then only one category is needed.
+ *
+ * Note: all limits are enforced per category unless explicitly stated otherwise.
+ *
+ * @hide
+ */
+abstract class QuotaTracker {
+ private static final String TAG = QuotaTracker.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final String ALARM_TAG_QUOTA_CHECK = "*" + TAG + ".quota_check*";
+
+ @VisibleForTesting
+ static class Injector {
+ long getElapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ boolean isAlarmManagerReady() {
+ return LocalServices.getService(SystemServiceManager.class).isBootCompleted();
+ }
+ }
+
+ final Object mLock = new Object();
+ final Categorizer mCategorizer;
+ @GuardedBy("mLock")
+ private final ArraySet<QuotaChangeListener> mQuotaChangeListeners = new ArraySet<>();
+
+ /**
+ * Listener to track and manage when each package comes back within quota.
+ */
+ @GuardedBy("mLock")
+ private final InQuotaAlarmListener mInQuotaAlarmListener = new InQuotaAlarmListener();
+
+ /** "Free quota status" for apps. */
+ @GuardedBy("mLock")
+ private final SparseArrayMap<Boolean> mFreeQuota = new SparseArrayMap<>();
+
+ private final AlarmManager mAlarmManager;
+ protected final Context mContext;
+ protected final Injector mInjector;
+
+ @GuardedBy("mLock")
+ private boolean mIsQuotaFree;
+
+ /**
+ * If QuotaTracker should actively track events and check quota. If false, quota will be free
+ * and events will not be tracked.
+ */
+ @GuardedBy("mLock")
+ private boolean mIsEnabled = true;
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private String getPackageName(Intent intent) {
+ final Uri uri = intent.getData();
+ return uri != null ? uri.getSchemeSpecificPart() : null;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null
+ || intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ return;
+ }
+ final String action = intent.getAction();
+ if (action == null) {
+ Slog.e(TAG, "Received intent with null action");
+ return;
+ }
+ switch (action) {
+ case Intent.ACTION_PACKAGE_FULLY_REMOVED:
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ synchronized (mLock) {
+ onAppRemovedLocked(getPackageName(intent), uid);
+ }
+ break;
+ case Intent.ACTION_USER_REMOVED:
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ synchronized (mLock) {
+ onUserRemovedLocked(userId);
+ }
+ break;
+ }
+ }
+ };
+
+ /** The maximum period any Category can have. */
+ @VisibleForTesting
+ static final long MAX_WINDOW_SIZE_MS = 30 * 24 * 60 * MINUTE_IN_MILLIS; // 1 month
+
+ /** The minimum time any window size can be. */
+ @VisibleForTesting
+ static final long MIN_WINDOW_SIZE_MS = 30_000; // 30 seconds
+
+ QuotaTracker(@NonNull Context context, @NonNull Categorizer categorizer,
+ @NonNull Injector injector) {
+ mCategorizer = categorizer;
+ mContext = context;
+ mInjector = injector;
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ filter.addDataScheme("package");
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null,
+ BackgroundThread.getHandler());
+ final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null,
+ BackgroundThread.getHandler());
+ }
+
+ // Exposed API to users.
+
+ /**
+ * @return true if the UPTC is within quota, false otherwise.
+ * @throws IllegalStateException if given categorizer returns a Category that's not recognized.
+ */
+ public boolean isWithinQuota(int userId, @NonNull String packageName, @Nullable String tag) {
+ synchronized (mLock) {
+ return isWithinQuotaLocked(userId, packageName, tag);
+ }
+ }
+
+ /**
+ * Indicates whether quota is currently free or not for a specific app. If quota is free, any
+ * currently ongoing events or instantaneous events won't be counted until quota is no longer
+ * free.
+ */
+ public void setQuotaFree(int userId, @NonNull String packageName, boolean isFree) {
+ synchronized (mLock) {
+ final boolean wasFree = mFreeQuota.getOrDefault(userId, packageName, Boolean.FALSE);
+ if (wasFree != isFree) {
+ mFreeQuota.add(userId, packageName, isFree);
+ onQuotaFreeChangedLocked(userId, packageName, isFree);
+ }
+ }
+ }
+
+ /** Indicates whether quota is currently free or not for all apps. */
+ public void setQuotaFree(boolean isFree) {
+ synchronized (mLock) {
+ if (mIsQuotaFree == isFree) {
+ return;
+ }
+ mIsQuotaFree = isFree;
+
+ if (!mIsEnabled) {
+ return;
+ }
+ onQuotaFreeChangedLocked(mIsQuotaFree);
+ }
+ scheduleQuotaCheck();
+ }
+
+ /**
+ * Register a {@link QuotaChangeListener} to be notified of when apps go in and out of quota.
+ */
+ public void registerQuotaChangeListener(QuotaChangeListener listener) {
+ synchronized (mLock) {
+ if (mQuotaChangeListeners.add(listener) && mQuotaChangeListeners.size() == 1) {
+ scheduleQuotaCheck();
+ }
+ }
+ }
+
+ /** Unregister the listener from future quota change notifications. */
+ public void unregisterQuotaChangeListener(QuotaChangeListener listener) {
+ synchronized (mLock) {
+ mQuotaChangeListeners.remove(listener);
+ }
+ }
+
+ // Configuration APIs
+
+ /**
+ * Completely enables or disables the quota tracker. If the tracker is disabled, all events and
+ * internal tracking data will be dropped.
+ */
+ public void setEnabled(boolean enable) {
+ synchronized (mLock) {
+ if (mIsEnabled == enable) {
+ return;
+ }
+ mIsEnabled = enable;
+
+ if (!mIsEnabled) {
+ mInQuotaAlarmListener.clearLocked();
+ mFreeQuota.clear();
+
+ dropEverythingLocked();
+ }
+ }
+ }
+
+ // Internal implementation.
+
+ @GuardedBy("mLock")
+ boolean isEnabledLocked() {
+ return mIsEnabled;
+ }
+
+ /** Returns true if global quota is free. */
+ @GuardedBy("mLock")
+ boolean isQuotaFreeLocked() {
+ return mIsQuotaFree;
+ }
+
+ /** Returns true if global quota is free or if quota is free for the given userId-package. */
+ @GuardedBy("mLock")
+ boolean isQuotaFreeLocked(int userId, @NonNull String packageName) {
+ return mIsQuotaFree || mFreeQuota.getOrDefault(userId, packageName, Boolean.FALSE);
+ }
+
+ /**
+ * Returns true only if quota is free for the given userId-package. Global quota is not taken
+ * into account.
+ */
+ @GuardedBy("mLock")
+ boolean isIndividualQuotaFreeLocked(int userId, @NonNull String packageName) {
+ return mFreeQuota.getOrDefault(userId, packageName, Boolean.FALSE);
+ }
+
+ /** The tracker has been disabled. Drop all events and internal tracking data. */
+ @GuardedBy("mLock")
+ abstract void dropEverythingLocked();
+
+ /** The global free quota status changed. */
+ @GuardedBy("mLock")
+ abstract void onQuotaFreeChangedLocked(boolean isFree);
+
+ /** The individual free quota status for the userId-package changed. */
+ @GuardedBy("mLock")
+ abstract void onQuotaFreeChangedLocked(int userId, @NonNull String packageName, boolean isFree);
+
+ /** Get the Handler used by the tracker. This Handler's thread will receive alarm callbacks. */
+ @NonNull
+ abstract Handler getHandler();
+
+ /** Makes sure to call out to AlarmManager on a separate thread. */
+ void scheduleAlarm(@AlarmManager.AlarmType int type, long triggerAtMillis, String tag,
+ AlarmManager.OnAlarmListener listener) {
+ // We don't know at what level in the lock hierarchy this tracker will be, so make sure to
+ // call out to AlarmManager without the lock held. The operation should be fast enough so
+ // put it on the FgThread.
+ FgThread.getHandler().post(() -> {
+ if (mInjector.isAlarmManagerReady()) {
+ mAlarmManager.set(type, triggerAtMillis, tag, listener, getHandler());
+ } else {
+ Slog.w(TAG, "Alarm not scheduled because boot isn't completed");
+ }
+ });
+ }
+
+ /** Makes sure to call out to AlarmManager on a separate thread. */
+ void cancelAlarm(AlarmManager.OnAlarmListener listener) {
+ // We don't know at what level in the lock hierarchy this tracker will be, so make sure to
+ // call out to AlarmManager without the lock held. The operation should be fast enough so
+ // put it on the FgThread.
+ FgThread.getHandler().post(() -> {
+ if (mInjector.isAlarmManagerReady()) {
+ mAlarmManager.cancel(listener);
+ } else {
+ Slog.w(TAG, "Alarm not cancelled because boot isn't completed");
+ }
+ });
+ }
+
+ /** Check the quota status of the specific UPTC. */
+ abstract void maybeUpdateQuotaStatus(int userId, @NonNull String packageName,
+ @Nullable String tag);
+
+ /** Check the quota status of all UPTCs in case a listener needs to be notified. */
+ @GuardedBy("mLock")
+ abstract void maybeUpdateAllQuotaStatusLocked();
+
+ /** Schedule a quota check for all apps. */
+ void scheduleQuotaCheck() {
+ // Using BackgroundThread because of the risk of lock contention.
+ BackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ if (mQuotaChangeListeners.size() > 0) {
+ maybeUpdateAllQuotaStatusLocked();
+ }
+ }
+ });
+ }
+
+ @GuardedBy("mLock")
+ abstract void handleRemovedAppLocked(String packageName, int uid);
+
+ @GuardedBy("mLock")
+ private void onAppRemovedLocked(String packageName, int uid) {
+ if (packageName == null) {
+ Slog.wtf(TAG, "Told app removed but given null package name.");
+ return;
+ }
+ final int userId = UserHandle.getUserId(uid);
+
+ mInQuotaAlarmListener.removeAlarmsLocked(userId, packageName);
+
+ mFreeQuota.delete(userId, packageName);
+
+ handleRemovedAppLocked(packageName, uid);
+ }
+
+ @GuardedBy("mLock")
+ abstract void handleRemovedUserLocked(int userId);
+
+ @GuardedBy("mLock")
+ private void onUserRemovedLocked(int userId) {
+ mInQuotaAlarmListener.removeAlarmsLocked(userId);
+ mFreeQuota.delete(userId);
+
+ handleRemovedUserLocked(userId);
+ }
+
+ @GuardedBy("mLock")
+ abstract boolean isWithinQuotaLocked(int userId, @NonNull String packageName,
+ @Nullable String tag);
+
+ void postQuotaStatusChanged(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ BackgroundThread.getHandler().post(() -> {
+ final QuotaChangeListener[] listeners;
+ synchronized (mLock) {
+ // Only notify all listeners if we aren't directing to one listener.
+ listeners = mQuotaChangeListeners.toArray(
+ new QuotaChangeListener[mQuotaChangeListeners.size()]);
+ }
+ for (QuotaChangeListener listener : listeners) {
+ listener.onQuotaStateChanged(userId, packageName, tag);
+ }
+ });
+ }
+
+ /**
+ * Return the time (in the elapsed realtime timebase) when the UPTC will have quota again. This
+ * value is only valid if the UPTC is currently out of quota.
+ */
+ @GuardedBy("mLock")
+ abstract long getInQuotaTimeElapsedLocked(int userId, @NonNull String packageName,
+ @Nullable String tag);
+
+ /**
+ * Maybe schedule a non-wakeup alarm for the next time this package will have quota to run
+ * again. This should only be called if the package is already out of quota.
+ */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ void maybeScheduleStartAlarmLocked(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ if (mQuotaChangeListeners.size() == 0) {
+ // No need to schedule the alarm since we won't do anything when the app gets quota
+ // again.
+ return;
+ }
+
+ final String pkgString = string(userId, packageName, tag);
+
+ if (isWithinQuota(userId, packageName, tag)) {
+ // Already in quota. Why was this method called?
+ if (DEBUG) {
+ Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+ + " even though it's within quota");
+ }
+ mInQuotaAlarmListener.removeAlarmLocked(new Uptc(userId, packageName, tag));
+ maybeUpdateQuotaStatus(userId, packageName, tag);
+ return;
+ }
+
+ mInQuotaAlarmListener.addAlarmLocked(new Uptc(userId, packageName, tag),
+ getInQuotaTimeElapsedLocked(userId, packageName, tag));
+ }
+
+ @GuardedBy("mLock")
+ void cancelScheduledStartAlarmLocked(final int userId,
+ @NonNull final String packageName, @Nullable final String tag) {
+ mInQuotaAlarmListener.removeAlarmLocked(new Uptc(userId, packageName, tag));
+ }
+
+ static class AlarmQueue extends PriorityQueue<Pair<Uptc, Long>> {
+ AlarmQueue() {
+ super(1, (o1, o2) -> (int) (o1.second - o2.second));
+ }
+
+ /**
+ * Remove any instances of the Uptc from the queue.
+ *
+ * @return true if an instance was removed, false otherwise.
+ */
+ boolean remove(@NonNull Uptc uptc) {
+ boolean removed = false;
+ Pair[] alarms = toArray(new Pair[size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ if (uptc.equals(alarms[i].first)) {
+ remove(alarms[i]);
+ removed = true;
+ }
+ }
+ return removed;
+ }
+ }
+
+ /** Track when UPTCs are expected to come back into quota. */
+ private class InQuotaAlarmListener implements AlarmManager.OnAlarmListener {
+ @GuardedBy("mLock")
+ private final AlarmQueue mAlarmQueue = new AlarmQueue();
+ /** The next time the alarm is set to go off, in the elapsed realtime timebase. */
+ @GuardedBy("mLock")
+ private long mTriggerTimeElapsed = 0;
+
+ @GuardedBy("mLock")
+ void addAlarmLocked(@NonNull Uptc uptc, long inQuotaTimeElapsed) {
+ mAlarmQueue.remove(uptc);
+ mAlarmQueue.offer(new Pair<>(uptc, inQuotaTimeElapsed));
+ setNextAlarmLocked();
+ }
+
+ @GuardedBy("mLock")
+ void clearLocked() {
+ cancelAlarm(this);
+ mAlarmQueue.clear();
+ mTriggerTimeElapsed = 0;
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmLocked(@NonNull Uptc uptc) {
+ if (mAlarmQueue.remove(uptc)) {
+ if (mAlarmQueue.size() == 0) {
+ cancelAlarm(this);
+ } else {
+ setNextAlarmLocked();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmsLocked(int userId) {
+ boolean removed = false;
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ final Uptc uptc = (Uptc) alarms[i].first;
+ if (userId == uptc.userId) {
+ mAlarmQueue.remove(alarms[i]);
+ removed = true;
+ }
+ }
+ if (removed) {
+ setNextAlarmLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmsLocked(int userId, @NonNull String packageName) {
+ boolean removed = false;
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ final Uptc uptc = (Uptc) alarms[i].first;
+ if (userId == uptc.userId && packageName.equals(uptc.packageName)) {
+ mAlarmQueue.remove(alarms[i]);
+ removed = true;
+ }
+ }
+ if (removed) {
+ setNextAlarmLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void setNextAlarmLocked() {
+ if (mAlarmQueue.size() > 0) {
+ final long nextTriggerTimeElapsed = mAlarmQueue.peek().second;
+ // Only schedule the alarm if one of the following is true:
+ // 1. There isn't one currently scheduled
+ // 2. The new alarm is significantly earlier than the previous alarm. If it's
+ // earlier but not significantly so, then we essentially delay the notification a
+ // few extra minutes.
+ if (mTriggerTimeElapsed == 0
+ || nextTriggerTimeElapsed < mTriggerTimeElapsed - 3 * MINUTE_IN_MILLIS
+ || mTriggerTimeElapsed < nextTriggerTimeElapsed) {
+ // Use a non-wakeup alarm for this
+ scheduleAlarm(AlarmManager.ELAPSED_REALTIME, nextTriggerTimeElapsed,
+ ALARM_TAG_QUOTA_CHECK, this);
+ mTriggerTimeElapsed = nextTriggerTimeElapsed;
+ }
+ } else {
+ mTriggerTimeElapsed = 0;
+ }
+ }
+
+ @Override
+ public void onAlarm() {
+ synchronized (mLock) {
+ while (mAlarmQueue.size() > 0) {
+ final Pair<Uptc, Long> alarm = mAlarmQueue.peek();
+ if (alarm.second <= mInjector.getElapsedRealtime()) {
+ getHandler().post(() -> maybeUpdateQuotaStatus(
+ alarm.first.userId, alarm.first.packageName, alarm.first.tag));
+ mAlarmQueue.remove(alarm);
+ } else {
+ break;
+ }
+ }
+ setNextAlarmLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.println("In quota alarms:");
+ pw.increaseIndent();
+
+ if (mAlarmQueue.size() == 0) {
+ pw.println("NOT WAITING");
+ } else {
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = 0; i < alarms.length; ++i) {
+ final Uptc uptc = (Uptc) alarms[i].first;
+ pw.print(uptc);
+ pw.print(": ");
+ pw.print(alarms[i].second);
+ pw.println();
+ }
+ }
+
+ pw.decreaseIndent();
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(QuotaTrackerProto.InQuotaAlarmListener.TRIGGER_TIME_ELAPSED,
+ mTriggerTimeElapsed);
+
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = 0; i < alarms.length; ++i) {
+ final long aToken = proto.start(QuotaTrackerProto.InQuotaAlarmListener.ALARMS);
+
+ final Uptc uptc = (Uptc) alarms[i].first;
+ uptc.dumpDebug(proto, QuotaTrackerProto.InQuotaAlarmListener.Alarm.UPTC);
+ proto.write(QuotaTrackerProto.InQuotaAlarmListener.Alarm.IN_QUOTA_TIME_ELAPSED,
+ (Long) alarms[i].second);
+
+ proto.end(aToken);
+ }
+
+ proto.end(token);
+ }
+ }
+
+ //////////////////////////// DATA DUMP //////////////////////////////
+
+ /** Dump state in text format. */
+ public void dump(final IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.println("Is enabled: " + mIsEnabled);
+ pw.println("Is global quota free: " + mIsQuotaFree);
+ pw.println("Current elapsed time: " + mInjector.getElapsedRealtime());
+ pw.println();
+
+ pw.println();
+ mInQuotaAlarmListener.dumpLocked(pw);
+
+ pw.println();
+ pw.println("Per-app free quota:");
+ pw.increaseIndent();
+ for (int u = 0; u < mFreeQuota.numMaps(); ++u) {
+ final int userId = mFreeQuota.keyAt(u);
+ for (int p = 0; p < mFreeQuota.numElementsForKey(userId); ++p) {
+ final String pkgName = mFreeQuota.keyAt(u, p);
+
+ pw.print(string(userId, pkgName, null));
+ pw.print(": ");
+ pw.println(mFreeQuota.get(userId, pkgName));
+ }
+ }
+ pw.decreaseIndent();
+ }
+ }
+
+ /**
+ * Dump state to proto.
+ *
+ * @param proto The ProtoOutputStream to write to.
+ * @param fieldId The field ID of the {@link QuotaTrackerProto}.
+ */
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ synchronized (mLock) {
+ proto.write(QuotaTrackerProto.IS_ENABLED, mIsEnabled);
+ proto.write(QuotaTrackerProto.IS_GLOBAL_QUOTA_FREE, mIsQuotaFree);
+ proto.write(QuotaTrackerProto.ELAPSED_REALTIME, mInjector.getElapsedRealtime());
+ mInQuotaAlarmListener.dumpLocked(proto, QuotaTrackerProto.IN_QUOTA_ALARM_LISTENER);
+ }
+
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 834e924..70fadb3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3990,7 +3990,10 @@
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
- if (okToAnimate() && appTransition.isTransitionSet()) {
+ // Note that we ignore display frozen since we want the opening / closing transition type
+ // can be updated correctly even display frozen, and it's safe since in applyAnimation will
+ // still check DC#okToAnimate again if the transition animation is fine to apply.
+ if (okToAnimate(true /* ignoreFrozen */) && appTransition.isTransitionSet()) {
if (visible) {
displayContent.mOpeningApps.add(this);
mEnteringAnimation = true;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 60f051c..331386f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -156,6 +156,8 @@
import android.app.admin.DevicePolicyCache;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.EnterPipRequestedItem;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -4959,6 +4961,44 @@
}
}
+ /**
+ * Requests that an activity should enter picture-in-picture mode if possible.
+ */
+ @Override
+ public void requestPictureInPictureMode(IBinder token) throws RemoteException {
+ mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "requestPictureInPictureMode");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
+ if (activity == null) {
+ return;
+ }
+
+ final boolean canEnterPictureInPicture = activity.checkEnterPictureInPictureState(
+ "requestPictureInPictureMode", /* beforeStopping */ false);
+ if (!canEnterPictureInPicture) {
+ throw new IllegalStateException(
+ "Requested PIP on an activity that doesn't support it");
+ }
+
+ try {
+ final ClientTransaction transaction = ClientTransaction.obtain(
+ activity.app.getThread(),
+ activity.token);
+ transaction.addCallback(EnterPipRequestedItem.obtain());
+ getLifecycleManager().scheduleTransaction(transaction);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to send enter pip requested item: "
+ + activity.intent.getComponent(), e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6e09b94..369dde6 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -624,8 +624,8 @@
// If we start the app transition at this point, we will interrupt it halfway with a
// new rotation animation after the old one finally finishes. It's better to defer the
// app transition.
- if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
- mDisplayContent.getDisplayRotation().needsUpdate()) {
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()
+ && mDisplayContent.getDisplayRotation().needsUpdate()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Delaying app transition for screen rotation animation to finish");
return false;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5bf8e05..5a5c102 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -842,6 +842,7 @@
// Update effect.
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
+
if (!mTmpApplySurfaceChangesTransactionState.obscured) {
final boolean isDisplayed = w.isDisplayedLw();
@@ -872,6 +873,10 @@
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
= w.mAttrs.preferredRefreshRate;
}
+
+ mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing
+ |= w.mAttrs.preferMinimalPostProcessing;
+
final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(w);
if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
@@ -3755,6 +3760,7 @@
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
+ mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
true /* inTraversal, must call performTraversalInTrans... below */);
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
@@ -3988,15 +3994,23 @@
}
boolean okToDisplay() {
+ return okToDisplay(false);
+ }
+
+ boolean okToDisplay(boolean ignoreFrozen) {
if (mDisplayId == DEFAULT_DISPLAY) {
- return !mWmService.mDisplayFrozen
+ return (!mWmService.mDisplayFrozen || ignoreFrozen)
&& mWmService.mDisplayEnabled && mWmService.mPolicy.isScreenOn();
}
return mDisplayInfo.state == Display.STATE_ON;
}
boolean okToAnimate() {
- return okToDisplay() &&
+ return okToAnimate(false);
+ }
+
+ boolean okToAnimate(boolean ignoreFrozen) {
+ return okToDisplay(ignoreFrozen) &&
(mDisplayId != DEFAULT_DISPLAY || mWmService.mPolicy.okToAnimate());
}
@@ -4058,6 +4072,7 @@
boolean displayHasContent;
boolean obscured;
boolean syswin;
+ boolean preferMinimalPostProcessing;
float preferredRefreshRate;
int preferredModeId;
@@ -4065,6 +4080,7 @@
displayHasContent = false;
obscured = false;
syswin = false;
+ preferMinimalPostProcessing = false;
preferredRefreshRate = 0;
preferredModeId = 0;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index fbbc941..694a73d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1878,7 +1878,7 @@
final Rect dfu = displayFrames.mUnrestricted;
Insets insets = Insets.of(0, 0, 0, 0);
for (int i = types.size() - 1; i >= 0; i--) {
- insets = Insets.max(insets, mDisplayContent.getInsetsStateController()
+ insets = Insets.max(insets, mDisplayContent.getInsetsPolicy()
.getInsetsForDispatch(win).getSource(types.valueAt(i))
.calculateInsets(dfu, attrs.getFitIgnoreVisibility()));
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 7dd9790..f778e4d 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -381,8 +381,8 @@
* @return the {@link DisplayContent} or {@code null} if nothing is found.
*/
DisplayContent getDisplayContent(String uniqueId) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final DisplayContent display = getChildAt(i);
final boolean isValid = display.mDisplay.isValid();
if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
return display;
@@ -394,8 +394,8 @@
// TODO: Look into consolidating with getDisplayContentOrCreate()
DisplayContent getDisplayContent(int displayId) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = getChildAt(i);
if (displayContent.mDisplayId == displayId) {
return displayContent;
}
@@ -438,16 +438,16 @@
boolean startHomeOnAllDisplays(int userId, String reason) {
boolean homeStarted = false;
- for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
- final int displayId = mDisplayContents.get(i).mDisplayId;
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final int displayId = getChildAt(i).mDisplayId;
homeStarted |= startHomeOnDisplay(userId, reason, displayId);
}
return homeStarted;
}
void startHomeOnEmptyDisplays(String reason) {
- for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent display = getChildAt(i);
if (display.topRunningActivity() == null) {
startHomeOnDisplay(mCurrentUser, reason, display.mDisplayId);
}
@@ -786,8 +786,8 @@
final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
// Traverse all displays.
- for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final DisplayContent display = getChildAt(i);
// Traverse all stacks on a display.
for (int j = display.getStackCount() - 1; j >= 0; --j) {
final ActivityStack stack = display.getStackAt(j);
@@ -808,8 +808,8 @@
}
ActivityStack getTopDisplayFocusedStack() {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final ActivityStack focusedStack = mDisplayContents.get(i).getFocusedStack();
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack focusedStack = getChildAt(i).getFocusedStack();
if (focusedStack != null) {
return focusedStack;
}
@@ -828,8 +828,8 @@
}
// The top focused stack might not have a resumed activity yet - look on all displays in
// focus order.
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final DisplayContent display = getChildAt(i);
final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
if (resumedActivityOnDisplay != null) {
return resumedActivityOnDisplay;
@@ -858,8 +858,8 @@
// previous app if this activity is being hosted by the process that is actually still the
// foreground.
WindowProcessController fgApp = null;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
if (isTopDisplayFocusedStack(stack)) {
@@ -886,8 +886,8 @@
boolean attachApplication(WindowProcessController app) throws RemoteException {
final String processName = app.mName;
boolean didSomething = false;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
final ActivityStack stack = display.getFocusedStack();
if (stack == null) {
continue;
@@ -955,8 +955,8 @@
try {
mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
// First the front stacks. In case any are not fullscreen and are in front of home.
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
display.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
}
@@ -985,8 +985,8 @@
mCurrentUser = userId;
mStackSupervisor.mStartingUsers.add(uss);
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
stack.switchUser(userId);
@@ -1165,8 +1165,8 @@
}
void executeAppTransitionForAllDisplay() {
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
display.mDisplayContent.executeAppTransition();
}
}
@@ -1199,8 +1199,8 @@
}
}
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
if (display.mDisplayId == preferredDisplayId) {
continue;
}
@@ -1224,8 +1224,8 @@
int finishTopCrashedActivities(WindowProcessController app, String reason) {
Task finishedTask = null;
ActivityStack focusedStack = getTopDisplayFocusedStack();
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
// It is possible that request to finish activity might also remove its task and stack,
// so we need to be careful with indexes in the loop and check child count every time.
for (int stackNdx = 0; stackNdx < display.getStackCount(); ++stackNdx) {
@@ -1256,9 +1256,9 @@
result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
boolean resumedOnDisplay = false;
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord topRunningActivity = stack.topRunningActivity();
@@ -1299,9 +1299,9 @@
}
void applySleepTokens(boolean applyToStacks) {
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
// Set the sleeping state of the display.
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ final DisplayContent display = getChildAt(displayNdx);
final boolean displayShouldSleep = display.shouldSleep();
if (displayShouldSleep == display.isSleeping()) {
continue;
@@ -1355,8 +1355,8 @@
}
protected ActivityStack getStack(int stackId) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final ActivityStack stack = mDisplayContents.get(i).getStack(stackId);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getChildAt(i).getStack(stackId);
if (stack != null) {
return stack;
}
@@ -1366,9 +1366,8 @@
/** @see DisplayContent#getStack(int, int) */
ActivityStack getStack(int windowingMode, int activityType) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final ActivityStack stack =
- mDisplayContents.get(i).getStack(windowingMode, activityType);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getChildAt(i).getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
@@ -1452,8 +1451,8 @@
ArrayList<ActivityManager.StackInfo> getAllStackInfos(int displayId) {
ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
if (displayId == INVALID_DISPLAY) {
- for (int displayNdx = 0; displayNdx < mDisplayContents.size(); ++displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
list.add(getStackInfo(stack));
@@ -1539,8 +1538,8 @@
/** Update lists of UIDs that are present on displays and have access to them. */
void updateUIDsPresentOnDisplay() {
mDisplayAccessUIDs.clear();
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent displayContent = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent displayContent = getChildAt(displayNdx);
// Only bother calculating the whitelist for private displays
if (displayContent.isPrivate()) {
mDisplayAccessUIDs.append(
@@ -1635,8 +1634,8 @@
}
void prepareForShutdown() {
- for (int i = 0; i < mDisplayContents.size(); i++) {
- createSleepToken("shutdown", mDisplayContents.get(i).mDisplayId);
+ for (int i = 0; i < getChildCount(); i++) {
+ createSleepToken("shutdown", getChildAt(i).mDisplayId);
}
}
@@ -1712,8 +1711,8 @@
}
void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
stack.scheduleDestroyActivities(app, reason);
@@ -1725,8 +1724,8 @@
// successfully put to sleep.
boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
boolean allSleep = true;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
// Stacks and activities could be removed while putting activities to sleep if
// the app process was gone. This prevents us getting exception by accessing an
@@ -1796,8 +1795,8 @@
}
boolean hasAwakeDisplay() {
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
if (!display.shouldSleep()) {
return true;
}
@@ -2070,8 +2069,8 @@
}
// Now look through all displays
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final DisplayContent display = getChildAt(i);
if (display == preferredDisplay) {
// We've already checked this one
continue;
@@ -2097,8 +2096,8 @@
* @return Next valid {@link ActivityStack}, null if not found.
*/
ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final DisplayContent display = getChildAt(i);
if (display.mDisplayId == currentFocus) {
continue;
}
@@ -2113,8 +2112,8 @@
boolean handleAppDied(WindowProcessController app) {
boolean hasVisibleActivities = false;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
hasVisibleActivities |= stack.handleAppDiedLocked(app);
@@ -2226,8 +2225,8 @@
}
void finishVoiceTask(IVoiceInteractionSession session) {
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
final int numStacks = display.getStackCount();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
@@ -2241,20 +2240,20 @@
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
void removeStacksInWindowingModes(int... windowingModes) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- mDisplayContents.get(i).removeStacksInWindowingModes(windowingModes);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ getChildAt(i).removeStacksInWindowingModes(windowingModes);
}
}
void removeStacksWithActivityTypes(int... activityTypes) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- mDisplayContents.get(i).removeStacksWithActivityTypes(activityTypes);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ getChildAt(i).removeStacksWithActivityTypes(activityTypes);
}
}
ActivityRecord topRunningActivity() {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final ActivityRecord topActivity = mDisplayContents.get(i).topRunningActivity();
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord topActivity = getChildAt(i).topRunningActivity();
if (topActivity != null) {
return topActivity;
}
@@ -2263,9 +2262,9 @@
}
boolean allResumedActivitiesIdle() {
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
// TODO(b/117135575): Check resumed activities on all visible stacks.
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ final DisplayContent display = getChildAt(displayNdx);
if (display.isSleeping()) {
// No resumed activities while display is sleeping.
continue;
@@ -2293,8 +2292,8 @@
boolean allResumedActivitiesVisible() {
boolean foundResumed = false;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord r = stack.getResumedActivity();
@@ -2311,8 +2310,8 @@
boolean allPausedActivitiesComplete() {
boolean pausing = true;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord r = stack.mPausingActivity;
@@ -2376,8 +2375,8 @@
}
void cancelInitializingActivities() {
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
stack.cancelInitializingActivities();
@@ -2462,9 +2461,9 @@
}
ActivityRecord isInAnyStack(IBinder token) {
- int numDisplays = mDisplayContents.size();
+ int numDisplays = getChildCount();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord r = stack.isInStackLocked(token);
@@ -2498,8 +2497,8 @@
// activity on all displays, or if there are no resumed activities in the system.
boolean noResumedActivities = true;
boolean allFocusedProcessesDiffer = true;
- for (int displayNdx = 0; displayNdx < mDisplayContents.size(); ++displayNdx) {
- final DisplayContent displayContent = mDisplayContents.get(displayNdx);
+ for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
+ final DisplayContent displayContent = getChildAt(displayNdx);
final ActivityRecord resumedActivity = displayContent.getResumedActivity();
final WindowProcessController resumedActivityProcess =
resumedActivity == null ? null : resumedActivity.app;
@@ -2545,9 +2544,9 @@
return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
} else {
ArrayList<ActivityRecord> activities = new ArrayList<>();
- int numDisplays = mDisplayContents.size();
+ int numDisplays = getChildCount();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent display = mDisplayContents.get(displayNdx);
+ final DisplayContent display = getChildAt(displayNdx);
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
@@ -2562,8 +2561,8 @@
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix);
pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final DisplayContent display = getChildAt(i);
display.dump(pw, prefix, true /* dumpAll */);
}
}
@@ -2574,17 +2573,17 @@
*/
void dumpDisplayConfigs(PrintWriter pw, String prefix) {
pw.print(prefix); pw.println("Display override configurations:");
- final int displayCount = mDisplayContents.size();
+ final int displayCount = getChildCount();
for (int i = 0; i < displayCount; i++) {
- final DisplayContent displayContent = mDisplayContents.get(i);
+ final DisplayContent displayContent = getChildAt(i);
pw.print(prefix); pw.print(" "); pw.print(displayContent.mDisplayId); pw.print(": ");
pw.println(displayContent.getRequestedOverrideConfiguration());
}
}
public void dumpDisplays(PrintWriter pw) {
- for (int i = mDisplayContents.size() - 1; i >= 0; --i) {
- final DisplayContent display = mDisplayContents.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final DisplayContent display = getChildAt(i);
pw.print("[id:" + display.mDisplayId + " stacks:");
display.dumpStacks(pw);
pw.print("]");
@@ -2595,13 +2594,12 @@
String dumpPackage) {
boolean printed = false;
boolean needSep = false;
- for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
- DisplayContent displayContent = mDisplayContents.get(displayNdx);
+ for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ DisplayContent displayContent = getChildAt(displayNdx);
pw.print("Display #"); pw.print(displayContent.mDisplayId);
pw.println(" (activities from top to bottom):");
- final DisplayContent display = mDisplayContents.get(displayNdx);
- for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getStackAt(stackNdx);
+ for (int stackNdx = displayContent.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = displayContent.getStackAt(stackNdx);
pw.println();
printed = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
needSep = printed;
@@ -2627,8 +2625,8 @@
@WindowTraceLogLevel int logLevel) {
final long token = proto.start(fieldId);
super.dumpDebug(proto, CONFIGURATION_CONTAINER, logLevel);
- for (int displayNdx = 0; displayNdx < mDisplayContents.size(); ++displayNdx) {
- final DisplayContent displayContent = mDisplayContents.get(displayNdx);
+ for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
+ final DisplayContent displayContent = getChildAt(displayNdx);
displayContent.dumpDebug(proto, DISPLAYS, logLevel);
}
mStackSupervisor.getKeyguardController().dumpDebug(proto, KEYGUARD_CONTROLLER);
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index c38e63e..2d303fa 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -198,7 +198,9 @@
}
void finishTaskPositioning() {
- mHandler.post(() -> {
+ // TaskPositioner attaches the InputEventReceiver to the animation thread. We need to
+ // dispose the receiver on the same thread to avoid race conditions.
+ mService.mAnimationHandler.post(() -> {
if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index ce8e6dd..0c39b67 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2036,7 +2036,11 @@
}
boolean okToAnimate() {
- return mDisplayContent != null && mDisplayContent.okToAnimate();
+ return okToAnimate(false /* ignoreFrozen */);
+ }
+
+ boolean okToAnimate(boolean ignoreFrozen) {
+ return mDisplayContent != null && mDisplayContent.okToAnimate(ignoreFrozen);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ba9e9ce..be41891 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1651,7 +1651,8 @@
outFrame, outContentInsets, outStableInsets, outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
- outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win));
+ outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win),
+ win.mClient instanceof IWindow.Stub /* copySource */);
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -2335,7 +2336,8 @@
outStableInsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
- outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win));
+ outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win),
+ win.mClient instanceof IWindow.Stub /* copySource */);
if (DEBUG) {
Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3039d69..886f11a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -767,7 +767,7 @@
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
mClientInsetsState =
- getDisplayContent().getInsetsStateController().getInsetsForDispatch(this);
+ getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eda69a9..1536816 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -84,6 +84,7 @@
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
+import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
@@ -5825,7 +5826,7 @@
idTypeToAttestationFlag.put(ID_TYPE_IMEI, AttestationUtils.ID_TYPE_IMEI);
idTypeToAttestationFlag.put(ID_TYPE_MEID, AttestationUtils.ID_TYPE_MEID);
idTypeToAttestationFlag.put(
- ID_TYPE_INDIVIDUAL_ATTESTATION, AttestationUtils.USE_INDIVIDUAL_ATTESTATION);
+ ID_TYPE_INDIVIDUAL_ATTESTATION, USE_INDIVIDUAL_ATTESTATION);
int numFlagsSet = Integer.bitCount(idAttestationFlags);
// No flags are set - return null to indicate no device ID attestation information should
@@ -5865,6 +5866,7 @@
if (deviceIdAttestationRequired && attestationUtilsFlags.length > 0) {
enforceCallerCanRequestDeviceIdAttestation(who, callerPackage, callingUid);
+ enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags);
} else {
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_CERT_INSTALL);
@@ -5959,6 +5961,17 @@
return false;
}
+ private void enforceIndividualAttestationSupportedIfRequested(int[] attestationUtilsFlags) {
+ for (int attestationFlag : attestationUtilsFlags) {
+ if (attestationFlag == USE_INDIVIDUAL_ATTESTATION
+ && !mInjector.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_DEVICE_UNIQUE_ATTESTATION)) {
+ throw new UnsupportedOperationException("Device Individual attestation is not "
+ + "supported on this device.");
+ }
+ }
+ }
+
@Override
public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
byte[] cert, byte[] chain, boolean isUserSelectable) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 50ae376..cfe1318 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -146,6 +146,7 @@
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.role.RoleManagerService;
import com.android.server.rollback.RollbackManagerService;
+import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
import com.android.server.signedconfig.SignedConfigService;
@@ -673,6 +674,13 @@
AppCompatCallbacks.install(new long[0]);
t.traceEnd();
+ // FileIntegrityService responds to requests from apps and the system. It needs to run after
+ // the source (i.e. keystore) is ready, and before the apps (or the first customer in the
+ // system) run.
+ t.traceBegin("StartFileIntegrityService");
+ mSystemServiceManager.startService(FileIntegrityService.class);
+ t.traceEnd();
+
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
new file mode 100644
index 0000000..6345c98
--- /dev/null
+++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 5c2ad94..73a191d 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -57,6 +57,7 @@
@Mock private AudioService mMockAudioService;
@Spy private AudioDeviceInventory mSpyDevInventory;
+ @Spy private AudioSystemAdapter mSpyAudioSystem;
private BluetoothDevice mFakeBtDevice;
@@ -65,7 +66,8 @@
mContext = InstrumentationRegistry.getTargetContext();
mMockAudioService = mock(AudioService.class);
- mSpyDevInventory = spy(new AudioDeviceInventory());
+ mSpyAudioSystem = spy(AudioSystemAdapter.getAlwaysOkAdapter());
+ mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem));
mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory);
mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
@@ -81,8 +83,9 @@
public void testSetUpAndTearDown() { }
/**
- * Verify call to postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for connection
- * calls into AudioDeviceInventory with the right params
+ * postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for connection:
+ * - verify it calls into AudioDeviceInventory with the right params
+ * - verify it calls into AudioSystem and stays connected (no 2nd call to disconnect)
* @throws Exception
*/
@Test
@@ -92,7 +95,7 @@
mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS);
verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState(
any(BluetoothDevice.class),
ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/,
@@ -100,6 +103,14 @@
ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/,
ArgumentMatchers.eq(1) /*a2dpVolume*/
);
+
+ final String expectedName = mFakeBtDevice.getName() == null ? "" : mFakeBtDevice.getName();
+ verify(mSpyAudioSystem, times(1)).setDeviceConnectionState(
+ ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
+ ArgumentMatchers.eq(AudioSystem.DEVICE_STATE_AVAILABLE),
+ ArgumentMatchers.eq(mFakeBtDevice.getAddress()),
+ ArgumentMatchers.eq(expectedName),
+ anyInt() /*codec*/);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index c080332..a2376a6 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -16,41 +16,380 @@
package com.android.server.integrity;
+import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
+import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
+import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE;
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_UID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.Rule;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.LocalServices;
+import com.android.server.integrity.engine.RuleEvaluationEngine;
+import com.android.server.integrity.model.IntegrityCheckResult;
+import com.android.server.testutils.TestUtils;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
/** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
@RunWith(AndroidJUnit4.class)
public class AppIntegrityManagerServiceImplTest {
+ private static final String TEST_DIR = "AppIntegrityManagerServiceImplTest";
- @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
+ private static final String VERSION = "version";
+ private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
+
+ private static final String PACKAGE_NAME = "com.test.app";
+ private static final int VERSION_CODE = 100;
+ private static final String INSTALLER = TEST_FRAMEWORK_PACKAGE;
+ // These are obtained by running the test and checking logcat.
+ private static final String APP_CERT =
+ "949ADC6CB92FF09E3784D6E9504F26F9BEAC06E60D881D55A6A81160F9CD6FD1";
+ private static final String INSTALLER_CERT =
+ "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
+ // We use SHA256 for package names longer than 32 characters.
+ private static final String INSTALLER_SHA256 =
+ "786933C28839603EB48C50B2A688DC6BE52C833627CB2731FF8466A2AE9F94CD";
+
+ @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock PackageManagerInternal mPackageManagerInternal;
+ @Mock Context mMockContext;
+ @Mock Resources mMockResources;
+ @Mock RuleEvaluationEngine mRuleEvaluationEngine;
+ @Mock IntegrityFileManager mIntegrityFileManager;
+ @Mock Handler mHandler;
+ private PackageManager mSpyPackageManager;
+ private File mTestApk;
+
+ private final Context mRealContext = InstrumentationRegistry.getTargetContext();
// under test
private AppIntegrityManagerServiceImpl mService;
@Before
- public void setup() {
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+ public void setup() throws Exception {
+ mTestApk = File.createTempFile("TestApk", /* suffix= */ null);
+ mTestApk.deleteOnExit();
+ try (InputStream inputStream = mRealContext.getAssets().open(TEST_DIR + "/test.apk")) {
+ Files.copy(inputStream, mTestApk.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ }
- mService = new AppIntegrityManagerServiceImpl(InstrumentationRegistry.getContext());
+ mService =
+ new AppIntegrityManagerServiceImpl(
+ mMockContext,
+ mPackageManagerInternal,
+ mRuleEvaluationEngine,
+ mIntegrityFileManager,
+ mHandler);
+
+ mSpyPackageManager = spy(mRealContext.getPackageManager());
+ // setup mocks to prevent NPE
+ when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestApk.delete();
+ }
+
+ // This is not a test of the class, but more of a safeguard that we don't block any install in
+ // the default case. This is needed because we don't have any emergency kill switch to disable
+ // this component.
+ @Test
+ public void default_allow() throws Exception {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+ mService = AppIntegrityManagerServiceImpl.create(mMockContext);
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext, times(2))
+ .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+ Intent intent = makeVerificationIntent();
+
+ broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+
+ // Since we are not mocking handler in this case, we must wait.
+ // 2 seconds should be a sensible timeout.
+ Thread.sleep(2000);
+ verify(mPackageManagerInternal)
+ .setIntegrityVerificationResult(
+ 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
}
@Test
- public void noop() {
- // We need this test just as a place holder since an empty test suite is treated as error.
+ public void updateRuleSet_notAuthorized() throws Exception {
+ makeUsSystemApp();
+ Rule rule =
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ Rule.DENY);
+ TestUtils.assertExpectException(
+ SecurityException.class,
+ "Only system packages specified in config_integrityRuleProviderPackages are"
+ + " allowed to call this method.",
+ () ->
+ mService.updateRuleSet(
+ VERSION,
+ new ParceledListSlice<>(Arrays.asList(rule)),
+ /* statusReceiver= */ null));
+ }
+
+ @Test
+ public void updateRuleSet_notSystemApp() throws Exception {
+ whitelistUsAsRuleProvider();
+ Rule rule =
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ Rule.DENY);
+ TestUtils.assertExpectException(
+ SecurityException.class,
+ "Only system packages specified in config_integrityRuleProviderPackages are"
+ + " allowed to call this method.",
+ () ->
+ mService.updateRuleSet(
+ VERSION,
+ new ParceledListSlice<>(Arrays.asList(rule)),
+ /* statusReceiver= */ null));
+ }
+
+ @Test
+ public void updateRuleSet_authorized() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
+ Rule rule =
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ Rule.DENY);
+
+ // no SecurityException
+ mService.updateRuleSet(
+ VERSION, new ParceledListSlice<>(Arrays.asList(rule)), mock(IntentSender.class));
+ }
+
+ @Test
+ public void updateRuleSet_correctMethodCall() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
+ IntentSender mockReceiver = mock(IntentSender.class);
+ List<Rule> rules =
+ Arrays.asList(
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ PACKAGE_NAME,
+ /* isHashedValue= */ false),
+ Rule.DENY));
+
+ mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
+ runJobInHandler();
+
+ verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules);
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any());
+ assertEquals(STATUS_SUCCESS, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1));
+ }
+
+ @Test
+ public void updateRuleSet_fail() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
+ doThrow(new IOException()).when(mIntegrityFileManager).writeRules(any(), any(), any());
+ IntentSender mockReceiver = mock(IntentSender.class);
+ List<Rule> rules =
+ Arrays.asList(
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ PACKAGE_NAME,
+ /* isHashedValue= */ false),
+ Rule.DENY));
+
+ mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
+ runJobInHandler();
+
+ verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules);
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any());
+ assertEquals(STATUS_FAILURE, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1));
+ }
+
+ @Test
+ public void broadcastReceiverRegistration() throws Exception {
+ ArgumentCaptor<IntentFilter> intentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ verify(mMockContext).registerReceiver(any(), intentFilterCaptor.capture(), any(), any());
+ assertEquals(1, intentFilterCaptor.getValue().countActions());
+ assertEquals(
+ Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION,
+ intentFilterCaptor.getValue().getAction(0));
+ assertEquals(1, intentFilterCaptor.getValue().countDataTypes());
+ assertEquals(PACKAGE_MIME_TYPE, intentFilterCaptor.getValue().getDataType(0));
+ }
+
+ @Test
+ public void handleBroadcast_correctArgs() throws Exception {
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext)
+ .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+ Intent intent = makeVerificationIntent();
+ when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
+
+ broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+ runJobInHandler();
+
+ ArgumentCaptor<AppInstallMetadata> metadataCaptor =
+ ArgumentCaptor.forClass(AppInstallMetadata.class);
+ Map<String, String> allowedInstallers = new HashMap<>();
+ ArgumentCaptor<Map<String, String>> allowedInstallersCaptor =
+ ArgumentCaptor.forClass(allowedInstallers.getClass());
+ verify(mRuleEvaluationEngine)
+ .evaluate(metadataCaptor.capture(), allowedInstallersCaptor.capture());
+ AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
+ allowedInstallers = allowedInstallersCaptor.getValue();
+ assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
+ assertEquals(APP_CERT, appInstallMetadata.getAppCertificate());
+ assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
+ assertEquals(INSTALLER_CERT, appInstallMetadata.getInstallerCertificate());
+ assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
+ assertFalse(appInstallMetadata.isPreInstalled());
+ // These are hardcoded in the test apk
+ assertEquals(2, allowedInstallers.size());
+ assertEquals("cert_1", allowedInstallers.get("store_1"));
+ assertEquals("cert_2", allowedInstallers.get("store_2"));
+ }
+
+ @Test
+ public void handleBroadcast_allow() throws Exception {
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext)
+ .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+ Intent intent = makeVerificationIntent();
+ when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
+
+ broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+ runJobInHandler();
+
+ verify(mPackageManagerInternal)
+ .setIntegrityVerificationResult(
+ 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ }
+
+ @Test
+ public void handleBroadcast_reject() throws Exception {
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext)
+ .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+ when(mRuleEvaluationEngine.evaluate(any(), any()))
+ .thenReturn(
+ IntegrityCheckResult.deny(
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, false),
+ Rule.DENY)));
+ Intent intent = makeVerificationIntent();
+
+ broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+ runJobInHandler();
+
+ verify(mPackageManagerInternal)
+ .setIntegrityVerificationResult(
+ 1, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
+ }
+
+ private void whitelistUsAsRuleProvider() {
+ Resources mockResources = mock(Resources.class);
+ when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
+ .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
+ when(mMockContext.getResources()).thenReturn(mockResources);
+ }
+
+ private void runJobInHandler() {
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ // sendMessageAtTime is the first non-final method in the call chain when "post" is invoked.
+ verify(mHandler).sendMessageAtTime(messageCaptor.capture(), anyLong());
+ messageCaptor.getValue().getCallback().run();
+ }
+
+ private void makeUsSystemApp() throws Exception {
+ PackageInfo packageInfo =
+ mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE, 0);
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ doReturn(packageInfo)
+ .when(mSpyPackageManager)
+ .getPackageInfo(eq(TEST_FRAMEWORK_PACKAGE), anyInt());
+ }
+
+ private Intent makeVerificationIntent() throws Exception {
+ Intent intent = new Intent();
+ intent.setDataAndType(Uri.fromFile(mTestApk), PACKAGE_MIME_TYPE);
+ intent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
+ intent.putExtra(EXTRA_VERIFICATION_ID, 1);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, PACKAGE_NAME);
+ intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, INSTALLER);
+ intent.putExtra(
+ EXTRA_VERIFICATION_INSTALLER_UID,
+ mRealContext.getPackageManager().getPackageUid(INSTALLER, /* flags= */ 0));
+ intent.putExtra(Intent.EXTRA_VERSION_CODE, VERSION_CODE);
+ return intent;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityUtilsTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityUtilsTest.java
new file mode 100644
index 0000000..ac7f8f9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.integrity;
+
+import static com.android.server.integrity.IntegrityUtils.getBytesFromHexDigest;
+import static com.android.server.integrity.IntegrityUtils.getHexDigest;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit test for {@link com.android.server.integrity.IntegrityUtils} */
+@RunWith(AndroidJUnit4.class)
+public class IntegrityUtilsTest {
+
+ private static final String HEX_DIGEST = "1234567890ABCDEF";
+ private static final byte[] BYTES =
+ new byte[] {0x12, 0x34, 0x56, 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF};
+
+ @Test
+ public void testGetBytesFromHexDigest() {
+ assertArrayEquals(BYTES, getBytesFromHexDigest(HEX_DIGEST));
+ }
+
+ @Test
+ public void testGetHexDigest() {
+ assertEquals(HEX_DIGEST, getHexDigest(BYTES));
+ }
+
+ @Test
+ public void testInvalidHexDigest() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ "must have even length",
+ () -> getBytesFromHexDigest("ABC"));
+
+ assertExpectException(
+ IllegalArgumentException.class,
+ "Invalid hex char",
+ () -> getBytesFromHexDigest("GH"));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index d6ef2d4..7457067 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -101,30 +101,6 @@
return mService.getLong(SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId) != 0;
}
- @Test
- public void testPasswordMigration() throws RemoteException {
- final LockscreenCredential password = newPassword("testPasswordMigration-password");
-
- disableSyntheticPassword();
- assertTrue(mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID));
- long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- enableSyntheticPassword();
- // Performs migration
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- password, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
-
- // SP-based verification
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- password, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertArrayNotEquals(primaryStorageKey,
- mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
- }
-
protected void initializeCredentialUnderSP(LockscreenCredential password, int userId)
throws RemoteException {
enableSyntheticPassword();
@@ -253,81 +229,6 @@
}
@Test
- public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
- LockscreenCredential UnifiedPassword = newPassword("unified-pwd");
- disableSyntheticPassword();
- mService.setLockCredential(UnifiedPassword, nonePassword(), PRIMARY_USER_ID);
- mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
- final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
- byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
- assertTrue(primarySid != 0);
- assertTrue(profileSid != 0);
- assertTrue(profileSid != primarySid);
-
- // do migration
- enableSyntheticPassword();
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- UnifiedPassword, 0, PRIMARY_USER_ID)
- .getResponseCode());
-
- // verify
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- UnifiedPassword, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
- assertArrayNotEquals(primaryStorageKey,
- mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
- assertArrayNotEquals(profileStorageKey,
- mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID));
- assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
- assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID));
- }
-
- @Test
- public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
- LockscreenCredential primaryPassword = newPassword("primary");
- LockscreenCredential profilePassword = newPassword("profile");
- disableSyntheticPassword();
- mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID);
- mService.setLockCredential(profilePassword, nonePassword(), MANAGED_PROFILE_USER_ID);
- final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
- byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
- assertTrue(primarySid != 0);
- assertTrue(profileSid != 0);
- assertTrue(profileSid != primarySid);
-
- // do migration
- enableSyntheticPassword();
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- primaryPassword, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword, 0, MANAGED_PROFILE_USER_ID)
- .getResponseCode());
-
- // verify
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- primaryPassword, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword, 0, MANAGED_PROFILE_USER_ID)
- .getResponseCode());
- assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
- assertArrayNotEquals(primaryStorageKey,
- mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
- assertArrayNotEquals(profileStorageKey,
- mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID));
- assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
- assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID));
- }
-
- @Test
public void testTokenBasedResetPassword() throws RemoteException {
LockscreenCredential password = newPassword("password");
LockscreenCredential pattern = newPattern("123654");
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 81fb0ec..13643a0 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -62,6 +62,7 @@
import android.os.PowerSaveState;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.FlakyTest;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.Display;
@@ -734,6 +735,7 @@
assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
}
+ @FlakyTest
@Test
public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception {
final String pkg = mContextSpy.getOpPackageName();
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
index 89c7dd4..ecdc58e 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.platform.test.annotations.FlakyTest;
import android.util.ArrayMap;
import androidx.test.InstrumentationRegistry;
@@ -328,6 +329,7 @@
veriryWtf(0);
}
+ @FlakyTest
@Test
public void testAll() throws Exception {
// Run multiple tests on the single target instance.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index f1de6e9..438de78 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -20,20 +20,30 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.EnterPipRequestedItem;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.RemoteException;
import android.view.IDisplayWindowListener;
import android.view.WindowContainerTransaction;
@@ -42,6 +52,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.MockitoSession;
import java.util.ArrayList;
@@ -56,6 +67,9 @@
@RunWith(WindowTestRunner.class)
public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
+ private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor =
+ ArgumentCaptor.forClass(ClientTransaction.class);
+
@Before
public void setUp() throws Exception {
doReturn(false).when(mService).isBooting();
@@ -78,6 +92,39 @@
}
@Test
+ public void testOnPictureInPictureRequested() throws RemoteException {
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
+ final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
+ ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
+ doNothing().when(lifecycleManager).scheduleTransaction(any());
+ doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
+
+ mService.requestPictureInPictureMode(activity.token);
+
+ verify(lifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
+ final ClientTransaction transaction = mClientTransactionCaptor.getValue();
+ // Check that only an enter pip request item callback was scheduled.
+ assertEquals(1, transaction.getCallbacks().size());
+ assertTrue(transaction.getCallbacks().get(0) instanceof EnterPipRequestedItem);
+ // Check the activity lifecycle state remains unchanged.
+ assertNull(transaction.getLifecycleStateRequest());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
+ final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
+ ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
+ doNothing().when(lifecycleManager).scheduleTransaction(any());
+ doReturn(false).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
+
+ mService.requestPictureInPictureMode(activity.token);
+
+ // Check enter no transactions with enter pip requests are made.
+ verify(lifecycleManager, times(0)).scheduleTransaction(any());
+ }
+
+ @Test
public void testTaskTransaction() {
removeGlobalMinSizeRestriction();
final ActivityStack stack = new StackBuilder(mRootActivityContainer)
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ccbafd4..612e051 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -514,6 +514,13 @@
// Prevent mInitialDisplayCutout from being updated from real display (e.g. null
// if the device has no cutout).
final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
+ // This test assumes it's a top cutout on a portrait display, so if it happens to be a
+ // landscape display let's rotate it.
+ if (dc.mInitialDisplayHeight < dc.mInitialDisplayWidth) {
+ int tmp = dc.mInitialDisplayHeight;
+ dc.mInitialDisplayHeight = dc.mInitialDisplayWidth;
+ dc.mInitialDisplayWidth = tmp;
+ }
// Rotation may use real display info to compute bound, so here also uses the
// same width and height.
final int displayWidth = dc.mInitialDisplayWidth;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index de73645..5aece45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -272,7 +272,7 @@
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
final InsetsState state =
- mDisplayContent.getInsetsStateController().getInsetsForDispatch(mWindow);
+ mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitIgnoreVisibility(true);
@@ -294,7 +294,7 @@
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
final InsetsState state =
- mDisplayContent.getInsetsStateController().getInsetsForDispatch(mWindow);
+ mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitIgnoreVisibility(false);
@@ -452,7 +452,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- mDisplayContent.getInsetsStateController().getInsetsForDispatch(mWindow)
+ mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
addWindow(mWindow);
@@ -473,7 +473,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- mDisplayContent.getInsetsStateController().getInsetsForDispatch(mWindow)
+ mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
addWindow(mWindow);
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index fbb1380..80a55b2 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -344,7 +344,7 @@
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccess(
+ if (devicePolicyManager != null && devicePolicyManager.hasDeviceIdentifierAccess(
callingPackage, pid, uid)) {
return true;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4bb237f..eea08dc 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2316,13 +2316,41 @@
/**
* Determine whether to use only RSRP for the number of LTE signal bars.
* @hide
+ *
+ * @deprecated use {@link #KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT}.
*/
// FIXME: this key and related keys must not be exposed without a consistent philosophy for
// all RATs.
+ @Deprecated
public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
"use_only_rsrp_for_lte_signal_bar_bool";
/**
+ * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP),
+ * Reference Signal Received Quality (RSRQ), or/and Reference Signal Signal to Noise Ratio
+ * (RSSNR) for the number of LTE signal bars and signal criteria reporting enabling.
+ *
+ * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
+ * not be used for calculating signal level. If multiple measures are set bit, the parameter
+ * whose value is smallest is used to indicate the signal level.
+ *
+ * RSRP = 1 << 0,
+ * RSRQ = 1 << 1,
+ * RSSNR = 1 << 2,
+ *
+ * The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
+ * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
+ *
+ * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+ * If the key is invalid or not configured, a default value (RSRP | RSSNR = 1 << 0 | 1 << 2)
+ * will apply.
+ *
+ * @hide
+ */
+ public static final String KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT =
+ "parameters_used_for_lte_signal_bar_int";
+
+ /**
* List of 4 customized 5G SS reference signal received power (SSRSRP) thresholds.
*
* Reference: 3GPP TS 38.215
@@ -2625,6 +2653,42 @@
"lte_rsrp_thresholds_int_array";
/**
+ * A list of 4 customized LTE Reference Signal Received Quality (RSRQ) thresholds.
+ *
+ * Reference: TS 136.133 v12.6.0 section 9.1.7 - RSRQ Measurement Report Mapping.
+ *
+ * 4 threshold integers must be within the boundaries [-34 dB, 3 dB], and the levels are:
+ * "NONE: [-34, threshold1)"
+ * "POOR: [threshold1, threshold2)"
+ * "MODERATE: [threshold2, threshold3)"
+ * "GOOD: [threshold3, threshold4)"
+ * "EXCELLENT: [threshold4, 3]"
+ *
+ * This key is considered invalid if the format is violated. If the key is invalid or
+ * not configured, a default value set will apply.
+ */
+ public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY =
+ "lte_rsrq_thresholds_int_array";
+
+ /**
+ * A list of 4 customized LTE Reference Signal Signal to Noise Ratio (RSSNR) thresholds.
+ *
+ * 4 threshold integers must be within the boundaries [-200, 300], and the levels are:
+ * "NONE: [-200, threshold1)"
+ * "POOR: [threshold1, threshold2)"
+ * "MODERATE: [threshold2, threshold3)"
+ * "GOOD: [threshold3, threshold4)"
+ * "EXCELLENT: [threshold4, 300]"
+ * Note: the unit of the values is 10*db; it is derived by multiplying 10 on the original dB
+ * value reported by modem.
+ *
+ * This key is considered invalid if the format is violated. If the key is invalid or
+ * not configured, a default value set will apply.
+ */
+ public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY =
+ "lte_rssnr_thresholds_int_array";
+
+ /**
* Decides when clients try to bind to iwlan network service, which package name will
* the binding intent go to.
* @hide
@@ -3749,6 +3813,20 @@
-108, /* SIGNAL_STRENGTH_GOOD */
-98, /* SIGNAL_STRENGTH_GREAT */
});
+ sDefaults.putIntArray(KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -19, /* SIGNAL_STRENGTH_POOR */
+ -17, /* SIGNAL_STRENGTH_MODERATE */
+ -14, /* SIGNAL_STRENGTH_GOOD */
+ -12 /* SIGNAL_STRENGTH_GREAT */
+ });
+ sDefaults.putIntArray(KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -30, /* SIGNAL_STRENGTH_POOR */
+ 10, /* SIGNAL_STRENGTH_MODERATE */
+ 45, /* SIGNAL_STRENGTH_GOOD */
+ 130 /* SIGNAL_STRENGTH_GREAT */
+ });
sDefaults.putIntArray(KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
new int[] {
-115, /* SIGNAL_STRENGTH_POOR */
@@ -3852,6 +3930,34 @@
new int[] {4 /* BUSY */});
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
+ sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
+ CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSSNR);
+ // Default wifi configurations.
+ sDefaults.putAll(Wifi.getDefaults());
+ }
+
+ /**
+ * Wi-Fi configs used in WiFi Module.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Wifi {
+ /** Prefix of all Wifi.KEY_* constants. */
+ public static final String KEY_PREFIX = "wifi.";
+ /**
+ * It contains the maximum client count definition that the carrier owns.
+ */
+ public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
+ KEY_PREFIX + "hotspot_maximum_client_count";
+
+ private static PersistableBundle getDefaults() {
+ PersistableBundle defaults = new PersistableBundle();
+ defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0);
+ return defaults;
+ }
+
+ private Wifi() {}
}
/**
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 8336d1b..8df9d23 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -55,6 +55,25 @@
private static final int MAX_LTE_RSRP = -44;
private static final int MIN_LTE_RSRP = -140;
+ /**
+ * Indicates RSRP is considered for {@link #getLevel()} and reported from modem.
+ *
+ * @hide
+ */
+ public static final int USE_RSRP = 1 << 0;
+ /**
+ * Indicates RSRQ is considered for {@link #getLevel()} and reported from modem.
+ *
+ * @hide
+ */
+ public static final int USE_RSRQ = 1 << 1;
+ /**
+ * Indicates RSSNR is considered for {@link #getLevel()} and reported from modem.
+ *
+ * @hide
+ */
+ public static final int USE_RSSNR = 1 << 2;
+
@UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mSignalStrength; // To be removed
private int mRssi;
@@ -70,6 +89,21 @@
private int mTimingAdvance;
private int mLevel;
+ /**
+ * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP),
+ * Reference Signal Received Quality (RSRQ), and/or Reference Signal Signal to Noise Ratio
+ * (RSSNR) for the number of LTE signal bars. If multiple measures are set, the parameter
+ * whose signal level value is smallest is used to indicate the signal level.
+ *
+ * RSRP = 1 << 0,
+ * RSRQ = 1 << 1,
+ * RSSNR = 1 << 2,
+ *
+ * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+ * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
+ */
+ private int mParametersUseForLevel;
+
/** @hide */
@UnsupportedAppUsage
public CellSignalStrengthLte() {
@@ -81,7 +115,7 @@
*
* @param rssi in dBm [-113,-51], UNKNOWN
* @param rsrp in dBm [-140,-43], UNKNOWN
- * @param rsrq in dB [-20,-3], UNKNOWN
+ * @param rsrq in dB [-34, 3], UNKNOWN
* @param rssnr in 10*dB [-200, +300], UNKNOWN
* @param cqi [0, 15], UNKNOWN
* @param timingAdvance [0, 1282], UNKNOWN
@@ -94,7 +128,7 @@
mRssi = inRangeOrUnavailable(rssi, -113, -51);
mSignalStrength = mRssi;
mRsrp = inRangeOrUnavailable(rsrp, -140, -43);
- mRsrq = inRangeOrUnavailable(rsrq, -20, -3);
+ mRsrq = inRangeOrUnavailable(rsrq, -34, 3);
mRssnr = inRangeOrUnavailable(rssnr, -200, 300);
mCqi = inRangeOrUnavailable(cqi, 0, 15);
mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
@@ -125,6 +159,7 @@
mCqi = s.mCqi;
mTimingAdvance = s.mTimingAdvance;
mLevel = s.mLevel;
+ mParametersUseForLevel = s.mParametersUseForLevel;
}
/** @hide */
@@ -144,6 +179,7 @@
mCqi = CellInfo.UNAVAILABLE;
mTimingAdvance = CellInfo.UNAVAILABLE;
mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ mParametersUseForLevel = USE_RSRP | USE_RSSNR;
}
/** {@inheritDoc} */
@@ -154,102 +190,153 @@
}
// Lifted from Default carrier configs and max range of RSRP
- private static final int[] sThresholds = new int[]{-115, -105, -95, -85};
+ private static final int[] sRsrpThresholds = new int[] {
+ -115, /* SIGNAL_STRENGTH_POOR */
+ -105, /* SIGNAL_STRENGTH_MODERATE */
+ -95, /* SIGNAL_STRENGTH_GOOD */
+ -85 /* SIGNAL_STRENGTH_GREAT */
+ };
+
+ // Lifted from Default carrier configs and max range of RSRQ
+ private static final int[] sRsrqThresholds = new int[] {
+ -19, /* SIGNAL_STRENGTH_POOR */
+ -17, /* SIGNAL_STRENGTH_MODERATE */
+ -14, /* SIGNAL_STRENGTH_GOOD */
+ -12 /* SIGNAL_STRENGTH_GREAT */
+ };
+ // Lifted from Default carrier configs and max range of RSSNR
+ private static final int[] sRssnrThresholds = new int[] {
+ -30, /* SIGNAL_STRENGTH_POOR */
+ 10, /* SIGNAL_STRENGTH_MODERATE */
+ 45, /* SIGNAL_STRENGTH_GOOD */
+ 130 /* SIGNAL_STRENGTH_GREAT */
+ };
private static final int sRsrpBoost = 0;
+ /**
+ * Checks if the given parameter type is considered to use for {@link #getLevel()}.
+ *
+ * Note: if multiple parameter types are considered, the smaller level for one of the
+ * parameters would be returned by {@link #getLevel()}
+ *
+ * @param parameterType bitwise OR of {@link #USE_RSRP}, {@link #USE_RSRQ},
+ * {@link #USE_RSSNR}
+ * @return {@code true} if the level is calculated based on the given parameter type;
+ * {@code false} otherwise.
+ */
+ private boolean isLevelForParameter(int parameterType) {
+ return (parameterType & mParametersUseForLevel) == parameterType;
+ }
+
/** @hide */
@Override
public void updateLevel(PersistableBundle cc, ServiceState ss) {
- int[] thresholds;
+ int[] rsrpThresholds, rsrqThresholds, rssnrThresholds;
boolean rsrpOnly;
if (cc == null) {
- thresholds = sThresholds;
+ mParametersUseForLevel = USE_RSRP | USE_RSSNR;
+ rsrpThresholds = sRsrpThresholds;
+ rsrqThresholds = sRsrqThresholds;
+ rssnrThresholds = sRssnrThresholds;
rsrpOnly = false;
} else {
+ mParametersUseForLevel = cc.getInt(
+ CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT);
+ Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+ rsrpThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+ if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds;
+ Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + Arrays.toString(rsrpThresholds));
+ rsrqThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
+ if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds;
+ Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + Arrays.toString(rsrqThresholds));
+ rssnrThresholds = cc.getIntArray(
+ CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
+ if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds;
+ Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + Arrays.toString(rssnrThresholds));
rsrpOnly = cc.getBoolean(
CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
- thresholds = cc.getIntArray(
- CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
- if (thresholds == null) thresholds = sThresholds;
- if (DBG) log("updateLevel() carrierconfig - rsrpOnly="
- + rsrpOnly + ", thresholds=" + Arrays.toString(thresholds));
}
-
int rsrpBoost = 0;
if (ss != null) {
rsrpBoost = ss.getLteEarfcnRsrpBoost();
}
- int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- int rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- int snrIconLevel = -1;
-
- int rsrp = mRsrp + rsrpBoost;
-
- if (rsrp < MIN_LTE_RSRP || rsrp > MAX_LTE_RSRP) {
- rsrpIconLevel = -1;
- } else {
- rsrpIconLevel = thresholds.length;
- while (rsrpIconLevel > 0 && rsrp < thresholds[rsrpIconLevel - 1]) rsrpIconLevel--;
- }
+ int rsrp = inRangeOrUnavailable(mRsrp + rsrpBoost, MIN_LTE_RSRP, MAX_LTE_RSRP);
if (rsrpOnly) {
- if (DBG) log("updateLevel() - rsrp = " + rsrpIconLevel);
- if (rsrpIconLevel != -1) {
- mLevel = rsrpIconLevel;
+ int level = updateLevelWithMeasure(rsrp, rsrpThresholds);
+ if (DBG) log("updateLevel() - rsrp = " + level);
+ if (level != SignalStrength.INVALID) {
+ mLevel = level;
return;
}
}
- /*
- * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
- * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
- * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
- * Icon Only
- */
- if (mRssnr > 300) snrIconLevel = -1;
- else if (mRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
- else if (mRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
- else if (mRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
- else if (mRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
- else if (mRssnr >= -200)
- snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ int rsrpLevel = SignalStrength.INVALID;
+ int rsrqLevel = SignalStrength.INVALID;
+ int rssnrLevel = SignalStrength.INVALID;
- if (DBG) log("updateLevel() - rsrp:" + mRsrp + " snr:" + mRssnr + " rsrpIconLevel:"
- + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
- + " lteRsrpBoost:" + sRsrpBoost);
-
- /* Choose a measurement type to use for notification */
- if (snrIconLevel != -1 && rsrpIconLevel != -1) {
- /*
- * The number of bars displayed shall be the smaller of the bars
- * associated with LTE RSRP and the bars associated with the LTE
- * RS_SNR
- */
- mLevel = (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
- return;
+ if (isLevelForParameter(USE_RSRP)) {
+ rsrpLevel = updateLevelWithMeasure(rsrp, rsrpThresholds);
+ Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
}
-
- if (snrIconLevel != -1) {
- mLevel = snrIconLevel;
- return;
+ if (isLevelForParameter(USE_RSRQ)) {
+ rsrqLevel = updateLevelWithMeasure(mRsrq, rsrqThresholds);
+ Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
}
-
- if (rsrpIconLevel != -1) {
- mLevel = rsrpIconLevel;
- return;
+ if (isLevelForParameter(USE_RSSNR)) {
+ rssnrLevel = updateLevelWithMeasure(mRssnr, rssnrThresholds);
+ Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
}
+ // Apply the smaller value among three levels of three measures.
+ mLevel = Math.min(Math.min(rsrpLevel, rsrqLevel), rssnrLevel);
- if (mRssi > -51) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (mRssi >= -89) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
- else if (mRssi >= -97) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
- else if (mRssi >= -103) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
- else if (mRssi >= -113) rssiIconLevel = SIGNAL_STRENGTH_POOR;
- else rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- if (DBG) log("getLteLevel - rssi:" + mRssi + " rssiIconLevel:"
- + rssiIconLevel);
- mLevel = rssiIconLevel;
+ if (mLevel == SignalStrength.INVALID) {
+ int rssiLevel;
+ if (mRssi > -51) {
+ rssiLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ } else if (mRssi >= -89) {
+ rssiLevel = SIGNAL_STRENGTH_GREAT;
+ } else if (mRssi >= -97) {
+ rssiLevel = SIGNAL_STRENGTH_GOOD;
+ } else if (mRssi >= -103) {
+ rssiLevel = SIGNAL_STRENGTH_MODERATE;
+ } else if (mRssi >= -113) {
+ rssiLevel = SIGNAL_STRENGTH_POOR;
+ } else {
+ rssiLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ }
+ if (DBG) log("getLteLevel - rssi:" + mRssi + " rssiIconLevel:" + rssiLevel);
+ mLevel = rssiLevel;
+ }
+ }
+
+ /**
+ * Update level with corresponding measure and thresholds.
+ *
+ * @param measure corresponding signal measure
+ * @param thresholds corresponding signal thresholds
+ * @return level of the signal strength
+ */
+ private int updateLevelWithMeasure(int measure, int[] thresholds) {
+ int level;
+ if (measure == CellInfo.UNAVAILABLE) {
+ level = SignalStrength.INVALID;
+ } else if (measure >= thresholds[3]) {
+ level = SIGNAL_STRENGTH_GREAT;
+ } else if (measure >= thresholds[2]) {
+ level = SIGNAL_STRENGTH_GOOD;
+ } else if (measure >= thresholds[1]) {
+ level = SIGNAL_STRENGTH_MODERATE;
+ } else if (measure >= thresholds[0]) {
+ level = SIGNAL_STRENGTH_POOR;
+ } else {
+ level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ }
+ return level;
}
/**
@@ -386,7 +473,8 @@
+ " rssnr=" + mRssnr
+ " cqi=" + mCqi
+ " ta=" + mTimingAdvance
- + " level=" + mLevel;
+ + " level=" + mLevel
+ + " parametersUseForLevel=" + mParametersUseForLevel;
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 78ad5c5..0610796 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -81,18 +81,20 @@
/**
- * Constructor
+ * Constructor of PreciseDataConnectionState
*
* @param state the state of the data connection
* @param networkType the access network that is/would carry this data connection
* @param apnTypes the APN types that this data connection carries
- * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings;
- * if there is no valid APN setting for the specific type, then this will be null
+ * @param apn the APN of this data connection
* @param linkProperties if the data connection is connected, the properties of the connection
* @param failCause in case a procedure related to this data connection fails, a non-zero error
* code indicating the cause of the failure.
+ * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings;
+ * if there is no valid APN setting for the specific type, then this will be null
* @hide
*/
+ @SystemApi
public PreciseDataConnectionState(@DataState int state,
@NetworkType int networkType,
@ApnType int apnTypes, @NonNull String apn,
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index cab5286..7215ef8 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -1635,14 +1636,16 @@
* operation is performed on the correct subscription.
* </p>
*
- * @param messageIndex is the record index of the message on ICC
- * @return true for success
+ * @param messageIndex This is the same index used to access a message
+ * from {@link #getMessagesFromIcc()}.
+ * @return true for success, false if the operation fails. Failure can be due to IPC failure,
+ * RIL/modem error which results in SMS failed to be deleted on SIM
*
* {@hide}
*/
- @UnsupportedAppUsage
- public boolean
- deleteMessageFromIcc(int messageIndex) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+ public boolean deleteMessageFromIcc(int messageIndex) {
boolean success = false;
try {
@@ -1684,6 +1687,7 @@
* {@hide}
*/
@UnsupportedAppUsage
+ @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
boolean success = false;
@@ -1716,8 +1720,22 @@
* operation is performed on the correct subscription.
* </p>
*
+ * @return <code>List</code> of <code>SmsMessage</code> objects
+ *
+ * {@hide}
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+ public @NonNull List<SmsMessage> getMessagesFromIcc() {
+ return getAllMessagesFromIcc();
+ }
+
+ /**
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects
*
+ * This is similar to {@link #getMessagesFromIcc} except that it will return ArrayList.
+ * Suggested to use {@link #getMessagesFromIcc} instead.
+ *
* {@hide}
*/
@UnsupportedAppUsage
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 392d3eb..2897358 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -1038,10 +1038,10 @@
}
/**
- * {@hide}
* Returns the recipient address(receiver) of this SMS message in String form or null if
* unavailable.
*/
+ @Nullable
public String getRecipientAddress() {
return mWrappedSmsMessage.getRecipientAddress();
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f08e1ec..15398ca 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3175,6 +3175,34 @@
}
/**
+ * Set uicc applications being enabled or disabled.
+ * The value will be remembered on the subscription and will be applied whenever it's present.
+ * If the subscription in currently present, it will also apply the setting to modem
+ * immediately.
+ *
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ *
+ * @param enabled whether uicc applications are enabled or disabled.
+ * @param subscriptionId which subscription to operate on.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setUiccApplicationsEnabled(boolean enabled, int subscriptionId) {
+ if (VDBG) {
+ logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled);
+ }
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.setUiccApplicationsEnabled(enabled, subscriptionId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
* Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM.
*
* Physical SIM refers non-euicc, or aka non-programmable SIM.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a96325e..39e57b7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4243,7 +4243,7 @@
* The returned set of subscriber IDs will include the subscriber ID corresponding to this
* TelephonyManager's subId.
*
- * This is deprecated and {@link #getMergedSubscriberIdsFromGroup()} should be used for data
+ * This is deprecated and {@link #getMergedImsisFromGroup()} should be used for data
* usage merging purpose.
* TODO: remove this API.
*
@@ -4264,25 +4264,27 @@
}
/**
- * Return the set of subscriber IDs that should be considered "merged together" for data usage
- * purposes. Unlike {@link #getMergedSubscriberIds()} this API merge subscriberIds based on
- * subscription grouping: subscriberId of those in the same group will all be returned.
+ * Return the set of IMSIs that should be considered "merged together" for data usage
+ * purposes. Unlike {@link #getMergedSubscriberIds()} this API merge IMSIs based on
+ * subscription grouping: IMSI of those in the same group will all be returned.
+ * Return the current IMSI if there is no subscription group.
*
* <p>Requires the calling app to have READ_PRIVILEGED_PHONE_STATE permission.
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public @Nullable String[] getMergedSubscriberIdsFromGroup() {
+ public @NonNull String[] getMergedImsisFromGroup() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getMergedSubscriberIdsFromGroup(getSubId(), getOpPackageName());
+ return telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
}
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
- return null;
+ return new String[0];
}
/**
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index c5d58ac..cc02a40 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -300,4 +300,6 @@
int getActiveDataSubscriptionId();
boolean canDisablePhysicalSubscription();
+
+ int setUiccApplicationsEnabled(boolean enabled, int subscriptionId);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0baac71..b99fe904 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1136,7 +1136,7 @@
/**
* @hide
*/
- String[] getMergedSubscriberIdsFromGroup(int subId, String callingPackage);
+ String[] getMergedImsisFromGroup(int subId, String callingPackage);
/**
* Override the operator branding for the current ICCID.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 024b640..08c536b 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -173,24 +173,6 @@
= "android.intent.action.ANY_DATA_STATE";
/**
- * Broadcast Action: An attempt to establish a data connection has failed.
- * The intent will have the following extra values:</p>
- * <dl>
- * <dt>phoneName</dt><dd>A string version of the phone name.</dd>
- * <dt>state</dt><dd>One of {@code CONNECTED}, {@code CONNECTING}, or {code DISCONNECTED}.</dd>
- * <dt>reason</dt><dd>A string indicating the reason for the failure, if available.</dd>
- * </dl>
- *
- * <p class="note">
- * Requires the READ_PHONE_STATE permission.
- *
- * <p class="note">This is a protected intent that can only be sent
- * by the system.
- */
- public static final String ACTION_DATA_CONNECTION_FAILED
- = "android.intent.action.DATA_CONNECTION_FAILED";
-
- /**
* Broadcast Action: The sim card state has changed.
* The intent will have the following extra values:</p>
* <dl>
diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp
new file mode 100644
index 0000000..a03c6e2
--- /dev/null
+++ b/tests/UsbManagerTests/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+ name: "UsbManagerTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "frameworks-base-testutils",
+ "androidx.test.rules",
+ "mockito-target-inline-minus-junit4",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ "UsbManagerTestLib",
+ ],
+ jni_libs: ["libdexmakerjvmtiagent"],
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/tests/UsbManagerTests/AndroidManifest.xml b/tests/UsbManagerTests/AndroidManifest.xml
new file mode 100644
index 0000000..4e0b790
--- /dev/null
+++ b/tests/UsbManagerTests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.server.usbtest" >
+
+ <uses-permission android:name="android.permission.MANAGE_USB" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.usbtest"
+ android:label="UsbManagerTests"/>
+</manifest>
diff --git a/tests/UsbManagerTests/AndroidTest.xml b/tests/UsbManagerTests/AndroidTest.xml
new file mode 100644
index 0000000..c6e22cd
--- /dev/null
+++ b/tests/UsbManagerTests/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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 USB API instrumentation Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="UsbManagerTests.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="UsbManagerTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.usbtest"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/UsbManagerTests/lib/Android.bp b/tests/UsbManagerTests/lib/Android.bp
new file mode 100644
index 0000000..3c5d91b
--- /dev/null
+++ b/tests/UsbManagerTests/lib/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_library {
+ name: "UsbManagerTestLib",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "frameworks-base-testutils",
+ "androidx.test.rules",
+ "mockito-target-inline-minus-junit4",
+ "platform-test-annotations",
+ "services.core",
+ "services.net",
+ "services.usb",
+ "truth-prebuilt",
+ "androidx.core_core",
+ ],
+ libs: [
+ "android.test.mock",
+ ],
+}
diff --git a/tests/UsbManagerTests/lib/AndroidManifest.xml b/tests/UsbManagerTests/lib/AndroidManifest.xml
new file mode 100644
index 0000000..c8b301c
--- /dev/null
+++ b/tests/UsbManagerTests/lib/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.server.usblib">
+
+ <application/>
+
+</manifest>
diff --git a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
new file mode 100644
index 0000000..782439f
--- /dev/null
+++ b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.usblib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+import android.os.RemoteException;
+import android.util.Log;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests lib for {@link android.hardware.usb.UsbManager}.
+ */
+public class UsbManagerTestLib {
+ private static final String TAG = UsbManagerTestLib.class.getSimpleName();
+
+ private Context mContext;
+
+ private UsbManager mUsbManagerSys;
+ private UsbManager mUsbManagerMock;
+ @Mock private android.hardware.usb.IUsbManager mMockUsbService;
+
+ public UsbManagerTestLib(Context context) {
+ MockitoAnnotations.initMocks(this);
+ mContext = context;
+
+ assertNotNull(mUsbManagerSys = mContext.getSystemService(UsbManager.class));
+ assertNotNull(mUsbManagerMock = new UsbManager(mContext, mMockUsbService));
+ }
+
+ private long getCurrentFunctions() {
+ return mUsbManagerMock.getCurrentFunctions();
+ }
+
+ private void setCurrentFunctions(long functions) {
+ mUsbManagerMock.setCurrentFunctions(functions);
+ }
+
+ private long getCurrentFunctionsSys() {
+ return mUsbManagerSys.getCurrentFunctions();
+ }
+
+ private void setCurrentFunctionsSys(long functions) {
+ mUsbManagerSys.setCurrentFunctions(functions);
+ }
+
+ private void testSetGetCurrentFunctions_Matched(long functions) {
+ setCurrentFunctions(functions);
+ assertEquals("CurrentFunctions mismatched: ", functions, getCurrentFunctions());
+ }
+
+ private void testGetCurrentFunctionsMock_Matched(long functions) {
+ try {
+ when(mMockUsbService.getCurrentFunctions()).thenReturn(functions);
+
+ assertEquals("CurrentFunctions mismatched: ", functions, getCurrentFunctions());
+ } catch (RemoteException remEx) {
+ Log.w(TAG, "RemoteException");
+ }
+ }
+
+ private void testSetCurrentFunctionsMock_Matched(long functions) {
+ try {
+ setCurrentFunctions(functions);
+
+ verify(mMockUsbService).setCurrentFunctions(eq(functions));
+ } catch (RemoteException remEx) {
+ Log.w(TAG, "RemoteException");
+ }
+ }
+
+ public void testGetCurrentFunctionsSysEx() throws Exception {
+ getCurrentFunctionsSys();
+ }
+
+ public void testSetCurrentFunctionsSysEx(long functions) throws Exception {
+ setCurrentFunctionsSys(functions);
+ }
+
+ public void testGetCurrentFunctionsEx() throws Exception {
+ getCurrentFunctions();
+
+ verify(mMockUsbService).getCurrentFunctions();
+ }
+
+ public void testSetCurrentFunctionsEx(long functions) throws Exception {
+ setCurrentFunctions(functions);
+
+ verify(mMockUsbService).setCurrentFunctions(eq(functions));
+ }
+
+ public void testGetCurrentFunctions_shouldMatched() {
+ testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NONE);
+ testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MTP);
+ testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_PTP);
+ testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MIDI);
+ testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS);
+ }
+
+ public void testSetCurrentFunctions_shouldMatched() {
+ testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NONE);
+ testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MTP);
+ testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_PTP);
+ testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MIDI);
+ testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS);
+ }
+}
diff --git a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java
new file mode 100644
index 0000000..8b21763
--- /dev/null
+++ b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.usbtest;
+
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.android.server.usblib.UsbManagerTestLib;
+
+/**
+ * Unit tests for {@link android.hardware.usb.UsbManager}.
+ * Note: MUST claimed MANAGE_USB permission in Manifest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UsbManagerApiTest {
+ private Context mContext;
+
+ private final UsbManagerTestLib mUsbManagerTestLib =
+ new UsbManagerTestLib(mContext = InstrumentationRegistry.getContext());
+
+ /**
+ * Verify NO SecurityException
+ * Go through System Server
+ */
+ @Test
+ public void testUsbApi_GetCurrentFunctionsSys_shouldNoSecurityException() throws Exception {
+ mUsbManagerTestLib.testGetCurrentFunctionsSysEx();
+ }
+
+ /**
+ * Verify NO SecurityException
+ * Go through System Server
+ */
+ @Test
+ public void testUsbApi_SetCurrentFunctionsSys_shouldNoSecurityException() throws Exception {
+ mUsbManagerTestLib.testSetCurrentFunctionsSysEx(UsbManager.FUNCTION_NONE);
+ }
+
+ /**
+ * Verify NO SecurityException
+ * Go through Direct API, will not be denied by @RequiresPermission annotation
+ */
+ @Test
+ public void testUsbApi_GetCurrentFunctions_shouldNoSecurityException() throws Exception {
+ mUsbManagerTestLib.testGetCurrentFunctionsEx();
+ }
+
+ /**
+ * Verify NO SecurityException
+ * Go through Direct API, will not be denied by @RequiresPermission annotation
+ */
+ @Test
+ public void testUsbApi_SetCurrentFunctions_shouldNoSecurityException() throws Exception {
+ mUsbManagerTestLib.testSetCurrentFunctionsEx(UsbManager.FUNCTION_NONE);
+ }
+
+ /**
+ * Verify API path from UsbManager to UsbService
+ */
+ @Test
+ public void testUsbApi_GetCurrentFunctions_shouldMatched() {
+ mUsbManagerTestLib.testGetCurrentFunctions_shouldMatched();
+ }
+
+ /**
+ * Verify API path from UsbManager to UsbService
+ */
+ @Test
+ public void testUsbApi_SetCurrentFunctions_shouldMatched() {
+ mUsbManagerTestLib.testSetCurrentFunctions_shouldMatched();
+ }
+}
diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp
index 1b2cf63..7c2be9b 100644
--- a/tests/UsbTests/Android.bp
+++ b/tests/UsbTests/Android.bp
@@ -26,6 +26,7 @@
"services.net",
"services.usb",
"truth-prebuilt",
+ "UsbManagerTestLib",
],
jni_libs: ["libdexmakerjvmtiagent"],
certificate: "platform",
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
new file mode 100644
index 0000000..a0fd9d4
--- /dev/null
+++ b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.usb;
+
+import android.content.Context;
+import android.hardware.usb.UsbManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.android.server.usblib.UsbManagerTestLib;
+
+/**
+ * Unit tests for {@link android.hardware.usb.UsbManager}.
+ * Note: NOT claimed MANAGE_USB permission in Manifest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UsbManagerNoPermTest {
+ private Context mContext;
+
+ private final UsbManagerTestLib mUsbManagerTestLib =
+ new UsbManagerTestLib(mContext = InstrumentationRegistry.getContext());
+
+ /**
+ * Verify SecurityException resulting from required permissions missing
+ * Go through System Server
+ */
+ @Test(expected = SecurityException.class)
+ public void testUsbApi_GetCurrentFunctionsSys_OnSecurityException() throws Exception {
+ mUsbManagerTestLib.testGetCurrentFunctionsSysEx();
+ }
+
+ /**
+ * Verify SecurityException resulting from required permissions missing
+ * Go through System Server
+ */
+ @Test(expected = SecurityException.class)
+ public void testUsbApi_SetCurrentFunctionsSys_OnSecurityException() throws Exception {
+ mUsbManagerTestLib.testSetCurrentFunctionsSysEx(UsbManager.FUNCTION_NONE);
+ }
+
+ /**
+ * Verify SecurityException resulting from required permissions missing
+ * Go through Direct API, will not be denied by @RequiresPermission annotation
+ */
+ @Test(expected = SecurityException.class)
+ @Ignore
+ public void testUsbApi_GetCurrentFunctions_OnSecurityException() throws Exception {
+ mUsbManagerTestLib.testGetCurrentFunctionsEx();
+ }
+
+ /**
+ * Verify SecurityException resulting from required permissions missing
+ * Go through Direct API, will not be denied by @RequiresPermission annotation
+ */
+ @Test(expected = SecurityException.class)
+ @Ignore
+ public void testUsbApi_SetCurrentFunctions_OnSecurityException() throws Exception {
+ mUsbManagerTestLib.testSetCurrentFunctionsEx(UsbManager.FUNCTION_NONE);
+ }
+}
diff --git a/tests/libs-permissions/Android.bp b/tests/libs-permissions/Android.bp
index 330bfc9..66a1f83 100644
--- a/tests/libs-permissions/Android.bp
+++ b/tests/libs-permissions/Android.bp
@@ -2,6 +2,7 @@
name: "com.android.test.libs.product",
installable: true,
product_specific: true,
+ sdk_version: "current",
srcs: ["product/java/**/*.java"],
required: ["com.android.test.libs.product.xml"],
}
diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
index 4fa93ee..8ccf007 100644
--- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
+++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
@@ -28,29 +28,27 @@
/**
* Easy Connect (DPP) Status Callback. Use this callback to get status updates (success, failure,
- * progress) from the Easy Connect operation started with
- * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Executor,
- * EasyConnectStatusCallback)} or {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
- * Executor, EasyConnectStatusCallback)}
- *
- * @hide
+ * progress) from the Easy Connect operations.
*/
-@SystemApi
public abstract class EasyConnectStatusCallback {
/**
* Easy Connect R1 Success event: Configuration sent (Configurator mode). This is the last
* and final Easy Connect event when either the local device or remote device implement R1.
* If both devices implement R2, this event will never be received, and the
- * {@link EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED} will be received.
+ * {@link #EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED} will be received.
+ * @hide
*/
+ @SystemApi
public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0;
/**
* East Connect R2 Success event: Configuration applied by Enrollee (Configurator mode).
* This is the last and final Easy Connect event when both the local device and remote device
* implement R2. If either the local device or remote device implement R1, this event will never
- * be received, and the {@link EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT} will be received.
+ * be received, and the {@link #EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT} will be received.
+ * @hide
*/
+ @SystemApi
public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1;
/** @hide */
@@ -64,22 +62,30 @@
/**
* Easy Connect Progress event: Initial authentication with peer succeeded.
+ * @hide
*/
+ @SystemApi
public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0;
/**
* Easy Connect Progress event: Peer requires more time to process bootstrapping.
+ * @hide
*/
+ @SystemApi
public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1;
/**
* Easy Connect R2 Progress event: Configuration sent to Enrollee, waiting for response
+ * @hide
*/
+ @SystemApi
public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2;
/**
* Easy Connect R2 Progress event: Configuration accepted by Enrollee, waiting for response
+ * @hide
*/
+ @SystemApi
public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3;
/** @hide */
@@ -174,6 +180,12 @@
public @interface EasyConnectFailureStatusCode {
}
+ /** @hide */
+ @SystemApi
+ public EasyConnectStatusCallback() {
+ // Fully-static utility classes must not have constructor
+ }
+
/**
* Called when local Easy Connect Enrollee successfully receives a new Wi-Fi configuration from
* the
@@ -185,7 +197,9 @@
* EasyConnectStatusCallback)} .
*
* @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
+ * @hide
*/
+ @SystemApi
public abstract void onEnrolleeSuccess(int newNetworkId);
/**
@@ -197,7 +211,9 @@
* int, Executor,EasyConnectStatusCallback)}.
*
* @param code Easy Connect success status code.
+ * @hide
*/
+ @SystemApi
public abstract void onConfiguratorSuccess(@EasyConnectSuccessStatusCode int code);
/**
@@ -205,7 +221,9 @@
* end of the current Easy Connect session, and no further callbacks will be called.
*
* @param code Easy Connect failure status code.
+ * @hide
*/
+ @SystemApi
public void onFailure(@EasyConnectFailureStatusCode int code) {}
/**
@@ -227,7 +245,9 @@
* @param operatingClassArray Array of bands the Enrollee supports as expressed as the Global
* Operating Class, see Table E-4 in IEEE Std 802.11-2016 - Global
* operating classes.
+ * @hide
*/
+ @SystemApi
public void onFailure(@EasyConnectFailureStatusCode int code, @Nullable String ssid,
@NonNull SparseArray<int[]> channelListArray, @NonNull int[] operatingClassArray) {
onFailure(code);
@@ -238,6 +258,8 @@
* to show progress.
*
* @param code Easy Connect progress status code.
+ * @hide
*/
+ @SystemApi
public abstract void onProgress(@EasyConnectProgressStatusCode int code);
}
diff --git a/wifi/java/android/net/wifi/ISoftApCallback.aidl b/wifi/java/android/net/wifi/ISoftApCallback.aidl
index 452a655..482b491 100644
--- a/wifi/java/android/net/wifi/ISoftApCallback.aidl
+++ b/wifi/java/android/net/wifi/ISoftApCallback.aidl
@@ -15,6 +15,7 @@
*/
package android.net.wifi;
+import android.net.wifi.SoftApCapability;
import android.net.wifi.SoftApInfo;
import android.net.wifi.WifiClient;
@@ -51,4 +52,12 @@
* @param softApInfo is the softap information. {@link SoftApInfo}
*/
void onInfoChanged(in SoftApInfo softApInfo);
+
+
+ /**
+ * Service to manager callback providing information of softap.
+ *
+ * @param capability is the softap capability. {@link SoftApCapability}
+ */
+ void onCapabilityChanged(in SoftApCapability capability);
}
diff --git a/wifi/java/android/net/wifi/SoftApCapability.aidl b/wifi/java/android/net/wifi/SoftApCapability.aidl
new file mode 100644
index 0000000..bf30709
--- /dev/null
+++ b/wifi/java/android/net/wifi/SoftApCapability.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable SoftApCapability;
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
new file mode 100644
index 0000000..506f493
--- /dev/null
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A class representing capability of the SoftAp.
+ * {@see WifiManager}
+ *
+ * @hide
+ */
+@SystemApi
+public final class SoftApCapability implements Parcelable {
+
+ private int mMaximumSupportedClientNumber;
+
+ /**
+ * Get the maximum supported client numbers which AP resides on.
+ */
+ public int getMaxSupportedClients() {
+ return mMaximumSupportedClientNumber;
+ }
+
+ /**
+ * Set the maximum supported client numbers which AP resides on.
+ * @hide
+ */
+ public void setMaxSupportedClients(int maxClient) {
+ mMaximumSupportedClientNumber = maxClient;
+ }
+
+ /**
+ * @hide
+ */
+ public SoftApCapability(@Nullable SoftApCapability source) {
+ if (source != null) {
+ mMaximumSupportedClientNumber = source.mMaximumSupportedClientNumber;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public SoftApCapability() {
+ }
+
+ @Override
+ /** Implement the Parcelable interface. */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ /** Implement the Parcelable interface */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mMaximumSupportedClientNumber);
+ }
+
+ @NonNull
+ /** Implement the Parcelable interface */
+ public static final Creator<SoftApCapability> CREATOR = new Creator<SoftApCapability>() {
+ public SoftApCapability createFromParcel(Parcel in) {
+ SoftApCapability capability = new SoftApCapability();
+ capability.mMaximumSupportedClientNumber = in.readInt();
+ return capability;
+ }
+
+ public SoftApCapability[] newArray(int size) {
+ return new SoftApCapability[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder sbuf = new StringBuilder();
+ sbuf.append("MaximumSupportedClientNumber=").append(mMaximumSupportedClientNumber);
+ return sbuf.toString();
+ }
+
+ @Override
+ public boolean equals(@NonNull Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SoftApCapability)) return false;
+ SoftApCapability capability = (SoftApCapability) o;
+ return mMaximumSupportedClientNumber == capability.mMaximumSupportedClientNumber;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMaximumSupportedClientNumber);
+ }
+}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index fd8a924..cd2826b 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -164,6 +164,11 @@
private final int mChannel;
/**
+ * The maximim allowed number of clients that can associate to the AP.
+ */
+ private final int mMaxNumberOfClients;
+
+ /**
* The operating security type of the AP.
* One of the security types from {@link @SecurityType}
*/
@@ -191,7 +196,7 @@
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
@Nullable String wpa2Passphrase, boolean hiddenSsid, @BandType int band, int channel,
- @SecurityType int securityType) {
+ @SecurityType int securityType, int maxNumberOfClients) {
mSsid = ssid;
mBssid = bssid;
mWpa2Passphrase = wpa2Passphrase;
@@ -199,6 +204,7 @@
mBand = band;
mChannel = channel;
mSecurityType = securityType;
+ mMaxNumberOfClients = maxNumberOfClients;
}
@Override
@@ -216,13 +222,14 @@
&& mHiddenSsid == other.mHiddenSsid
&& mBand == other.mBand
&& mChannel == other.mChannel
- && mSecurityType == other.mSecurityType;
+ && mSecurityType == other.mSecurityType
+ && mMaxNumberOfClients == other.mMaxNumberOfClients;
}
@Override
public int hashCode() {
return Objects.hash(mSsid, mBssid, mWpa2Passphrase, mHiddenSsid,
- mBand, mChannel, mSecurityType);
+ mBand, mChannel, mSecurityType, mMaxNumberOfClients);
}
@Override
@@ -236,6 +243,7 @@
sbuf.append(" \n Band =").append(mBand);
sbuf.append(" \n Channel =").append(mChannel);
sbuf.append(" \n SecurityType=").append(getSecurityType());
+ sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
return sbuf.toString();
}
@@ -248,6 +256,7 @@
dest.writeInt(mBand);
dest.writeInt(mChannel);
dest.writeInt(mSecurityType);
+ dest.writeInt(mMaxNumberOfClients);
}
@Override
@@ -262,7 +271,8 @@
return new SoftApConfiguration(
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
- in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt());
+ in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
+ in.readInt());
}
@Override
@@ -282,7 +292,7 @@
/**
* Returns MAC address set to be BSSID for the AP.
- * {@link #setBssid(MacAddress)}.
+ * {@link Builder#setBssid(MacAddress)}.
*/
@Nullable
public MacAddress getBssid() {
@@ -291,7 +301,7 @@
/**
* Returns String set to be passphrase for the WPA2-PSK AP.
- * {@link #setWpa2Passphrase(String)}.
+ * {@link Builder#setWpa2Passphrase(String)}.
*/
@Nullable
public String getWpa2Passphrase() {
@@ -301,7 +311,7 @@
/**
* Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
* not (false: broadcasts its SSID) for the AP.
- * {@link #setHiddenSsid(boolean)}.
+ * {@link Builder#setHiddenSsid(boolean)}.
*/
public boolean isHiddenSsid() {
return mHiddenSsid;
@@ -309,7 +319,7 @@
/**
* Returns {@link BandType} set to be the band for the AP.
- * {@link #setBand(@BandType int)}.
+ * {@link Builder#setBand(@BandType int)}.
*/
public @BandType int getBand() {
return mBand;
@@ -317,7 +327,7 @@
/**
* Returns Integer set to be the channel for the AP.
- * {@link #setChannel(int)}.
+ * {@link Builder#setChannel(int)}.
*/
public int getChannel() {
return mChannel;
@@ -333,6 +343,14 @@
}
/**
+ * Returns the maximum number of clients that can associate to the AP.
+ * {@link Builder#setMaxNumberOfClients(int)}.
+ */
+ public int getMaxNumberOfClients() {
+ return mMaxNumberOfClients;
+ }
+
+ /**
* Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
* Soft AP.
*
@@ -346,6 +364,7 @@
private boolean mHiddenSsid;
private int mBand;
private int mChannel;
+ private int mMaxNumberOfClients;
private int setSecurityType() {
int securityType = SECURITY_TYPE_OPEN;
@@ -369,6 +388,7 @@
mHiddenSsid = false;
mBand = BAND_2GHZ;
mChannel = 0;
+ mMaxNumberOfClients = 0;
}
/**
@@ -383,6 +403,7 @@
mHiddenSsid = other.mHiddenSsid;
mBand = other.mBand;
mChannel = other.mChannel;
+ mMaxNumberOfClients = other.mMaxNumberOfClients;
}
/**
@@ -393,7 +414,7 @@
@NonNull
public SoftApConfiguration build() {
return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase,
- mHiddenSsid, mBand, mChannel, setSecurityType());
+ mHiddenSsid, mBand, mChannel, setSecurityType(), mMaxNumberOfClients);
}
/**
@@ -523,5 +544,37 @@
mChannel = channel;
return this;
}
+
+ /**
+ * Specifies the maximum number of clients that can associate to the AP.
+ *
+ * The maximum number of clients (STAs) which can associate to the AP.
+ * The AP will reject association from any clients above this number.
+ * Specify a value of 0 to have the framework automatically use the maximum number
+ * which the device can support (based on hardware and carrier constraints).
+ * <p>
+ * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
+ * {@link SoftApCapability#getMaxSupportedClients} to get the maximum number of clients
+ * which the device supports (based on hardware and carrier constraints).
+ *
+ * <p>
+ * <li>If not set, defaults to 0.</li>
+ *
+ * This method requires hardware support. If the method is used to set a
+ * non-zero {@code maxNumberOfClients} value then
+ * {@link WifiManager#startTetheredHotspot} will report error code
+ * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}.
+ *
+ * @param maxNumberOfClients maximum client number of the AP.
+ * @return Builder for chaining.
+ */
+ @NonNull
+ public Builder setMaxNumberOfClients(int maxNumberOfClients) {
+ if (maxNumberOfClients < 0) {
+ throw new IllegalArgumentException("maxNumberOfClients should be not negative");
+ }
+ mMaxNumberOfClients = maxNumberOfClients;
+ return this;
+ }
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 5ab0583..7693f9a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -549,6 +549,7 @@
* currently support general and no_channel
* @see #SAP_START_FAILURE_GENERAL
* @see #SAP_START_FAILURE_NO_CHANNEL
+ * @see #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION
*
* @hide
*/
@@ -652,6 +653,7 @@
@IntDef(flag = false, prefix = { "SAP_START_FAILURE_" }, value = {
SAP_START_FAILURE_GENERAL,
SAP_START_FAILURE_NO_CHANNEL,
+ SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SapStartFailure {}
@@ -673,6 +675,15 @@
@SystemApi
public static final int SAP_START_FAILURE_NO_CHANNEL = 1;
+ /**
+ * If Wi-Fi AP start failed, this reason code means that the specified configuration
+ * is not supported by the current HAL version.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"IFACE_IP_MODE_"}, value = {
@@ -3431,6 +3442,16 @@
default void onInfoChanged(@NonNull SoftApInfo softApInfo) {
// Do nothing: can be updated to add SoftApInfo details (e.g. channel) to the UI.
}
+
+ /**
+ * Called when capability of softap changes.
+ *
+ * @param softApCapability is the softap capability. {@link SoftApCapability}
+ */
+ default void onCapabilityChanged(@NonNull SoftApCapability softApCapability) {
+ // Do nothing: can be updated to add SoftApCapability details (e.g. meximum supported
+ // client number) to the UI.
+ }
}
/**
@@ -3484,6 +3505,19 @@
mCallback.onInfoChanged(softApInfo);
});
}
+
+ @Override
+ public void onCapabilityChanged(SoftApCapability capability) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "SoftApCallbackProxy: onCapabilityChanged: SoftApCapability="
+ + capability);
+ }
+
+ Binder.clearCallingIdentity();
+ mExecutor.execute(() -> {
+ mCallback.onCapabilityChanged(capability);
+ });
+ }
}
/**
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 8fedda4..8badcc0 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -724,21 +724,6 @@
public int min24GHzRssi;
/** Minimum 6GHz RSSI for a BSSID to be considered */
public int min6GHzRssi;
- /** Maximum score that a network can have before bonuses */
- public int initialScoreMax;
- /**
- * Only report when there is a network's score this much higher
- * than the current connection.
- */
- public int currentConnectionBonus;
- /** score bonus for all networks with the same network flag */
- public int sameNetworkBonus;
- /** score bonus for networks that are not open */
- public int secureBonus;
- /** 5GHz RSSI score bonus (applied to all 5GHz networks) */
- public int band5GHzBonus;
- /** 6GHz RSSI score bonus (applied to all 5GHz networks) */
- public int band6GHzBonus;
/** Pno Network filter list */
public PnoNetwork[] networkList;
@@ -753,12 +738,6 @@
dest.writeInt(min5GHzRssi);
dest.writeInt(min24GHzRssi);
dest.writeInt(min6GHzRssi);
- dest.writeInt(initialScoreMax);
- dest.writeInt(currentConnectionBonus);
- dest.writeInt(sameNetworkBonus);
- dest.writeInt(secureBonus);
- dest.writeInt(band5GHzBonus);
- dest.writeInt(band6GHzBonus);
if (networkList != null) {
dest.writeInt(networkList.length);
for (int i = 0; i < networkList.length; i++) {
@@ -781,12 +760,6 @@
settings.min5GHzRssi = in.readInt();
settings.min24GHzRssi = in.readInt();
settings.min6GHzRssi = in.readInt();
- settings.initialScoreMax = in.readInt();
- settings.currentConnectionBonus = in.readInt();
- settings.sameNetworkBonus = in.readInt();
- settings.secureBonus = in.readInt();
- settings.band5GHzBonus = in.readInt();
- settings.band6GHzBonus = in.readInt();
int numNetworks = in.readInt();
settings.networkList = new PnoNetwork[numNetworks];
for (int i = 0; i < numNetworks; i++) {
diff --git a/wifi/java/android/net/wifi/wificond/SingleScanSettings.java b/wifi/java/android/net/wifi/wificond/SingleScanSettings.java
index 8065c01..8c341b8 100644
--- a/wifi/java/android/net/wifi/wificond/SingleScanSettings.java
+++ b/wifi/java/android/net/wifi/wificond/SingleScanSettings.java
@@ -16,7 +16,6 @@
package android.net.wifi.wificond;
-import android.net.wifi.IWifiScannerImpl;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index 94f1212..283f2dd 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -24,14 +24,6 @@
import android.annotation.SystemService;
import android.app.AlarmManager;
import android.content.Context;
-import android.net.wifi.IApInterface;
-import android.net.wifi.IApInterfaceEventCallback;
-import android.net.wifi.IClientInterface;
-import android.net.wifi.IPnoScanEvent;
-import android.net.wifi.IScanEvent;
-import android.net.wifi.ISendMgmtFrameEvent;
-import android.net.wifi.IWifiScannerImpl;
-import android.net.wifi.IWificond;
import android.net.wifi.SoftApInfo;
import android.net.wifi.WifiAnnotations;
import android.net.wifi.WifiScanner;
diff --git a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
new file mode 100644
index 0000000..9cb221d
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcel;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.SoftApCapability}.
+ */
+@SmallTest
+public class SoftApCapabilityTest {
+
+ /**
+ * Verifies copy constructor.
+ */
+ @Test
+ public void testCopyOperator() throws Exception {
+ SoftApCapability capability = new SoftApCapability();
+ capability.setMaxSupportedClients(10);
+
+ SoftApCapability copiedCapability = new SoftApCapability(capability);
+
+ assertEquals(capability, copiedCapability);
+ assertEquals(capability.hashCode(), copiedCapability.hashCode());
+ }
+
+ /**
+ * Verifies parcel serialization/deserialization.
+ */
+ @Test
+ public void testParcelOperation() throws Exception {
+ SoftApCapability capability = new SoftApCapability();
+ capability.setMaxSupportedClients(10);
+
+ Parcel parcelW = Parcel.obtain();
+ capability.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ SoftApCapability fromParcel = SoftApCapability.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(capability, fromParcel);
+ assertEquals(capability.hashCode(), fromParcel.hashCode());
+ }
+
+}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 60125e3..1f60103 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -50,6 +50,7 @@
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
assertThat(original.getChannel()).isEqualTo(0);
assertThat(original.isHiddenSsid()).isEqualTo(false);
+ assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
@@ -73,7 +74,7 @@
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
assertThat(original.getChannel()).isEqualTo(0);
assertThat(original.isHiddenSsid()).isEqualTo(false);
-
+ assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
@@ -87,12 +88,13 @@
}
@Test
- public void testWpa2WithBandAndChannelAndHiddenNetwork() {
+ public void testWpa2WithAllFieldCustomized() {
SoftApConfiguration original = new SoftApConfiguration.Builder()
.setWpa2Passphrase("secretsecret")
.setBand(SoftApConfiguration.BAND_ANY)
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
+ .setMaxNumberOfClients(10)
.build();
assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
assertThat(original.getSecurityType()).isEqualTo(
@@ -100,7 +102,7 @@
assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
assertThat(original.getChannel()).isEqualTo(149);
assertThat(original.isHiddenSsid()).isEqualTo(true);
-
+ assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8216611..d8bc134 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -877,6 +877,25 @@
verify(mSoftApCallback).onInfoChanged(testSoftApInfo);
}
+
+ /*
+ * Verify client-provided callback is being called through callback proxy
+ */
+ @Test
+ public void softApCallbackProxyCallsOnCapabilityChanged() throws Exception {
+ SoftApCapability testSoftApCapability = new SoftApCapability();
+ testSoftApCapability.setMaxSupportedClients(10);
+ ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
+ ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
+ mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
+ verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
+ anyInt());
+
+ callbackCaptor.getValue().onCapabilityChanged(testSoftApCapability);
+ mLooper.dispatchAll();
+ verify(mSoftApCallback).onCapabilityChanged(testSoftApCapability);
+ }
+
/*
* Verify client-provided callback is being called through callback proxy on multiple events
*/
@@ -885,6 +904,8 @@
SoftApInfo testSoftApInfo = new SoftApInfo();
testSoftApInfo.setFrequency(TEST_AP_FREQUENCY);
testSoftApInfo.setBandwidth(TEST_AP_BANDWIDTH);
+ SoftApCapability testSoftApCapability = new SoftApCapability();
+ testSoftApCapability.setMaxSupportedClients(10);
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback);
@@ -896,12 +917,15 @@
callbackCaptor.getValue().onConnectedClientsChanged(testClients);
callbackCaptor.getValue().onInfoChanged(testSoftApInfo);
callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
+ callbackCaptor.getValue().onCapabilityChanged(testSoftApCapability);
+
mLooper.dispatchAll();
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLING, 0);
verify(mSoftApCallback).onConnectedClientsChanged(testClients);
verify(mSoftApCallback).onInfoChanged(testSoftApInfo);
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
+ verify(mSoftApCallback).onCapabilityChanged(testSoftApCapability);
}
/*
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index fa4f711..1af0bcb 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -80,12 +80,6 @@
private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60;
private static final int TEST_PNOSETTINGS_MIN_2GHZ_RSSI = -70;
private static final int TEST_PNOSETTINGS_MIN_6GHZ_RSSI = -55;
- private static final int TEST_PNOSETTINGS_INITIAL_SCORE_MAX = 50;
- private static final int TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS = 10;
- private static final int TEST_PNOSETTINGS_SAME_NETWORK_BONUS = 11;
- private static final int TEST_PNOSETTINGS_SECURE_BONUS = 12;
- private static final int TEST_PNOSETTINGS_BAND_5GHZ_BONUS = 13;
- private static final int TEST_PNOSETTINGS_BAND_6GHZ_BONUS = 15;
private static final String TEST_SSID_1 = "TEST1";
private static final String TEST_SSID_2 = "TEST2";
private static final int[] TEST_FREQUENCIES_1 = {};
@@ -186,12 +180,6 @@
pnoSettings.min5GHzRssi = TEST_PNOSETTINGS_MIN_5GHZ_RSSI;
pnoSettings.min24GHzRssi = TEST_PNOSETTINGS_MIN_2GHZ_RSSI;
pnoSettings.min6GHzRssi = TEST_PNOSETTINGS_MIN_6GHZ_RSSI;
- pnoSettings.initialScoreMax = TEST_PNOSETTINGS_INITIAL_SCORE_MAX;
- pnoSettings.currentConnectionBonus = TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS;
- pnoSettings.sameNetworkBonus = TEST_PNOSETTINGS_SAME_NETWORK_BONUS;
- pnoSettings.secureBonus = TEST_PNOSETTINGS_SECURE_BONUS;
- pnoSettings.band5GHzBonus = TEST_PNOSETTINGS_BAND_5GHZ_BONUS;
- pnoSettings.band6GHzBonus = TEST_PNOSETTINGS_BAND_6GHZ_BONUS;
Parcel parcel = Parcel.obtain();
pnoSettings.writeToParcel(parcel, 0);
@@ -205,14 +193,6 @@
assertEquals(TEST_PNOSETTINGS_MIN_5GHZ_RSSI, pnoSettingsDeserialized.min5GHzRssi);
assertEquals(TEST_PNOSETTINGS_MIN_2GHZ_RSSI, pnoSettingsDeserialized.min24GHzRssi);
assertEquals(TEST_PNOSETTINGS_MIN_6GHZ_RSSI, pnoSettingsDeserialized.min6GHzRssi);
- assertEquals(TEST_PNOSETTINGS_INITIAL_SCORE_MAX, pnoSettingsDeserialized.initialScoreMax);
- assertEquals(TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS,
- pnoSettingsDeserialized.currentConnectionBonus);
- assertEquals(TEST_PNOSETTINGS_SAME_NETWORK_BONUS,
- pnoSettingsDeserialized.sameNetworkBonus);
- assertEquals(TEST_PNOSETTINGS_SECURE_BONUS, pnoSettingsDeserialized.secureBonus);
- assertEquals(TEST_PNOSETTINGS_BAND_5GHZ_BONUS, pnoSettingsDeserialized.band5GHzBonus);
- assertEquals(TEST_PNOSETTINGS_BAND_6GHZ_BONUS, pnoSettingsDeserialized.band6GHzBonus);
// Test parsing of PnoNetwork
assertEquals(pnoSettings.networkList.length, pnoSettingsDeserialized.networkList.length);
diff --git a/wifi/tests/src/android/net/wifi/wificond/SingleScanSettingsTest.java b/wifi/tests/src/android/net/wifi/wificond/SingleScanSettingsTest.java
index ef59839..f20ec47 100644
--- a/wifi/tests/src/android/net/wifi/wificond/SingleScanSettingsTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/SingleScanSettingsTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertEquals;
-import android.net.wifi.IWifiScannerImpl;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index 68e5336..f3867c1 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -38,14 +38,6 @@
import android.app.AlarmManager;
import android.app.test.TestAlarmManager;
import android.content.Context;
-import android.net.wifi.IApInterface;
-import android.net.wifi.IApInterfaceEventCallback;
-import android.net.wifi.IClientInterface;
-import android.net.wifi.IPnoScanEvent;
-import android.net.wifi.IScanEvent;
-import android.net.wifi.ISendMgmtFrameEvent;
-import android.net.wifi.IWifiScannerImpl;
-import android.net.wifi.IWificond;
import android.net.wifi.SoftApInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiScanner;