diff options
291 files changed, 7663 insertions, 2969 deletions
diff --git a/Android.bp b/Android.bp index 536f6885bd13..08b5b4aeed21 100644 --- a/Android.bp +++ b/Android.bp @@ -353,9 +353,6 @@ java_library { "com.android.sysprop.apex", "PlatformProperties", ], - aidl: { - include_dirs: ["system/connectivity/wificond/aidl"], - }, sdk_version: "core_platform", installable: false, } @@ -605,7 +602,7 @@ filegroup { java_library { name: "framework-annotations-lib", srcs: [ ":framework-annotations" ], - sdk_version: "current", + sdk_version: "core_current", } filegroup { @@ -699,7 +696,7 @@ java_library { "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 @@ java_library { "core/proto/android/privacy.proto", "core/proto/android/section.proto", ], + sdk_version: "core_current", // Protos have lots of MissingOverride and similar. errorprone: { javacflags: ["-XepDisableAllChecks"], @@ -833,6 +831,7 @@ filegroup { name: "dataloader_aidl", srcs: [ "core/java/android/content/pm/DataLoaderParamsParcel.aidl", + "core/java/android/content/pm/DataLoaderType.aidl", "core/java/android/content/pm/FileSystemControlParcel.aidl", "core/java/android/content/pm/IDataLoaderStatusListener.aidl", "core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl", @@ -1023,645 +1022,6 @@ python_binary_host { ], } -// 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: [ @@ -1777,3 +1137,20 @@ filegroup { "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 000000000000..e373db66925f --- /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 000000000000..78f1b9ca26e5 --- /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/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkext/derive_sdk/derive_sdk.cpp index 7536def60767..0a9711677015 100644 --- a/apex/sdkext/derive_sdk/derive_sdk.cpp +++ b/apex/sdkext/derive_sdk/derive_sdk.cpp @@ -63,6 +63,7 @@ int main(int, char**) { LOG(ERROR) << "failed to parse " << path; continue; } + LOG(INFO) << "Read version " << sdk_version.version() << " from " << path; versions.push_back(sdk_version.version()); } auto itr = std::min_element(versions.begin(), versions.end()); @@ -73,5 +74,6 @@ int main(int, char**) { return EXIT_FAILURE; } + LOG(INFO) << "R extension version is " << prop_value; return EXIT_SUCCESS; } diff --git a/apex/statsd/aidl/android/os/IPullAtomCallback.aidl b/apex/statsd/aidl/android/os/IPullAtomCallback.aidl index 88d3c3e46ff5..ff0b97bb5b84 100644 --- a/apex/statsd/aidl/android/os/IPullAtomCallback.aidl +++ b/apex/statsd/aidl/android/os/IPullAtomCallback.aidl @@ -26,6 +26,6 @@ interface IPullAtomCallback { /** * Initiate a request for a pull for an atom. */ - void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver); + oneway void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver); } diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl index 22a25374e064..5a6118ef81ca 100644 --- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl +++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl @@ -90,4 +90,7 @@ interface IStatsCompanionService { /** Tells StatsCompanionService to tell statsd to register a puller for the given atom id */ oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, in int[] additiveFields, IPullAtomCallback pullerCallback); + + /** Tells StatsCompanionService to tell statsd to unregister a puller for the given atom id */ + oneway void unregisterPullAtomCallback(int atomTag); } diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index cffc6ce6e4df..cce79fae7587 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -215,6 +215,11 @@ interface IStatsd { */ oneway void unregisterPullerCallback(int atomTag, String packageName); + /** + * Unregisters any pullAtomCallback for the given uid/atom. + */ + oneway void unregisterPullAtomCallback(int uid, int atomTag); + /** * The install requires staging. */ diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index 37b07a6380aa..a2b0577fe001 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -24,7 +24,7 @@ java_library { 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/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index c5fd3f2025e7..7ed51caf23dd 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -2752,6 +2752,30 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + @Override + public void unregisterPullAtomCallback(int atomTag) { + synchronized (sStatsdLock) { + // Always remove the puller in SCS. + // If statsd is down, we will not register it when it comes back up. + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + PullerKey key = new PullerKey(callingUid, atomTag); + mPullers.remove(key); + + if (sStatsd == null) { + Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag); + return; + } + try { + sStatsd.unregisterPullAtomCallback(callingUid, atomTag); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + // Statsd related code /** diff --git a/api/current.txt b/api/current.txt index 86aa80b159bd..4d5d1a1c02d3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3955,6 +3955,7 @@ package android.app { public class ActivityManager { method public int addAppTask(@NonNull android.app.Activity, @NonNull android.content.Intent, @Nullable android.app.ActivityManager.TaskDescription, @NonNull android.graphics.Bitmap); + method public void appNotResponding(@NonNull String); method public boolean clearApplicationUserData(); method public void clearWatchHeapLimit(); method @RequiresPermission(android.Manifest.permission.DUMP) public void dumpPackageState(java.io.FileDescriptor, String); @@ -9972,6 +9973,7 @@ package android.content { 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"; @@ -23742,6 +23744,7 @@ package android.media { 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 @@ -41387,6 +41390,11 @@ package android.security { 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); @@ -45487,6 +45495,7 @@ package android.telephony { ctor public PhoneStateListener(); ctor public PhoneStateListener(@NonNull java.util.concurrent.Executor); method public void onActiveDataSubscriptionIdChanged(int); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int); method public void onCallForwardingIndicatorChanged(boolean); method public void onCallStateChanged(int, String); method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>); @@ -45494,6 +45503,7 @@ package android.telephony { method public void onDataActivity(int); method public void onDataConnectionStateChanged(int); method public void onDataConnectionStateChanged(int, int); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); method public void onMessageWaitingIndicatorChanged(boolean); method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); method public void onServiceStateChanged(android.telephony.ServiceState); @@ -45501,6 +45511,7 @@ package android.telephony { method public void onSignalStrengthsChanged(android.telephony.SignalStrength); method public void onUserMobileDataStateChanged(boolean); field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8 field public static final int LISTEN_CALL_STATE = 32; // 0x20 field public static final int LISTEN_CELL_INFO = 1024; // 0x400 @@ -45508,6 +45519,7 @@ package android.telephony { field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 field public static final int LISTEN_NONE = 0; // 0x0 field @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 @@ -51681,6 +51693,7 @@ package android.view { method public boolean isScrollContainer(); method public boolean isScrollbarFadingEnabled(); method @android.view.ViewDebug.ExportedProperty public boolean isSelected(); + method public final boolean isShowingLayoutBounds(); method public boolean isShown(); method @android.view.ViewDebug.ExportedProperty public boolean isSoundEffectsEnabled(); method public final boolean isTemporarilyDetached(); @@ -55256,6 +55269,7 @@ package android.view.textclassifier { 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 @@ -55282,6 +55296,7 @@ package android.view.textclassifier { 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; @@ -55293,6 +55308,7 @@ package android.view.textclassifier { 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/removed.txt b/api/removed.txt index 8b30d0a5cf39..fb6d57694c78 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -435,15 +435,6 @@ package android.provider { field @Deprecated public static final String TIMESTAMP = "timestamp"; } - public final class MediaStore { - method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context); - method @Deprecated public static boolean getIncludePending(@NonNull android.net.Uri); - method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); - method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); - method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long); - method @Deprecated public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri); - } - public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns { field public static final String ALBUM = "album"; field public static final String ARTIST = "artist"; diff --git a/api/system-current.txt b/api/system-current.txt index 2d0213fa3c0f..f2433dec4ef9 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -260,7 +260,6 @@ package android { public static final class R.drawable { field public static final int ic_info = 17301684; // 0x10800b4 - field public static final int stat_notify_wifi_in_range = 17301685; // 0x10800b5 } public static final class R.raw { @@ -281,9 +280,6 @@ package android { field public static final int config_helpIntentNameKey = 17039390; // 0x104001e field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c - field public static final int notification_channel_network_alerts = 17039398; // 0x1040026 - field public static final int notification_channel_network_available = 17039399; // 0x1040027 - field public static final int notification_channel_network_status = 17039397; // 0x1040025 } public static final class R.style { @@ -1934,10 +1930,12 @@ package android.content.pm { } public class DataLoaderParams { - ctor public DataLoaderParams(@NonNull String, @NonNull String, @Nullable java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>); + method @NonNull public static final android.content.pm.DataLoaderParams forIncremental(@NonNull android.content.ComponentName, @NonNull String, @Nullable java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>); + method @NonNull public static final android.content.pm.DataLoaderParams forStreaming(@NonNull android.content.ComponentName, @NonNull String); + method @NonNull public final String getArguments(); + method @NonNull public final android.content.ComponentName getComponentName(); method @NonNull public final java.util.Map<java.lang.String,android.os.ParcelFileDescriptor> getDynamicArgs(); - method @NonNull public final String getPackageName(); - method @NonNull public final String getStaticArgs(); + method @NonNull public final int getType(); } public final class InstantAppInfo implements android.os.Parcelable { @@ -2050,11 +2048,11 @@ package android.content.pm { public static class PackageInstaller.SessionParams implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean); method @Deprecated public void setAllowDowngrade(boolean); + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); method public void setDontKillApp(boolean); method public void setEnableRollback(boolean); method public void setEnableRollback(boolean, int); method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]); - method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setIncrementalParams(@NonNull android.content.pm.DataLoaderParams); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex(); method public void setInstallAsInstantApp(boolean); method public void setInstallAsVirtualPreload(); @@ -4068,6 +4066,7 @@ package android.media { 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 @@ -5392,11 +5391,15 @@ package android.net.wifi { ctor public EasyConnectStatusCallback(); method public abstract void onConfiguratorSuccess(int); method public abstract void onEnrolleeSuccess(int); - method public abstract void onFailure(int); + 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 @@ -5404,7 +5407,10 @@ package android.net.wifi { 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 field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1 + field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1; // 0x1 field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0 } @@ -8897,6 +8903,7 @@ package android.telephony { } public class CarrierConfigManager { + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName(); method @NonNull public static android.os.PersistableBundle getDefaultConfig(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int, String); @@ -8971,6 +8978,34 @@ package android.telephony { field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService"; } + public abstract class CellIdentity implements android.os.Parcelable { + method @NonNull public abstract android.telephony.CellLocation asCellLocation(); + } + + public final class CellIdentityCdma extends android.telephony.CellIdentity { + method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation(); + } + + public final class CellIdentityGsm extends android.telephony.CellIdentity { + method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + } + + public final class CellIdentityLte extends android.telephony.CellIdentity { + method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + } + + public final class CellIdentityNr extends android.telephony.CellIdentity { + method @NonNull public android.telephony.CellLocation asCellLocation(); + } + + public final class CellIdentityTdscdma extends android.telephony.CellIdentity { + method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + } + + public final class CellIdentityWcdma extends android.telephony.CellIdentity { + method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + } + public final class DataFailCause { field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab field public static final int ACCESS_BLOCK = 2087; // 0x827 @@ -9550,8 +9585,6 @@ package android.telephony { public class PhoneStateListener { method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber); method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber); method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); @@ -9559,8 +9592,6 @@ package android.telephony { method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 @@ -9570,6 +9601,7 @@ package android.telephony { } public final class PreciseCallState implements android.os.Parcelable { + ctor public PreciseCallState(int, int, int, int, int); method public int describeContents(); method public int getBackgroundCallState(); method public int getForegroundCallState(); @@ -9941,6 +9973,7 @@ package android.telephony { 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 503941c86939..08a21609d476 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4359,6 +4359,7 @@ package android.view { method public void setAutofilled(boolean); method public final void setFocusedInCluster(); method public void setIsRootNamespace(boolean); + method public final void setShowingLayoutBounds(boolean); } public class ViewConfiguration { diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index afff61497157..887d17cb7e4b 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -409,7 +409,7 @@ cc_benchmark { // ==== 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/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 4d38ba03b1df..bb3a094af34a 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1320,6 +1320,13 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p return Status::ok(); } +Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) { + ENFORCE_UID(AID_SYSTEM); + VLOG("StatsService::unregisterPullAtomCallback called."); + mPullerManager->UnregisterPullAtomCallback(uid, atomTag); + return Status::ok(); +} + Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn, const int64_t trainVersionCodeIn, const int options, diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 9abf415ad91f..de55ca9c38cd 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -199,6 +199,11 @@ public: virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override; /** + * Binder call to unregister any existing callback for the given uid and atom. + */ + virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override; + + /** * Binder call to log BinaryPushStateChanged atom. */ virtual Status sendBinaryPushStateChangedAtom( diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index 3c6bc2db1d62..883bd28a4d13 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -47,6 +47,7 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) { if (mHasGoodData) { (*data) = mCachedData; StatsdStats::getInstance().notePullFromCache(mTagId); + } return mHasGoodData; } diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index f913118385a3..50896f84da43 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -294,6 +294,11 @@ StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { } bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) { + AutoMutex _l(mLock); + return PullLocked(tagId, data); +} + +bool StatsPullerManager::PullLocked(int tagId, vector<shared_ptr<LogEvent>>* data) { VLOG("Initiating pulling %d", tagId); if (kAllPullAtomInfo.find({.atomTag = tagId}) != kAllPullAtomInfo.end()) { @@ -422,7 +427,7 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { for (const auto& pullInfo : needToPull) { vector<shared_ptr<LogEvent>> data; - bool pullSuccess = Pull(pullInfo.first, &data); + bool pullSuccess = PullLocked(pullInfo.first, &data); if (pullSuccess) { StatsdStats::getInstance().notePullDelay( pullInfo.first, getElapsedRealtimeNs() - elapsedTimeNs); @@ -522,6 +527,12 @@ void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) { kAllPullAtomInfo.erase({.atomTag = atomTag}); } +void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) { + AutoMutex _l(mLock); + StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false); + kAllPullAtomInfo.erase({.atomTag = atomTag}); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index 1bd9f92e4448..349fd47b6c9d 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -125,6 +125,8 @@ public: void UnregisterPullerCallback(int32_t atomTag); + void UnregisterPullAtomCallback(const int uid, const int32_t atomTag); + static std::map<PullerKey, PullAtomInfo> kAllPullAtomInfo; private: @@ -139,6 +141,8 @@ private: // mapping from simple matcher tagId to receivers std::map<int, std::list<ReceiverInfo>> mReceivers; + bool PullLocked(int tagId, vector<std::shared_ptr<LogEvent>>* data); + // locks for data receiver and StatsCompanionService changes Mutex mLock; diff --git a/cmds/svc/src/com/android/commands/svc/DataCommand.java b/cmds/svc/src/com/android/commands/svc/DataCommand.java deleted file mode 100644 index 35510cfd38b1..000000000000 --- a/cmds/svc/src/com/android/commands/svc/DataCommand.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.commands.svc; - -import android.os.ServiceManager; -import android.os.RemoteException; -import android.content.Context; -import com.android.internal.telephony.ITelephony; - -public class DataCommand extends Svc.Command { - public DataCommand() { - super("data"); - } - - public String shortHelp() { - return "Control mobile data connectivity"; - } - - public String longHelp() { - return shortHelp() + "\n" - + "\n" - + "usage: svc data [enable|disable]\n" - + " Turn mobile data on or off.\n\n"; - } - - public void run(String[] args) { - boolean validCommand = false; - if (args.length >= 2) { - boolean flag = false; - if ("enable".equals(args[1])) { - flag = true; - validCommand = true; - } else if ("disable".equals(args[1])) { - flag = false; - validCommand = true; - } - if (validCommand) { - ITelephony phoneMgr - = ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); - try { - if (flag) { - phoneMgr.enableDataConnectivity(); - } else - phoneMgr.disableDataConnectivity(); - } - catch (RemoteException e) { - System.err.println("Mobile data operation failed: " + e); - } - return; - } - } - System.err.println(longHelp()); - } -} diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java index e602e2abbe03..2ed2678bc877 100644 --- a/cmds/svc/src/com/android/commands/svc/Svc.java +++ b/cmds/svc/src/com/android/commands/svc/Svc.java @@ -93,7 +93,6 @@ public class Svc { public static final Command[] COMMANDS = new Command[] { COMMAND_HELP, new PowerCommand(), - new DataCommand(), // `svc wifi` has been migrated to WifiShellCommand new UsbCommand(), new NfcCommand(), diff --git a/cmds/svc/svc b/cmds/svc/svc index 60c95c7ec855..95265e817c1b 100755 --- a/cmds/svc/svc +++ b/cmds/svc/svc @@ -19,6 +19,20 @@ if [ "x$1" == "xwifi" ]; then exit 1 fi +if [ "x$1" == "xdata" ]; then + if [ "x$2" == "xenable" ]; then + exec cmd phone data enable + elif [ "x$2" == "xdisable" ]; then + exec cmd phone data disable + else + echo "Enable/Disable Mobile Data Connectivity" + echo "" + echo "usage: svc data [enable|disable]" + echo "" + fi + exit 1 +fi + export CLASSPATH=/system/framework/svc.jar exec app_process /system/bin com.android.commands.svc.Svc "$@" diff --git a/core/java/android/annotation/CallbackExecutor.java b/core/java/android/annotation/CallbackExecutor.java index 5671a3d2b6d6..4258f730eb16 100644 --- a/core/java/android/annotation/CallbackExecutor.java +++ b/core/java/android/annotation/CallbackExecutor.java @@ -19,9 +19,6 @@ package android.annotation; 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 @@ import java.util.concurrent.Executor; * @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 59d419f78ee0..1d89e31b2b99 100644 --- a/core/java/android/annotation/RequiresPermission.java +++ b/core/java/android/annotation/RequiresPermission.java @@ -15,11 +15,6 @@ */ package android.annotation; -import android.content.Intent; - -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; @@ -27,6 +22,9 @@ import static java.lang.annotation.ElementType.METHOD; 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/> @@ -57,7 +55,8 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; * <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(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 ff581c059935..9e0c2fc18fd9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -8674,7 +8674,6 @@ public class Activity extends ContextThemeWrapper * @hide */ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) - @UnsupportedAppUsage public void registerRemoteAnimations(RemoteAnimationDefinition definition) { try { ActivityTaskManager.getService().registerRemoteAnimations(mToken, definition); @@ -8683,6 +8682,20 @@ public class Activity extends ContextThemeWrapper } } + /** + * Unregisters all remote animations for this activity. + * + * @hide + */ + @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) + public void unregisterRemoteAnimations() { + try { + ActivityTaskManager.getService().unregisterRemoteAnimations(mToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + class HostCallbacks extends FragmentHostCallback<Activity> { public HostCallbacks() { super(Activity.this /*activity*/); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 68bdfae218ae..3f9f7fbbe477 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4547,4 +4547,17 @@ public class ActivityManager { throw e.rethrowFromSystemServer(); } } + + /** + * Method for the app to tell system that it's wedged and would like to trigger an ANR. + * + * @param reason The description of that what happened + */ + public void appNotResponding(@NonNull final String reason) { + try { + getService().appNotResponding(reason); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 112bd303a8b3..e8494c4c5893 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -583,4 +583,9 @@ interface IActivityManager { * unlockProgressListener can be null if monitoring progress is not necessary. */ boolean startUserInForegroundWithListener(int userid, IProgressListener unlockProgressListener); + + /** + * Method for the app to tell system that it's wedged and would like to trigger an ANR. + */ + void appNotResponding(String reason); } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index e2b1b8664d75..df5d6c71756d 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -437,6 +437,11 @@ interface IActivityTaskManager { void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); /** + * Unregisters all remote animations for a specific activity. + */ + void unregisterRemoteAnimations(in IBinder token); + + /** * Registers a remote animation to be run for all activity starts from a certain package during * a short predefined amount of time. */ diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index cd855cf134d9..2d851e0abe6d 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -107,6 +107,20 @@ public final class StatsManager { */ public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; + // Pull atom callback return codes. + /** + * Value indicating that this pull was successful and that the result should be used. + * + * @hide + **/ + public static final int PULL_SUCCESS = 0; + + /** + * Value indicating that this pull was unsuccessful and that the result should not be used. + * @hide + **/ + public static final int PULL_SKIP = 1; + private static final long DEFAULT_COOL_DOWN_NS = 1_000_000_000L; // 1 second. private static final long DEFAULT_TIMEOUT_NS = 10_000_000_000L; // 10 seconds. @@ -508,13 +522,11 @@ public final class StatsManager { * additive fields for mapping isolated to host uids. * @param callback The callback to be invoked when the stats service pulls the atom. * @param executor The executor in which to run the callback - * @throws RemoteException if unsuccessful due to failing to connect to system server. * * @hide */ public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata, - @NonNull StatsPullAtomCallback callback, @NonNull Executor executor) - throws RemoteException, SecurityException { + @NonNull StatsPullAtomCallback callback, @NonNull Executor executor) { long coolDownNs = metadata == null ? DEFAULT_COOL_DOWN_NS : metadata.mCoolDownNs; long timeoutNs = metadata == null ? DEFAULT_TIMEOUT_NS : metadata.mTimeoutNs; int[] additiveFields = metadata == null ? new int[0] : metadata.mAdditiveFields; @@ -522,10 +534,34 @@ public final class StatsManager { additiveFields = new int[0]; } synchronized (sLock) { - IStatsCompanionService service = getIStatsCompanionServiceLocked(); - PullAtomCallbackInternal rec = + try { + IStatsCompanionService service = getIStatsCompanionServiceLocked(); + PullAtomCallbackInternal rec = new PullAtomCallbackInternal(atomTag, callback, executor); - service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields, rec); + service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields, + rec); + } catch (RemoteException e) { + throw new RuntimeException("Unable to register pull callback", e); + } + } + } + + /** + * Unregisters a callback for an atom when that atom is to be pulled. Note that any ongoing + * pulls will still occur. + * + * @param atomTag The tag of the atom of which to unregister + * + * @hide + */ + public void unregisterPullAtomCallback(int atomTag) { + synchronized (sLock) { + try { + IStatsCompanionService service = getIStatsCompanionServiceLocked(); + service.unregisterPullAtomCallback(atomTag); + } catch (RemoteException e) { + throw new RuntimeException("Unable to unregister pull atom callback"); + } } } @@ -544,9 +580,11 @@ public final class StatsManager { public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) { mExecutor.execute(() -> { List<StatsEvent> data = new ArrayList<>(); - boolean success = mCallback.onPullAtom(atomTag, data); + int successInt = mCallback.onPullAtom(atomTag, data); + boolean success = successInt == PULL_SUCCESS; StatsEventParcel[] parcels = new StatsEventParcel[data.size()]; for (int i = 0; i < data.size(); i++) { + parcels[i] = new StatsEventParcel(); parcels[i].buffer = data.get(i).getBytes(); } try { @@ -649,9 +687,9 @@ public final class StatsManager { public interface StatsPullAtomCallback { /** * Pull data for the specified atom tag, filling in the provided list of StatsEvent data. - * @return if the pull was successful + * @return {@link #PULL_SUCCESS} if the pull was successful, or {@link #PULL_SKIP} if not. */ - boolean onPullAtom(int atomTag, List<StatsEvent> data); + int onPullAtom(int atomTag, List<StatsEvent> data); } private class StatsdDeathRecipient implements IBinder.DeathRecipient { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 69c37ec42bd0..31c73b90aace 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -50,6 +50,8 @@ import android.content.ContentCaptureOptions; 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.PermissionControllerManager; 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 @@ public final class SystemServiceRegistry { return new DynamicSystemManager( IDynamicSystemService.Stub.asInterface(b)); }}); + registerService(Context.BATTERY_STATS_SERVICE, BatteryStatsManager.class, new CachedServiceFetcher<BatteryStatsManager>() { @Override @@ -1245,7 +1250,26 @@ public final class SystemServiceRegistry { 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 3ca8f49e4d54..11feccada492 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5061,12 +5061,17 @@ public class DevicePolicyManager { * 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[]) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java b/core/java/android/companion/Association.aidl index bc6b83ba1def..2a28f1f094ad 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java +++ b/core/java/android/companion/Association.aidl @@ -13,18 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.companion; -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; - -@Qualifier -@Documented -@Retention(RUNTIME) -public @interface BgHandler { -} +parcelable Association; diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java new file mode 100644 index 000000000000..3fa6a3e76634 --- /dev/null +++ b/core/java/android/companion/Association.java @@ -0,0 +1,179 @@ +/* + * 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.companion; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Objects; + +/** + * A record indicating that a device with a given address was confirmed by the user to be + * associated to a given companion app + * + * @hide + */ +@DataClass(genEqualsHashCode = true, genToString = true) +public class Association implements Parcelable { + + public final int userId; + public final @NonNull String deviceAddress; + public final @NonNull String companionAppPackage; + + + + + // Code below generated by codegen v1.0.13. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/companion/Association.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public Association( + int userId, + @NonNull String deviceAddress, + @NonNull String companionAppPackage) { + this.userId = userId; + this.deviceAddress = deviceAddress; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, deviceAddress); + this.companionAppPackage = companionAppPackage; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, companionAppPackage); + + // onConstructed(); // You can define this method to get a callback + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "Association { " + + "userId = " + userId + ", " + + "deviceAddress = " + deviceAddress + ", " + + "companionAppPackage = " + companionAppPackage + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(Association other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + Association that = (Association) o; + //noinspection PointlessBooleanExpression + return true + && userId == that.userId + && Objects.equals(deviceAddress, that.deviceAddress) + && Objects.equals(companionAppPackage, that.companionAppPackage); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + userId; + _hash = 31 * _hash + Objects.hashCode(deviceAddress); + _hash = 31 * _hash + Objects.hashCode(companionAppPackage); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(userId); + dest.writeString(deviceAddress); + dest.writeString(companionAppPackage); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected Association(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int _userId = in.readInt(); + String _deviceAddress = in.readString(); + String _companionAppPackage = in.readString(); + + this.userId = _userId; + this.deviceAddress = _deviceAddress; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, deviceAddress); + this.companionAppPackage = _companionAppPackage; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, companionAppPackage); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<Association> CREATOR + = new Parcelable.Creator<Association>() { + @Override + public Association[] newArray(int size) { + return new Association[size]; + } + + @Override + public Association createFromParcel(@NonNull Parcel in) { + return new Association(in); + } + }; + + @DataClass.Generated( + time = 1573767103332L, + codegenVersion = "1.0.13", + sourceFile = "frameworks/base/core/java/android/companion/Association.java", + inputSignatures = "public final int userId\npublic final @android.annotation.NonNull java.lang.String deviceAddress\npublic final @android.annotation.NonNull java.lang.String companionAppPackage\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl index 5398c3cea68d..5e3d46caae91 100644 --- a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl +++ b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl @@ -16,9 +16,10 @@ package android.companion; +import android.companion.Association; import android.companion.AssociationRequest; -import android.companion.ICompanionDeviceDiscoveryServiceCallback; import android.companion.IFindDeviceCallback; +import com.android.internal.infra.AndroidFuture; /** @hide */ @@ -27,5 +28,5 @@ interface ICompanionDeviceDiscoveryService { in AssociationRequest request, in String callingPackage, in IFindDeviceCallback findCallback, - in ICompanionDeviceDiscoveryServiceCallback serviceCallback); + in AndroidFuture<Association> serviceCallback); } diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl deleted file mode 100644 index c9dc019bd2f2..000000000000 --- a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing per missions and - * limitations under the License. - */ - -package android.companion; - -/** @hide */ -interface ICompanionDeviceDiscoveryServiceCallback { - @UnsupportedAppUsage - oneway void onDeviceSelected(String packageName, int userId, String deviceAddress); - @UnsupportedAppUsage - oneway void onDeviceSelectionCancel(); -} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 483bf3f1ec87..4815d7847115 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5045,6 +5045,14 @@ public abstract class Context { 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/Intent.java b/core/java/android/content/Intent.java index c5a147af3137..7967708ce19a 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -11050,6 +11050,7 @@ public class Intent implements Parcelable, Cloneable { case ACTION_MEDIA_SCANNER_FINISHED: case ACTION_MEDIA_SCANNER_SCAN_FILE: case ACTION_PACKAGE_NEEDS_VERIFICATION: + case ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION: case ACTION_PACKAGE_VERIFIED: case ACTION_PACKAGE_ENABLE_ROLLBACK: // Ignore legacy actions diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java index c9634758f63f..70776c76531b 100644 --- a/core/java/android/content/integrity/AppInstallMetadata.java +++ b/core/java/android/content/integrity/AppInstallMetadata.java @@ -86,6 +86,19 @@ public final class AppInstallMetadata { 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/DataLoaderParams.java b/core/java/android/content/pm/DataLoaderParams.java index af4b99a5f815..60d7bb3c5918 100644 --- a/core/java/android/content/pm/DataLoaderParams.java +++ b/core/java/android/content/pm/DataLoaderParams.java @@ -19,6 +19,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.ComponentName; import android.os.ParcelFileDescriptor; import java.util.Arrays; @@ -26,7 +27,7 @@ import java.util.Map; import java.util.stream.Collectors; /** - * This class represents the parameters used to configure an Incremental Data Loader. + * This class represents the parameters used to configure a Data Loader. * * WARNING: This is a system API to aid internal development. * Use at your own risk. It will change or be removed without warning. @@ -34,13 +35,41 @@ import java.util.stream.Collectors; */ @SystemApi public class DataLoaderParams { - @NonNull private final DataLoaderParamsParcel mData; + @NonNull + private final DataLoaderParamsParcel mData; - public DataLoaderParams(@NonNull String url, @NonNull String packageName, + /** + * Creates and populates set of Data Loader parameters for Streaming installation. + * + * @param componentName Data Loader component supporting Streaming installation. + * @param arguments free form installation arguments + */ + public static final @NonNull DataLoaderParams forStreaming(@NonNull ComponentName componentName, + @NonNull String arguments) { + return new DataLoaderParams(DataLoaderType.STREAMING, componentName, arguments, null); + } + + /** + * Creates and populates set of Data Loader parameters for Incremental installation. + * + * @param componentName Data Loader component supporting Incremental installation. + * @param arguments free form installation arguments + * @param namedFds TODO(b/146080380) remove + */ + public static final @NonNull DataLoaderParams forIncremental( + @NonNull ComponentName componentName, @NonNull String arguments, @Nullable Map<String, ParcelFileDescriptor> namedFds) { + return new DataLoaderParams(DataLoaderType.INCREMENTAL, componentName, arguments, namedFds); + } + + /** @hide */ + public DataLoaderParams(@NonNull @DataLoaderType int type, @NonNull ComponentName componentName, + @NonNull String arguments, @Nullable Map<String, ParcelFileDescriptor> namedFds) { DataLoaderParamsParcel data = new DataLoaderParamsParcel(); - data.staticArgs = url; - data.packageName = packageName; + data.type = type; + data.packageName = componentName.getPackageName(); + data.className = componentName.getClassName(); + data.arguments = arguments; if (namedFds == null || namedFds.isEmpty()) { data.dynamicArgs = new NamedParcelFileDescriptor[0]; } else { @@ -56,39 +85,42 @@ public class DataLoaderParams { mData = data; } - /** - * @hide - */ - public DataLoaderParams(@NonNull DataLoaderParamsParcel data) { + /** @hide */ + DataLoaderParams(@NonNull DataLoaderParamsParcel data) { mData = data; } + /** @hide */ + public final @NonNull DataLoaderParamsParcel getData() { + return mData; + } + /** - * @return static server's URL + * @return data loader type */ - public final @NonNull String getStaticArgs() { - return mData.staticArgs; + public final @NonNull @DataLoaderType int getType() { + return mData.type; } /** - * @return data loader's package name + * @return data loader's component name */ - public final @NonNull String getPackageName() { - return mData.packageName; + public final @NonNull ComponentName getComponentName() { + return new ComponentName(mData.packageName, mData.className); } /** - * @hide + * @return data loader's arguments */ - public final @NonNull DataLoaderParamsParcel getData() { - return mData; + public final @NonNull String getArguments() { + return mData.arguments; } /** - * @return data loader's dynamic arguments such as file descriptors + * @return data loader's dynamic arguments such as file descriptors TODO: remove */ public final @NonNull Map<String, ParcelFileDescriptor> getDynamicArgs() { return Arrays.stream(mData.dynamicArgs).collect( - Collectors.toMap(p->p.name, p->p.fd)); + Collectors.toMap(p -> p.name, p -> p.fd)); } } diff --git a/core/java/android/content/pm/DataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl index 33163980b915..e05843b4d4e9 100644 --- a/core/java/android/content/pm/DataLoaderParamsParcel.aidl +++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl @@ -16,6 +16,7 @@ package android.content.pm; +import android.content.pm.DataLoaderType; import android.content.pm.NamedParcelFileDescriptor; /** @@ -23,7 +24,9 @@ import android.content.pm.NamedParcelFileDescriptor; * @hide */ parcelable DataLoaderParamsParcel { + DataLoaderType type; @utf8InCpp String packageName; - @utf8InCpp String staticArgs; + @utf8InCpp String className; + @utf8InCpp String arguments; NamedParcelFileDescriptor[] dynamicArgs; } diff --git a/core/java/android/content/pm/DataLoaderType.aidl b/core/java/android/content/pm/DataLoaderType.aidl new file mode 100644 index 000000000000..7d726f5ddd6f --- /dev/null +++ b/core/java/android/content/pm/DataLoaderType.aidl @@ -0,0 +1,37 @@ +/* + * 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.content.pm; + +/** + * Types of Data Loader for an installation session. + * @hide + */ +@Backing(type="int") +enum DataLoaderType { + /** + * Default value, legacy installation. + */ + NONE = 0, + /** + * Streaming installation using data loader. + */ + STREAMING = 1, + /** + * Streaming installation using Incremental FileSystem. + */ + INCREMENTAL = 2, +} diff --git a/core/java/android/content/pm/IDataLoader.aidl b/core/java/android/content/pm/IDataLoader.aidl index c65bd6acbaf8..b5baa9379d16 100644 --- a/core/java/android/content/pm/IDataLoader.aidl +++ b/core/java/android/content/pm/IDataLoader.aidl @@ -27,7 +27,9 @@ import java.util.List; */ oneway interface IDataLoader { void create(int id, in Bundle params, IDataLoaderStatusListener listener); - void start(in List<InstallationFile> fileInfos); + void start(); void stop(); void destroy(); + + void prepareImage(in List<InstallationFile> addedFiles, in List<String> removedFiles); } diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl index a60d6ee2d28a..5011faafa2f7 100644 --- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl +++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl @@ -22,13 +22,18 @@ package android.content.pm; */ oneway interface IDataLoaderStatusListener { /** Data loader status */ - const int DATA_LOADER_READY = 0; - const int DATA_LOADER_NOT_READY = 1; - const int DATA_LOADER_RUNNING = 2; + const int DATA_LOADER_CREATED = 0; + const int DATA_LOADER_DESTROYED = 1; + + const int DATA_LOADER_STARTED = 2; const int DATA_LOADER_STOPPED = 3; - const int DATA_LOADER_SLOW_CONNECTION = 4; - const int DATA_LOADER_NO_CONNECTION = 5; - const int DATA_LOADER_CONNECTION_OK = 6; + + const int DATA_LOADER_IMAGE_READY = 4; + const int DATA_LOADER_IMAGE_NOT_READY = 5; + + const int DATA_LOADER_SLOW_CONNECTION = 6; + const int DATA_LOADER_NO_CONNECTION = 7; + const int DATA_LOADER_CONNECTION_OK = 8; /** Data loader status callback */ void onStatusChanged(in int dataLoaderId, in int status); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 3d6d849bf03d..e4a0bc0197a1 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1461,10 +1461,7 @@ public class PackageInstaller { /** {@hide} */ public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; /** {@hide} */ - public DataLoaderParams incrementalParams; - /** TODO(b/146080380): add a class name to make it fully compatible with ComponentName. - * {@hide} */ - public String dataLoaderPackageName; + public DataLoaderParams dataLoaderParams; /** {@hide} */ public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE; @@ -1503,10 +1500,8 @@ public class PackageInstaller { DataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable( DataLoaderParamsParcel.class.getClassLoader()); if (dataLoaderParamsParcel != null) { - incrementalParams = new DataLoaderParams( - dataLoaderParamsParcel); + dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel); } - dataLoaderPackageName = source.readString(); rollbackDataPolicy = source.readInt(); } @@ -1531,8 +1526,7 @@ public class PackageInstaller { ret.isMultiPackage = isMultiPackage; ret.isStaged = isStaged; ret.requiredInstalledVersionCode = requiredInstalledVersionCode; - ret.incrementalParams = incrementalParams; - ret.dataLoaderPackageName = dataLoaderPackageName; + ret.dataLoaderParams = dataLoaderParams; ret.rollbackDataPolicy = rollbackDataPolicy; return ret; } @@ -1893,29 +1887,18 @@ public class PackageInstaller { } /** - * Set Incremental data loader params. + * Set the data loader params for the session. + * This also switches installation into data provider mode and disallow direct writes into + * staging folder. + * * WARNING: This is a system API to aid internal development. * Use at your own risk. It will change or be removed without warning. * {@hide} */ @SystemApi @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) - public void setIncrementalParams(@NonNull DataLoaderParams incrementalParams) { - this.incrementalParams = incrementalParams; - } - - /** - * Set the data provider params for the session. - * This also switches installation into callback mode and disallow direct writes into - * staging folder. - * TODO(b/146080380): unify dataprovider params with Incremental. - * - * @param dataLoaderPackageName name of the dataLoader package - * {@hide} - */ - @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) - public void setDataLoaderPackageName(String dataLoaderPackageName) { - this.dataLoaderPackageName = dataLoaderPackageName; + public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) { + this.dataLoaderParams = dataLoaderParams; } /** {@hide} */ @@ -1938,7 +1921,7 @@ public class PackageInstaller { pw.printPair("isMultiPackage", isMultiPackage); pw.printPair("isStaged", isStaged); pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode); - pw.printPair("dataLoaderPackageName", dataLoaderPackageName); + pw.printPair("dataLoaderParams", dataLoaderParams); pw.printPair("rollbackDataPolicy", rollbackDataPolicy); pw.println(); } @@ -1969,12 +1952,11 @@ public class PackageInstaller { dest.writeBoolean(isMultiPackage); dest.writeBoolean(isStaged); dest.writeLong(requiredInstalledVersionCode); - if (incrementalParams != null) { - dest.writeParcelable(incrementalParams.getData(), flags); + if (dataLoaderParams != null) { + dest.writeParcelable(dataLoaderParams.getData(), flags); } else { dest.writeParcelable(null, flags); } - dest.writeString(dataLoaderPackageName); dest.writeInt(rollbackDataPolicy); } diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index fb94fc9bd719..2138d553102e 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -87,8 +87,8 @@ public final class IncrementalFileStorages { mPackageName = packageName; mStageDir = stageDir; mIncrementalManager = incrementalManager; - if (dataLoaderParams.getPackageName().equals("local")) { - final String incrementalPath = dataLoaderParams.getStaticArgs(); + if (dataLoaderParams.getComponentName().getPackageName().equals("local")) { + final String incrementalPath = dataLoaderParams.getArguments(); mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); mDefaultDir = incrementalPath; return; diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java new file mode 100644 index 000000000000..cdd6584e9b35 --- /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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java b/core/java/android/security/IFileIntegrityService.aidl index 750d7d72035c..ebb8bcb85350 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java +++ b/core/java/android/security/IFileIntegrityService.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,17 +14,13 @@ * limitations under the License. */ -package com.android.systemui.dagger.qualifiers; +package android.security; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -import javax.inject.Qualifier; - -@Qualifier -@Documented -@Retention(RUNTIME) -public @interface MainLooper { +/** + * Binder interface to communicate with FileIntegrityService. + * @hide + */ +interface IFileIntegrityService { + boolean isApkVeritySupported(); + boolean isAppSourceCertificateTrusted(in byte[] certificateBytes); } diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java index 54a4fa6023a1..75f252e23f79 100644 --- a/core/java/android/service/dataloader/DataLoaderService.java +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -16,7 +16,6 @@ package android.service.dataloader; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -27,19 +26,16 @@ import android.content.pm.DataLoaderParamsParcel; import android.content.pm.FileSystemControlParcel; import android.content.pm.IDataLoader; import android.content.pm.IDataLoaderStatusListener; -import android.content.pm.IPackageInstallerSessionFileSystemConnector; import android.content.pm.InstallationFile; import android.content.pm.NamedParcelFileDescriptor; import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; import android.util.ExceptionUtils; import android.util.Slog; import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.util.Collection; import java.util.List; /** @@ -55,88 +51,35 @@ import java.util.List; */ @SystemApi public abstract class DataLoaderService extends Service { - private static final String TAG = "IncrementalDataLoaderService"; + private static final String TAG = "DataLoaderService"; private final DataLoaderBinderService mBinder = new DataLoaderBinderService(); - /** @hide */ - public static final int DATA_LOADER_READY = - IDataLoaderStatusListener.DATA_LOADER_READY; - /** @hide */ - public static final int DATA_LOADER_NOT_READY = - IDataLoaderStatusListener.DATA_LOADER_NOT_READY; - /** @hide */ - public static final int DATA_LOADER_RUNNING = - IDataLoaderStatusListener.DATA_LOADER_RUNNING; - /** @hide */ - public static final int DATA_LOADER_STOPPED = - IDataLoaderStatusListener.DATA_LOADER_STOPPED; - /** @hide */ - public static final int DATA_LOADER_SLOW_CONNECTION = - IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION; - /** @hide */ - public static final int DATA_LOADER_NO_CONNECTION = - IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION; - /** @hide */ - public static final int DATA_LOADER_CONNECTION_OK = - IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"DATA_LOADER_"}, value = { - DATA_LOADER_READY, - DATA_LOADER_NOT_READY, - DATA_LOADER_RUNNING, - DATA_LOADER_STOPPED, - DATA_LOADER_SLOW_CONNECTION, - DATA_LOADER_NO_CONNECTION, - DATA_LOADER_CONNECTION_OK - }) - public @interface DataLoaderStatus { - } - /** - * Managed DataLoader interface. Each instance corresponds to a single Incremental File System - * instance. + * Managed DataLoader interface. Each instance corresponds to a single installation session. * @hide */ - public abstract static class DataLoader { + public interface DataLoader { /** - * A virtual constructor used to do simple initialization. Not ready to serve any data yet. - * All heavy-lifting has to be done in onStart. + * A virtual constructor. * - * @param params Data loader configuration parameters. - * @param connector IncFS API wrapper. - * @param listener Used for reporting internal state to IncrementalService. + * @param dataLoaderParams parameters set in the installation session + * @param connector FS API wrapper * @return True if initialization of a Data Loader was successful. False will be reported to - * IncrementalService and can cause an unmount of an IFS instance. + * PackageManager and fail the installation */ - public abstract boolean onCreate(@NonNull DataLoaderParams params, - @NonNull FileSystemConnector connector, - @NonNull StatusListener listener); + boolean onCreate(@NonNull DataLoaderParams dataLoaderParams, + @NonNull FileSystemConnector connector); /** - * Start the data loader. After this method returns data loader is considered to be ready to - * receive callbacks from IFS, supply data via connector and send status updates via - * callbacks. + * Prepare installation image. After this method succeeds installer will validate the files + * and continue installation. * - * @return True if Data Loader was able to start. False will be reported to - * IncrementalService and can cause an unmount of an IFS instance. - */ - public abstract boolean onStart(); - - /** - * Stop the data loader. Use to stop any additional threads and free up resources. Data - * loader is not longer responsible for supplying data. Start/Stop pair can be called - * multiple times e.g. if IFS detects corruption and data needs to be re-loaded. + * @param addedFiles list of files created in this installation session. + * @param removedFiles list of files removed in this installation session. + * @return false if unable to create and populate all addedFiles. */ - public abstract void onStop(); - - /** - * Virtual destructor. Use to cleanup all internal state. After this method returns, the - * data loader can no longer use connector or callbacks. For any additional operations with - * this instance of IFS a new DataLoader will be created using createDataLoader method. - */ - public abstract void onDestroy(); + boolean onPrepareImage(Collection<InstallationFile> addedFiles, + Collection<String> removedFiles); } /** @@ -145,7 +88,9 @@ public abstract class DataLoaderService extends Service { * @return An instance of a DataLoader. * @hide */ - public abstract @Nullable DataLoader onCreateDataLoader(); + public @Nullable DataLoader onCreateDataLoader() { + return null; + } /** * @hide @@ -160,148 +105,125 @@ public abstract class DataLoaderService extends Service { @Override public void create(int id, @NonNull Bundle options, @NonNull IDataLoaderStatusListener listener) - throws IllegalArgumentException, RuntimeException { + throws IllegalArgumentException, RuntimeException { mId = id; - final DataLoaderParamsParcel params = options.getParcelable("params"); + final DataLoaderParamsParcel params = options.getParcelable("params"); if (params == null) { - throw new IllegalArgumentException("Must specify Incremental data loader params"); + throw new IllegalArgumentException("Must specify data loader params"); } - final FileSystemControlParcel control = - options.getParcelable("control"); + final FileSystemControlParcel control = options.getParcelable("control"); if (control == null) { - throw new IllegalArgumentException("Must specify Incremental control parcel"); + throw new IllegalArgumentException("Must specify control parcel"); } - mStatusListener = listener; try { if (!nativeCreateDataLoader(id, control, params, listener)) { Slog.e(TAG, "Failed to create native loader for " + mId); } } catch (Exception ex) { + Slog.e(TAG, "Failed to create native loader for " + mId, ex); destroy(); throw new RuntimeException(ex); } finally { // Closing FDs. - if (control.incremental.cmd != null) { - try { - control.incremental.cmd.close(); - } catch (IOException e) { - Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e); + if (control.incremental != null) { + if (control.incremental.cmd != null) { + try { + control.incremental.cmd.close(); + } catch (IOException e) { + Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e); + } } - } - if (control.incremental.log != null) { - try { - control.incremental.log.close(); - } catch (IOException e) { - Slog.e(TAG, "Failed to close IncFs LOG file descriptor " + e); + if (control.incremental.log != null) { + try { + control.incremental.log.close(); + } catch (IOException e) { + Slog.e(TAG, "Failed to close IncFs LOG file descriptor " + e); + } } } - NamedParcelFileDescriptor[] fds = params.dynamicArgs; - for (NamedParcelFileDescriptor nfd : fds) { - try { - nfd.fd.close(); - } catch (IOException e) { - Slog.e(TAG, - "Failed to close DynamicArgs parcel file descriptor " + e); + if (params.dynamicArgs != null) { + NamedParcelFileDescriptor[] fds = params.dynamicArgs; + for (NamedParcelFileDescriptor nfd : fds) { + try { + nfd.fd.close(); + } catch (IOException e) { + Slog.e(TAG, "Failed to close DynamicArgs parcel file descriptor " + e); + } } } } } @Override - public void start(List<InstallationFile> fileInfos) { + public void start() { if (!nativeStartDataLoader(mId)) { - Slog.e(TAG, "Failed to start loader: loader not found for " + mId); + Slog.e(TAG, "Failed to start loader: " + mId); } } @Override public void stop() { if (!nativeStopDataLoader(mId)) { - Slog.w(TAG, "Failed to stop loader: loader not found for " + mId); + Slog.w(TAG, "Failed to stop loader: " + mId); } } @Override public void destroy() { if (!nativeDestroyDataLoader(mId)) { - Slog.w(TAG, "Failed to destroy loader: loader not found for " + mId); + Slog.w(TAG, "Failed to destroy loader: " + mId); + } + } + + @Override + public void prepareImage(List<InstallationFile> addedFiles, List<String> removedFiles) { + if (!nativePrepareImage(mId, addedFiles, removedFiles)) { + Slog.w(TAG, "Failed to destroy loader: " + mId); } } } /** - * * Used by the DataLoaderService implementations. * * @hide */ public static final class FileSystemConnector { /** - * Creates a wrapper for an installation session connector. + * Create a wrapper for a native instance. + * * @hide */ - FileSystemConnector(IPackageInstallerSessionFileSystemConnector connector) { - mConnector = connector; + FileSystemConnector(long nativeInstance) { + mNativeInstance = nativeInstance; } /** * Write data to an installation file from an arbitrary FD. * - * @param name name of file previously added to the installation session. - * @param offsetBytes offset into the file to begin writing at, or 0 to - * start at the beginning of the file. - * @param lengthBytes total size of the file being written, used to - * preallocate the underlying disk space, or -1 if unknown. - * The system may clear various caches as needed to allocate - * this space. - * @param incomingFd FD to read bytes from. - * @throws IOException if trouble opening the file for writing, such as - * lack of disk space or unavailable media. + * @param name name of file previously added to the installation session. + * @param offsetBytes offset into the file to begin writing at, or 0 to start at the + * beginning of the file. + * @param lengthBytes total size of the file being written, used to preallocate the + * underlying disk space, or -1 if unknown. The system may clear various + * caches as needed to allocate this space. + * @param incomingFd FD to read bytes from. + * @throws IOException if trouble opening the file for writing, such as lack of disk space + * or unavailable media. */ public void writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd) throws IOException { try { - mConnector.writeData(name, offsetBytes, lengthBytes, incomingFd); + nativeWriteData(mNativeInstance, name, offsetBytes, lengthBytes, incomingFd); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); } } - private final IPackageInstallerSessionFileSystemConnector mConnector; - } - - /** - * Wrapper for native reporting DataLoader statuses. - * @hide - */ - public static final class StatusListener { - /** - * Creates a wrapper for a native instance. - * @hide - */ - StatusListener(long nativeInstance) { - mNativeInstance = nativeInstance; - } - - /** - * Report the status of DataLoader. Used for system-wide notifications e.g., disabling - * applications which rely on this data loader to function properly. - * - * @param status status to report. - * @return True if status was reported successfully. - */ - public boolean onStatusChanged(@DataLoaderStatus int status) { - return nativeReportStatus(mNativeInstance, status); - } - private final long mNativeInstance; } - private IDataLoaderStatusListener mStatusListener = null; - /* Native methods */ private native boolean nativeCreateDataLoader(int storageId, @NonNull FileSystemControlParcel control, @@ -314,5 +236,10 @@ public abstract class DataLoaderService extends Service { private native boolean nativeDestroyDataLoader(int storageId); - private static native boolean nativeReportStatus(long nativeInstance, int status); + private native boolean nativePrepareImage(int storageId, + Collection<InstallationFile> addedFiles, Collection<String> removedFiles); + + private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes, + long lengthBytes, ParcelFileDescriptor incomingFd); + } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 51a9c864ee29..7841f991c4bc 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -24,7 +24,6 @@ import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Binder; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; @@ -319,10 +318,8 @@ public class PhoneStateListener { * Listen for call disconnect causes which contains {@link DisconnectCause} and * {@link PreciseDisconnectCause}. * - * @hide */ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) - @SystemApi public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; /** @@ -342,10 +339,8 @@ public class PhoneStateListener { * {@link android.telephony.ims.ImsReasonInfo} * * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo) - * @hide */ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) - @SystemApi public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000; /** @@ -661,10 +656,8 @@ public class PhoneStateListener { * @param disconnectCause {@link DisconnectCause}. * @param preciseDisconnectCause {@link PreciseDisconnectCause}. * - * @hide */ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) - @SystemApi public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) { // default implementation empty } @@ -680,10 +673,8 @@ public class PhoneStateListener { * * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. * - * @hide */ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) - @SystemApi public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) { // default implementation empty } @@ -911,8 +902,7 @@ public class PhoneStateListener { * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * - * Requires - * the READ_PRIVILEGED_PHONE_STATE permission. + * @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE} * @param state the modem radio power state * @hide */ @@ -993,8 +983,11 @@ public class PhoneStateListener { () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi))); } - public void onCellLocationChanged(Bundle bundle) { - CellLocation location = CellLocation.newFromBundle(bundle); + public void onCellLocationChanged(CellIdentity cellIdentity) { + // There is no system/public API to create an CellIdentity in system server, + // so the server pass a null to indicate an empty initial location. + CellLocation location = + cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation(); PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index f574160e5303..1b2fedad842e 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -22,7 +22,6 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; import android.os.Binder; -import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.Annotation.CallState; @@ -633,10 +632,14 @@ public class TelephonyRegistryManager { } /** - * TODO change from bundle to CellLocation? + * Notify {@link android.telephony.CellLocation} changed. + * + * <p>To be compatible with {@link TelephonyRegistry}, use {@link CellIdentity} which is + * parcelable, and convert to CellLocation in client code. + * * @hide */ - public void notifyCellLocation(int subId, Bundle cellLocation) { + public void notifyCellLocation(int subId, CellIdentity cellLocation) { try { sRegistry.notifyCellLocationForSubscriber(subId, cellLocation); } catch (RemoteException ex) { diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java index c9bd92ae84ba..5a8ac544e27e 100644 --- a/core/java/android/view/RemoteAnimationDefinition.java +++ b/core/java/android/view/RemoteAnimationDefinition.java @@ -22,9 +22,12 @@ import android.annotation.Nullable; import android.app.WindowConfiguration; import android.app.WindowConfiguration.ActivityType; import android.compat.annotation.UnsupportedAppUsage; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; import android.util.ArraySet; +import android.util.Slog; import android.util.SparseArray; import android.view.WindowManager.TransitionType; @@ -124,6 +127,20 @@ public class RemoteAnimationDefinition implements Parcelable { } } + /** + * Links the death of the runner to the provided death recipient. + */ + public void linkToDeath(IBinder.DeathRecipient deathRecipient) { + try { + for (int i = 0; i < mTransitionAnimationMap.size(); i++) { + mTransitionAnimationMap.valueAt(i).adapter.getRunner().asBinder() + .linkToDeath(deathRecipient, 0 /* flags */); + } + } catch (RemoteException e) { + Slog.e("RemoteAnimationDefinition", "Failed to link to death recipient"); + } + } + @Override public int describeContents() { return 0; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 93cc626b51a7..c566eaefa0aa 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -158,11 +158,10 @@ public final class SurfaceControl implements Parcelable { IBinder displayToken, long numFrames, long timestamp); private static native int nativeGetActiveConfig(IBinder displayToken); private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); - private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken, - int[] allowedConfigs); - private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken); private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken, SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs); + private static native SurfaceControl.DesiredDisplayConfigSpecs + nativeGetDesiredDisplayConfigSpecs(IBinder displayToken); private static native int[] nativeGetDisplayColorModes(IBinder displayToken); private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); @@ -1474,58 +1473,60 @@ public final class SurfaceControl implements Parcelable { } /** + * Contains information about desired display configuration. + * * @hide */ - public static boolean setAllowedDisplayConfigs(IBinder displayToken, int[] allowedConfigs) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - if (allowedConfigs == null) { - throw new IllegalArgumentException("allowedConfigs must not be null"); + public static final class DesiredDisplayConfigSpecs { + public int defaultConfig; + public float minRefreshRate; + public float maxRefreshRate; + + public DesiredDisplayConfigSpecs() {} + + public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) { + copyFrom(other); } - return nativeSetAllowedDisplayConfigs(displayToken, allowedConfigs); - } + public DesiredDisplayConfigSpecs( + int defaultConfig, float minRefreshRate, float maxRefreshRate) { + this.defaultConfig = defaultConfig; + this.minRefreshRate = minRefreshRate; + this.maxRefreshRate = maxRefreshRate; + } - /** - * @hide - */ - public static int[] getAllowedDisplayConfigs(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); + @Override + public boolean equals(Object o) { + return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o); } - return nativeGetAllowedDisplayConfigs(displayToken); - } - /** - * Contains information about desired display configuration. - * - * @hide - */ - public static final class DesiredDisplayConfigSpecs { /** - * @hide + * Tests for equality. */ - public int mDefaultModeId; + public boolean equals(DesiredDisplayConfigSpecs other) { + return other != null && defaultConfig == other.defaultConfig + && minRefreshRate == other.minRefreshRate + && maxRefreshRate == other.maxRefreshRate; + } - /** - * @hide - */ - public float mMinRefreshRate; + @Override + public int hashCode() { + return 0; // don't care + } /** - * @hide + * Copies the supplied object's values to this object. */ - public float mMaxRefreshRate; + public void copyFrom(DesiredDisplayConfigSpecs other) { + defaultConfig = other.defaultConfig; + minRefreshRate = other.minRefreshRate; + maxRefreshRate = other.maxRefreshRate; + } - /** - * @hide - */ - public DesiredDisplayConfigSpecs( - int defaultModeId, float minRefreshRate, float maxRefreshRate) { - mDefaultModeId = defaultModeId; - mMinRefreshRate = minRefreshRate; - mMaxRefreshRate = maxRefreshRate; + @Override + public String toString() { + return String.format("defaultConfig=%d min=%.0f max=%.0f", defaultConfig, + minRefreshRate, maxRefreshRate); } } @@ -1544,6 +1545,18 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public static SurfaceControl.DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs( + IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + + return nativeGetDesiredDisplayConfigSpecs(displayToken); + } + + /** + * @hide + */ public static int[] getDisplayColorModes(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1a5b3e58f576..0db80e2749c3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6162,10 +6162,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this)); } - final boolean debugDraw() { + /** + * Returns {@code true} when the View is attached and the system developer setting to show + * the layout bounds is enabled or {@code false} otherwise. + */ + public final boolean isShowingLayoutBounds() { return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout; } + /** + * Used to test isShowingLayoutBounds(). This sets the local value used + * by that function. This method does nothing if the layout isn't attached. + * + * @hide + */ + @TestApi + public final void setShowingLayoutBounds(boolean debugLayout) { + if (mAttachInfo != null) { + mAttachInfo.mDebugLayout = debugLayout; + } + } + private static SparseArray<String> getAttributeMap() { if (mAttributeMap == null) { mAttributeMap = new SparseArray<>(); @@ -20881,7 +20898,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().draw(canvas); } - if (debugDraw()) { + if (isShowingLayoutBounds()) { debugDrawFocus(canvas); } } else { @@ -22026,7 +22043,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Step 7, draw the default focus highlight drawDefaultFocusHighlight(canvas); - if (debugDraw()) { + if (isShowingLayoutBounds()) { debugDrawFocus(canvas); } @@ -22201,7 +22218,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Step 6, draw decorations (foreground, scrollbars) onDrawForeground(canvas); - if (debugDraw()) { + if (isShowingLayoutBounds()) { debugDrawFocus(canvas); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 21e9e3185066..5fb71773db8f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -681,7 +681,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private void initViewGroup() { // ViewGroup doesn't draw by default - if (!debugDraw()) { + if (!isShowingLayoutBounds()) { setFlags(WILL_NOT_DRAW, DRAW_MASK); } mGroupFlags |= FLAG_CLIP_CHILDREN; @@ -4156,7 +4156,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } if (usingRenderNodeProperties) canvas.insertInorderBarrier(); - if (debugDraw()) { + if (isShowingLayoutBounds()) { onDebugDraw(canvas); } diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index 237d1a9e7838..1aa2aeccc0db 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -42,6 +42,7 @@ import com.android.internal.util.Preconditions; 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 @@ public final class TextLinks implements Parcelable { /** * Returns the text that was used to generate these links. - * @hide */ @NonNull public String getText() { @@ -342,6 +342,7 @@ public final class TextLinks implements Parcelable { 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 @@ public final class TextLinks implements Parcelable { 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 @@ public final class TextLinks implements Parcelable { } /** + * @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 @@ public final class TextLinks implements Parcelable { @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 @@ public final class TextLinks implements Parcelable { } /** + * @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 @@ public final class TextLinks implements Parcelable { 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 @@ public final class TextLinks implements Parcelable { 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 246a92cc6e1c..a86b56602efc 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -120,7 +120,6 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.content.PackageMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.ImageUtils; import com.android.internal.widget.GridLayoutManager; import com.android.internal.widget.RecyclerView; import com.android.internal.widget.ResolverDrawerLayout; @@ -345,7 +344,9 @@ public class ChooserActivity extends ResolverActivity implements mHandler.sendEmptyMessageDelayed(IMAGE_LOAD_TIMEOUT, mImageLoadTimeoutMillis); AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - final Bitmap bmp = loadThumbnail(uri, new Size(200, 200)); + int size = getResources().getDimensionPixelSize( + R.dimen.chooser_preview_image_max_dimen); + final Bitmap bmp = loadThumbnail(uri, new Size(size, size)); final Message msg = Message.obtain(); msg.what = IMAGE_LOAD_INTO_VIEW; msg.obj = new LoadUriTask(imageResourceId, uri, extraImages, bmp); @@ -1438,20 +1439,22 @@ public class ChooserActivity extends ResolverActivity implements 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); @@ -2127,7 +2130,7 @@ public class ChooserActivity extends ResolverActivity implements } try { - return ImageUtils.loadThumbnail(getContentResolver(), uri, size); + return getContentResolver().loadThumbnail(uri, size, null); } catch (IOException | NullPointerException | SecurityException ex) { logContentPreviewWarning(uri); } diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java index ff6582d10535..f4c69a51ece1 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.DialogInterface; 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 @@ public class ChooserStackedAppDialogFragment extends DialogFragment 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 @@ public class ChooserStackedAppDialogFragment extends DialogFragment @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/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 33b2113b0674..c610ac4503c9 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -45,12 +45,14 @@ public class SuspendedAppActivity extends AlertActivity public static final String EXTRA_SUSPENDING_PACKAGE = PACKAGE_NAME + ".extra.SUSPENDING_PACKAGE"; public static final String EXTRA_DIALOG_INFO = PACKAGE_NAME + ".extra.DIALOG_INFO"; + public static final String EXTRA_ACTIVITY_OPTIONS = PACKAGE_NAME + ".extra.ACTIVITY_OPTIONS"; private Intent mMoreDetailsIntent; private int mUserId; private PackageManager mPm; private Resources mSuspendingAppResources; private SuspendDialogInfo mSuppliedDialogInfo; + private Bundle mOptions; private CharSequence getAppLabel(String packageName) { try { @@ -143,6 +145,7 @@ public class SuspendedAppActivity extends AlertActivity getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); final Intent intent = getIntent(); + mOptions = intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS); mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1); if (mUserId < 0) { Slog.wtf(TAG, "Invalid user: " + mUserId); @@ -178,20 +181,22 @@ public class SuspendedAppActivity extends AlertActivity public void onClick(DialogInterface dialog, int which) { switch (which) { case AlertDialog.BUTTON_NEUTRAL: - startActivityAsUser(mMoreDetailsIntent, UserHandle.of(mUserId)); - Slog.i(TAG, "Started more details activity"); + startActivityAsUser(mMoreDetailsIntent, mOptions, UserHandle.of(mUserId)); + Slog.i(TAG, "Started activity: " + mMoreDetailsIntent.getAction() + + " in user " + mUserId); break; } finish(); } public static Intent createSuspendedAppInterceptIntent(String suspendedPackage, - String suspendingPackage, SuspendDialogInfo dialogInfo, int userId) { + String suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options, int userId) { return new Intent() .setClassName("android", SuspendedAppActivity.class.getName()) .putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage) .putExtra(EXTRA_DIALOG_INFO, dialogInfo) .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage) + .putExtra(EXTRA_ACTIVITY_OPTIONS, options) .putExtra(Intent.EXTRA_USER_ID, userId) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java index 55200c8d9d20..e58258352106 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 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo { 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 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo { 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 dec9ae701fb2..221cd6d8a5c3 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.LinkedList; 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 @@ public abstract class FileSystemProvider extends DocumentsProvider { 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 @@ public abstract class FileSystemProvider extends DocumentsProvider { 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 @@ public abstract class FileSystemProvider extends DocumentsProvider { } 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 @@ public abstract class FileSystemProvider extends DocumentsProvider { 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/java/com/android/internal/infra/AndroidFuture.aidl b/core/java/com/android/internal/infra/AndroidFuture.aidl index b19aab8ab0d5..5f623b183168 100644 --- a/core/java/com/android/internal/infra/AndroidFuture.aidl +++ b/core/java/com/android/internal/infra/AndroidFuture.aidl @@ -17,4 +17,4 @@ package com.android.internal.infra; /** @hide */ -parcelable AndroidFuture; +parcelable AndroidFuture<T>; diff --git a/core/java/com/android/internal/infra/PerUser.java b/core/java/com/android/internal/infra/PerUser.java new file mode 100644 index 000000000000..560ca8cff8df --- /dev/null +++ b/core/java/com/android/internal/infra/PerUser.java @@ -0,0 +1,59 @@ +/* + * 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.internal.infra; + +import android.annotation.NonNull; +import android.util.SparseArray; + +import com.android.internal.util.Preconditions; + +/** + * A {@link SparseArray} customized for a common use-case of storing state per-user. + * + * Unlike a normal {@link SparseArray} this will always create a value on {@link #get} if one is + * not present instead of returning null. + * + * @param <T> user state type + */ +public abstract class PerUser<T> extends SparseArray<T> { + + /** + * Initialize state for the given user + */ + protected abstract @NonNull T create(int userId); + + /** + * Same as {@link #get(int)}, renamed for readability. + * + * This will never return null, deferring to {@link #create} instead + * when called for the first time. + */ + public @NonNull T forUser(int userId) { + return get(userId); + } + + @Override + public @NonNull T get(int userId) { + T userState = super.get(userId); + if (userState != null) { + return userState; + } else { + userState = Preconditions.checkNotNull(create(userId)); + put(userId, userState); + return userState; + } + } +} diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index c390a518bb22..2248b8853f8c 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -316,7 +316,9 @@ public final class Zygote { * @param appDataDir null-ok The data directory of the app. * @param isTopApp True if the process is for top (high priority) application. * @param pkgDataInfoList A list that stores related packages and its app data - * info: volume uuid and inode. + * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name, + * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid, + * app_b_ce_inode, ...]; */ private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index cb67309ce74f..6c7e3dc84425 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -16,8 +16,8 @@ package com.android.internal.telephony; -import android.os.Bundle; import android.telephony.CallAttributes; +import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.PhoneCapability; @@ -37,8 +37,8 @@ oneway interface IPhoneStateListener { void onMessageWaitingIndicatorChanged(boolean mwi); void onCallForwardingIndicatorChanged(boolean cfi); - // we use bundle here instead of CellLocation so it can get the right subclass - void onCellLocationChanged(in Bundle location); + // Uses CellIdentity which is Parcelable here; will convert to CellLocation in client. + void onCellLocationChanged(in CellIdentity location); void onCallStateChanged(int state, String incomingNumber); void onDataConnectionStateChanged(int state, int networkType); void onDataActivity(int direction); @@ -63,4 +63,3 @@ oneway interface IPhoneStateListener { void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause); void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo); } - diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index f954679ebf4d..4e405037f6ef 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -19,8 +19,8 @@ package com.android.internal.telephony; import android.content.Intent; import android.net.LinkProperties; import android.net.NetworkCapabilities; -import android.os.Bundle; import android.telephony.CallQuality; +import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.ims.ImsReasonInfo; import android.telephony.PhoneCapability; @@ -66,9 +66,9 @@ interface ITelephonyRegistry { int phoneId, int subId, String apnType, in PreciseDataConnectionState preciseState); @UnsupportedAppUsage void notifyDataConnectionFailed(String apnType); - @UnsupportedAppUsage(maxTargetSdk = 28) - void notifyCellLocation(in Bundle cellLocation); - void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation); + // Uses CellIdentity which is Parcelable here; will convert to CellLocation in client. + void notifyCellLocation(in CellIdentity cellLocation); + void notifyCellLocationForSubscriber(in int subId, in CellIdentity cellLocation); @UnsupportedAppUsage void notifyCellInfo(in List<CellInfo> cellInfo); void notifyPreciseCallState(int phoneId, int subId, int ringingCallState, diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java index b955f672fe93..3c9791791a68 100644 --- a/core/java/com/android/internal/util/FunctionalUtils.java +++ b/core/java/com/android/internal/util/FunctionalUtils.java @@ -19,6 +19,7 @@ package com.android.internal.util; import android.os.RemoteException; import android.util.ExceptionUtils; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -54,6 +55,13 @@ public class FunctionalUtils { /** * @see #uncheckExceptions(ThrowingConsumer) */ + public static <A, B> BiConsumer<A, B> uncheckExceptions(ThrowingBiConsumer<A, B> action) { + return action; + } + + /** + * @see #uncheckExceptions(ThrowingConsumer) + */ public static <T> Supplier<T> uncheckExceptions(ThrowingSupplier<T> action) { return action; } @@ -185,4 +193,29 @@ public class FunctionalUtils { } } } + + /** + * A {@link BiConsumer} that allows throwing checked exceptions from its single abstract method. + * + * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression + * that throws a checked exception into a regular {@link Function} + * + * @param <A> see {@link BiConsumer} + * @param <B> see {@link BiConsumer} + */ + @FunctionalInterface + @SuppressWarnings("FunctionalInterfaceMethodChanged") + public interface ThrowingBiConsumer<A, B> extends BiConsumer<A, B> { + /** @see ThrowingFunction */ + void acceptOrThrow(A a, B b) throws Exception; + + @Override + default void accept(A a, B b) { + try { + acceptOrThrow(a, b); + } catch (Exception ex) { + throw ExceptionUtils.propagate(ex); + } + } + } } diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index 99b5f8592e5d..a3c455bfc111 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 @@ static inline audio_format_t audioFormatToNative(int audioFormat) 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 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) 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_os_Debug.cpp b/core/jni/android_os_Debug.cpp index e21eefb196c9..6e0d5d8f6461 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -828,7 +828,7 @@ static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) { } } - for (dmabufinfo::DmaBuffer buf : dmabufs) { + for (const dmabufinfo::DmaBuffer& buf : dmabufs) { ionPss += buf.size() / 1024; } diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp index 381b386d7f62..a62d127a968f 100644 --- a/core/jni/android_service_DataLoaderService.cpp +++ b/core/jni/android_service_DataLoaderService.cpp @@ -51,13 +51,19 @@ static jboolean nativeDestroyDataLoader(JNIEnv* env, } -static jboolean nativeReportStatus(JNIEnv* env, - jobject clazz, - jlong self, - jint status) { - auto listener = (DataLoaderStatusListenerPtr)self; - return DataLoader_StatusListener_reportStatus(listener, - (DataLoaderStatus)status); +static jboolean nativePrepareImage(JNIEnv* env, jobject thiz, jint storageId, jobject addedFiles, jobject removedFiles) { + return DataLoaderService_OnPrepareImage(storageId, addedFiles, removedFiles); +} + +static void nativeWriteData(JNIEnv* env, + jobject clazz, + jlong self, + jstring name, + jlong offsetBytes, + jlong lengthBytes, + jobject incomingFd) { + auto connector = (DataLoaderFilesystemConnectorPtr)self; + return DataLoader_FilesystemConnector_writeData(connector, name, offsetBytes, lengthBytes, incomingFd); } static const JNINativeMethod dlc_method_table[] = { @@ -69,7 +75,8 @@ static const JNINativeMethod dlc_method_table[] = { {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader}, {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader}, {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader}, - {"nativeReportStatus", "(JI)Z", (void*)nativeReportStatus}, + {"nativePrepareImage", "(ILjava/util/Collection;Ljava/util/Collection;)Z", (void*)nativePrepareImage}, + {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", (void*)nativeWriteData}, }; } // namespace diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index c6e678ab50b0..33da34d809eb 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -141,7 +141,7 @@ static struct { static struct { jclass clazz; jmethodID ctor; - jfieldID defaultModeId; + jfieldID defaultConfig; jfieldID minRefreshRate; jfieldID maxRefreshRate; } gDesiredDisplayConfigSpecsClassInfo; @@ -776,65 +776,40 @@ static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, return configArray; } -static jboolean nativeSetAllowedDisplayConfigs(JNIEnv* env, jclass clazz, - jobject tokenObj, jintArray configArray) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == nullptr) return JNI_FALSE; - - std::vector<int32_t> allowedConfigs; - jsize configArraySize = env->GetArrayLength(configArray); - allowedConfigs.reserve(configArraySize); - - jint* configArrayElements = env->GetIntArrayElements(configArray, 0); - for (int i = 0; i < configArraySize; i++) { - allowedConfigs.push_back(configArrayElements[i]); - } - env->ReleaseIntArrayElements(configArray, configArrayElements, 0); - - size_t result = SurfaceComposerClient::setAllowedDisplayConfigs(token, allowedConfigs); - return result == NO_ERROR ? JNI_TRUE : JNI_FALSE; -} - -static jintArray nativeGetAllowedDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == nullptr) return JNI_FALSE; - - std::vector<int32_t> allowedConfigs; - size_t result = SurfaceComposerClient::getAllowedDisplayConfigs(token, &allowedConfigs); - if (result != NO_ERROR) { - return nullptr; - } - - jintArray allowedConfigsArray = env->NewIntArray(allowedConfigs.size()); - if (allowedConfigsArray == nullptr) { - jniThrowException(env, "java/lang/OutOfMemoryError", NULL); - return nullptr; - } - jint* allowedConfigsArrayValues = env->GetIntArrayElements(allowedConfigsArray, 0); - for (size_t i = 0; i < allowedConfigs.size(); i++) { - allowedConfigsArrayValues[i] = static_cast<jint>(allowedConfigs[i]); - } - env->ReleaseIntArrayElements(allowedConfigsArray, allowedConfigsArrayValues, 0); - return allowedConfigsArray; -} - static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj, jobject desiredDisplayConfigSpecs) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == nullptr) return JNI_FALSE; - jint defaultModeId = env->GetIntField(desiredDisplayConfigSpecs, - gDesiredDisplayConfigSpecsClassInfo.defaultModeId); + jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs, + gDesiredDisplayConfigSpecsClassInfo.defaultConfig); jfloat minRefreshRate = env->GetFloatField(desiredDisplayConfigSpecs, gDesiredDisplayConfigSpecsClassInfo.minRefreshRate); jfloat maxRefreshRate = env->GetFloatField(desiredDisplayConfigSpecs, gDesiredDisplayConfigSpecsClassInfo.maxRefreshRate); size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs( - token, defaultModeId, minRefreshRate, maxRefreshRate); + token, defaultConfig, minRefreshRate, maxRefreshRate); return result == NO_ERROR ? JNI_TRUE : JNI_FALSE; } +static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == nullptr) return nullptr; + + int32_t defaultConfig; + float minRefreshRate; + float maxRefreshRate; + if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig, &minRefreshRate, + &maxRefreshRate) != NO_ERROR) { + return nullptr; + } + + return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz, + gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig, minRefreshRate, + maxRefreshRate); +} + static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return -1; @@ -1387,13 +1362,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetActiveConfig }, {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveConfig }, - {"nativeSetAllowedDisplayConfigs", "(Landroid/os/IBinder;[I)Z", - (void*)nativeSetAllowedDisplayConfigs }, - {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I", - (void*)nativeGetAllowedDisplayConfigs }, {"nativeSetDesiredDisplayConfigSpecs", "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z", (void*)nativeSetDesiredDisplayConfigSpecs }, + {"nativeGetDesiredDisplayConfigSpecs", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;", + (void*)nativeGetDesiredDisplayConfigSpecs }, {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", (void*)nativeGetDisplayColorModes}, {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", @@ -1569,12 +1543,12 @@ int register_android_view_SurfaceControl(JNIEnv* env) MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz); gDesiredDisplayConfigSpecsClassInfo.ctor = GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IFF)V"); - gDesiredDisplayConfigSpecsClassInfo.defaultModeId = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "mDefaultModeId", "I"); + gDesiredDisplayConfigSpecsClassInfo.defaultConfig = + GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I"); gDesiredDisplayConfigSpecsClassInfo.minRefreshRate = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "mMinRefreshRate", "F"); + GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "minRefreshRate", "F"); gDesiredDisplayConfigSpecsClassInfo.maxRefreshRate = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "mMaxRefreshRate", "F"); + GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "maxRefreshRate", "F"); return err; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1165d2d673d5..44a902c830f2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4113,7 +4113,7 @@ <permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT" android:protectionLevel="signature|privileged" /> - <!-- Must be required by intent filter verifier receiver, to ensure that only the + <!-- Must be required by intent filter verifier rintent-filtereceiver, to ensure that only the system can interact with it. @hide --> @@ -5143,6 +5143,12 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> -</application> + <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"> + <intent-filter> + <action android:name="android.intent.action.LOAD_DATA" /> + </intent-filter> + </service> + + </application> </manifest> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f28f08b8abd1..9073a02e72d3 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/dimens.xml b/core/res/res/values/dimens.xml index 2543967be8a8..bf7558c8597a 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -760,6 +760,7 @@ <dimen name="chooser_edge_margin_normal">24dp</dimen> <dimen name="chooser_preview_image_font_size">20sp</dimen> <dimen name="chooser_preview_image_border">1dp</dimen> + <dimen name="chooser_preview_image_max_dimen">200dp</dimen> <dimen name="chooser_preview_width">-1px</dimen> <dimen name="chooser_target_width">90dp</dimen> <dimen name="chooser_header_scroll_elevation">4dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 78c4efec5b72..4bbfeafdfc9c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3007,8 +3007,6 @@ </public-group> <public-group type="drawable" first-id="0x010800b5"> - <!-- @hide @SystemApi --> - <public name="stat_notify_wifi_in_range" /> </public-group> <public-group type="style" first-id="0x010302e5"> @@ -3019,11 +3017,11 @@ </public-group> <public-group type="string" first-id="0x01040025"> - <!-- @hide @SystemApi --> + <!-- @hide --> <public name="notification_channel_network_status" /> - <!-- @hide @SystemApi --> + <!-- @hide --> <public name="notification_channel_network_alerts" /> - <!-- @hide @SystemApi --> + <!-- @hide --> <public name="notification_channel_network_available" /> <!-- @hide @SystemApi --> <public name="config_defaultCallRedirection" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7cf2b784af1b..e82439e5ce34 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1342,6 +1342,7 @@ <java-symbol type="drawable" name="picture_emergency" /> <java-symbol type="drawable" name="platlogo" /> <java-symbol type="drawable" name="stat_notify_sync_error" /> + <java-symbol type="drawable" name="stat_notify_wifi_in_range" /> <java-symbol type="drawable" name="ic_wifi_signal_0" /> <java-symbol type="drawable" name="ic_wifi_signal_1" /> <java-symbol type="drawable" name="ic_wifi_signal_2" /> @@ -2721,6 +2722,7 @@ <java-symbol type="layout" name="chooser_grid_preview_file" /> <java-symbol type="id" name="chooser_row_text_option" /> <java-symbol type="dimen" name="chooser_row_text_option_translate" /> + <java-symbol type="dimen" name="chooser_preview_image_max_dimen"/> <java-symbol type="integer" name="config_maxShortcutTargetsPerApp" /> <java-symbol type="layout" name="resolve_grid_item" /> <java-symbol type="id" name="day_picker_view_pager" /> diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java index b9cc8f4ba4a0..ec5813fd4123 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 androidx.test.runner.AndroidJUnit4; 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 @@ public class TextLinksTest { 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 @@ public class TextLinksTest { 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 @@ public class TextLinksTest { result.getEntityConfig().resolveEntityListModifications(Collections.emptyList())); assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY)); assertEquals(packageName, result.getCallingPackageName()); + assertEquals(referenceTime, result.getReferenceTime()); } } diff --git a/core/tests/overlaytests/device/res/layout/layout.xml b/core/tests/overlaytests/device/res/layout/layout.xml new file mode 100644 index 000000000000..e12c715a2a32 --- /dev/null +++ b/core/tests/overlaytests/device/res/layout/layout.xml @@ -0,0 +1,39 @@ +<?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. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout android:id="@id/view_1" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.overlaytest.view.TestTextView + android:id="@id/view_2" + android:layout_width="match_parent" + android:layout_height="100dp" + app:customAttribute="none"/> + + <com.android.overlaytest.view.TestTextView + android:id="@id/view_3" + android:layout_width="match_parent" + android:layout_height="100dp" + app:customAttribute="none" /> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/core/tests/overlaytests/device/res/values/config.xml b/core/tests/overlaytests/device/res/values/config.xml index c692a2625199..e918268fa073 100644 --- a/core/tests/overlaytests/device/res/values/config.xml +++ b/core/tests/overlaytests/device/res/values/config.xml @@ -56,4 +56,16 @@ <item>17</item> <item>19</item> </integer-array> + + <attr name="customAttribute" /> + <id name="view_1" /> + <id name="view_2" /> + <id name="view_3" /> + + <!-- Stabilize the ids of attributes and ids used in test layouts so that they differ from the + overlay resource ids --> + <public type="attr" name="customAttribute" id="0x7f200000"/> + <public type="id" name="view_1" id="0x7f210000"/> + <public type="id" name="view_2" id="0x7f210001"/> + <public type="id" name="view_3" id="0x7f210002"/> </resources> diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java index fdb6bbb4550b..636f4c8b59ca 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java @@ -18,20 +18,26 @@ package com.android.overlaytest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.content.Context; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.LocaleList; import android.util.AttributeSet; +import android.util.TypedValue; import android.util.Xml; +import android.view.LayoutInflater; +import android.view.View; import androidx.test.InstrumentationRegistry; import com.android.internal.util.ArrayUtils; +import com.android.overlaytest.view.TestTextView; import org.junit.Before; import org.junit.Ignore; @@ -45,6 +51,7 @@ import java.util.Locale; @Ignore public abstract class OverlayBaseTest { + private Context mContext; private Resources mResources; private final int mMode; static final int MODE_NO_OVERLAY = 0; @@ -61,7 +68,8 @@ public abstract class OverlayBaseTest { @Before public void setUp() { - mResources = InstrumentationRegistry.getContext().getResources(); + mContext = InstrumentationRegistry.getContext(); + mResources = mContext.getResources(); } private int calculateRawResourceChecksum(int resId) throws Throwable { @@ -321,6 +329,50 @@ public abstract class OverlayBaseTest { assertEquals("com.android.overlaytest", contents); } + @Test + public void testRewrite() throws Throwable { + final TypedValue result = new TypedValue(); + mResources.getValue(R.string.str, result, true); + assertEquals(result.resourceId & 0xff000000, 0x7f000000); + } + + @Test + public void testOverlayLayout() throws Throwable { + final LayoutInflater inflater = LayoutInflater.from(mContext); + final View layout = inflater.inflate(R.layout.layout, null); + assertNotNull(layout.findViewById(R.id.view_1)); + + final TestTextView view2 = layout.findViewById(R.id.view_2); + assertNotNull(view2); + switch (mMode) { + case MODE_NO_OVERLAY: + assertEquals("none", view2.getCustomAttributeValue()); + break; + case MODE_SINGLE_OVERLAY: + assertEquals("single", view2.getCustomAttributeValue()); + break; + case MODE_MULTIPLE_OVERLAYS: + assertEquals("multiple", view2.getCustomAttributeValue()); + break; + default: + fail("Unknown mode " + mMode); + } + + final TestTextView view3 = layout.findViewById(R.id.view_3); + assertNotNull(view3); + switch (mMode) { + case MODE_NO_OVERLAY: + assertEquals("none", view3.getCustomAttributeValue()); + break; + case MODE_SINGLE_OVERLAY: + case MODE_MULTIPLE_OVERLAYS: + assertEquals("overlaid", view3.getCustomAttributeValue()); + break; + default: + fail("Unknown mode " + mMode); + } + } + /* * testMatrix* tests * diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/view/TestTextView.java b/core/tests/overlaytests/device/src/com/android/overlaytest/view/TestTextView.java new file mode 100644 index 000000000000..2245e2b8121b --- /dev/null +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/view/TestTextView.java @@ -0,0 +1,48 @@ +/* + * 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.overlaytest.view; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.TextView; + +public class TestTextView extends TextView { + + private final String mCustomAttributeValue; + + public TestTextView(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.textViewStyle, 0); + } + + public TestTextView(Context context, AttributeSet attrs, + int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + int[] testResources = new int[]{com.android.overlaytest.R.attr.customAttribute}; + final Resources.Theme theme = context.getTheme(); + TypedArray typedArray = theme.obtainStyledAttributes(attrs, testResources, defStyleAttr, + defStyleRes); + mCustomAttributeValue = typedArray.getString(0); + } + + public String getCustomAttributeValue() { + return mCustomAttributeValue; + } +} diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml index 7d2840886683..873ca3c9b821 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml @@ -19,5 +19,6 @@ android:versionCode="1" android:versionName="1.0"> <application android:hasCode="false" /> - <overlay android:targetPackage="com.android.overlaytest" /> + <overlay android:targetPackage="com.android.overlaytest" + android:resourcesMap="@xml/overlays"/> </manifest> diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/layout/layout.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/layout/layout.xml new file mode 100644 index 000000000000..7b636054c936 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/layout/layout.xml @@ -0,0 +1,39 @@ +<?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. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout android:id="@+id/view_1" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.overlaytest.view.TestTextView + android:id="@+id/view_2" + android:layout_width="match_parent" + android:layout_height="100dp" + app:customAttribute="@string/str" /> + + <com.android.overlaytest.view.TestTextView + android:id="@+id/view_3" + android:layout_width="match_parent" + android:layout_height="100dp" + app:customAttribute="overlaid" /> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml index 972137a3d1bf..74c496386f2f 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <resources> <string name="str">single</string> - <string name="str2">single</string> <integer name="matrix_101000">300</integer> <integer name="matrix_101001">300</integer> <integer name="matrix_101010">300</integer> @@ -18,7 +17,6 @@ <integer name="matrix_111101">300</integer> <integer name="matrix_111110">300</integer> <integer name="matrix_111111">300</integer> - <bool name="usually_false">true</bool> <integer-array name="fibonacci"> <item>21</item> <item>13</item> @@ -29,7 +27,10 @@ <item>1</item> <item>1</item> </integer-array> + <!-- The following integer does not exist in the original package. Idmap generation should therefore ignore it. --> <integer name="integer_not_in_original_package">0</integer> + + <attr name="customAttribute" /> </resources> diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml new file mode 100644 index 000000000000..38e5fa18300c --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml @@ -0,0 +1,61 @@ +<?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. + --> +<overlay> + <item target="drawable/drawable" value="@drawable/drawable"/> + <item target="layout/layout" value="@layout/layout"/> + <item target="raw/lorem_ipsum" value="@raw/lorem_ipsum"/> + <item target="xml/integer" value="@xml/integer"/> + <item target="string/str" value="@string/str"/> + <item target="string/str2" value="single"/> + + <item target="integer/matrix_100100" value="@integer/matrix_100100"/> + <item target="integer/matrix_100101" value="@integer/matrix_100101"/> + <item target="integer/matrix_100110" value="@integer/matrix_100110"/> + <item target="integer/matrix_100110" value="@integer/matrix_100110"/> + <item target="integer/matrix_100111" value="@integer/matrix_100111"/> + <item target="integer/matrix_101000" value="@integer/matrix_101000"/> + <item target="integer/matrix_101001" value="@integer/matrix_101001"/> + <item target="integer/matrix_101010" value="@integer/matrix_101010"/> + <item target="integer/matrix_101011" value="@integer/matrix_101011"/> + <item target="integer/matrix_101100" value="@integer/matrix_101100"/> + <item target="integer/matrix_101101" value="@integer/matrix_101101"/> + <item target="integer/matrix_101110" value="@integer/matrix_101110"/> + <item target="integer/matrix_101111" value="@integer/matrix_101111"/> + <item target="integer/matrix_110100" value="@integer/matrix_110100"/> + <item target="integer/matrix_110101" value="@integer/matrix_110101"/> + <item target="integer/matrix_110110" value="@integer/matrix_110110"/> + <item target="integer/matrix_110111" value="@integer/matrix_110111"/> + <item target="integer/matrix_111000" value="@integer/matrix_111000"/> + <item target="integer/matrix_111001" value="@integer/matrix_111001"/> + <item target="integer/matrix_111010" value="@integer/matrix_111010"/> + <item target="integer/matrix_111011" value="@integer/matrix_111011"/> + <item target="integer/matrix_111100" value="@integer/matrix_111100"/> + <item target="integer/matrix_111101" value="@integer/matrix_111101"/> + <item target="integer/matrix_111110" value="@integer/matrix_111110"/> + <item target="integer/matrix_111111" value="@integer/matrix_111111"/> + + <item target="bool/usually_false" value="true"/> + + <item target="array/fibonacci" value="@array/fibonacci"/> + + <item target="attr/customAttribute" value="@attr/customAttribute"/> + <item target="id/view_1" value="@id/view_1"/> + <item target="id/view_2" value="@id/view_2"/> + <item target="id/view_3" value="@id/view_3"/> +</overlay> + + diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index ce3bffff0aa5..0b2fd9ec982d 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -57,7 +57,7 @@ OverlayStringPool::~OverlayStringPool() { const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); - if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) { + if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { return idmap_string_pool_->stringAt(idx - offset, outLen); } @@ -66,13 +66,17 @@ const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); - if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) { + if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { return idmap_string_pool_->string8At(idx - offset, outLen); } return ResStringPool::string8At(idx, outLen); } +size_t OverlayStringPool::size() const { + return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U); +} + OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header, const Idmap_overlay_entry* entries, uint8_t target_assigned_package_id) diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ab4c9c204842..ccb57f373473 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -40,8 +40,9 @@ class IdmapResMap; class OverlayStringPool : public ResStringPool { public: virtual ~OverlayStringPool(); - virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; - virtual const char* string8At(size_t idx, size_t* outLen) const; + const char16_t* stringAt(size_t idx, size_t* outLen) const override; + const char* string8At(size_t idx, size_t* outLen) const override; + size_t size() const override; explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); private: @@ -53,8 +54,8 @@ class OverlayStringPool : public ResStringPool { // resources to the resource id of corresponding target resources. class OverlayDynamicRefTable : public DynamicRefTable { public: - virtual ~OverlayDynamicRefTable() = default; - virtual status_t lookupResourceId(uint32_t* resId) const; + ~OverlayDynamicRefTable() override = default; + status_t lookupResourceId(uint32_t* resId) const override; private: explicit OverlayDynamicRefTable(const Idmap_data_header* data_header, diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index b603326cec34..35cebd425dc7 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -520,7 +520,7 @@ public: ssize_t indexOfString(const char16_t* str, size_t strLen) const; - size_t size() const; + virtual size_t size() const; size_t styleCount() const; size_t bytes() const; const void* data() const; diff --git a/media/Android.bp b/media/Android.bp index 75ccb227e5c5..a1365179c53a 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -26,7 +26,8 @@ java_library { 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 @@ java_library { 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 489d0501280d..7ff15df2d65e 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -281,6 +281,8 @@ public final class AudioFormat implements Parcelable { * 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { case ENCODING_AC4: case ENCODING_E_AC3_JOC: case ENCODING_DOLBY_MAT: + case ENCODING_OPUS: return true; default: return false; @@ -576,6 +581,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_AC4: case ENCODING_E_AC3_JOC: case ENCODING_DOLBY_MAT: + case ENCODING_OPUS: return true; default: return false; @@ -607,6 +613,7 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { 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 @@ public final class AudioFormat implements Parcelable { 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 ab80b3af9fc8..515e9d0d4c5c 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -400,6 +400,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { * configurations * @return true if active */ + @SystemApi public boolean isActive() { switch (mPlayerState) { case PLAYER_STATE_STARTED: @@ -420,18 +421,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @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 @@ public final class AudioPlaybackConfiguration implements Parcelable { && (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/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java index b5de88ae1da9..7078d4a0b568 100644 --- a/media/java/android/media/MediaRoute2ProviderInfo.java +++ b/media/java/android/media/MediaRoute2ProviderInfo.java @@ -46,9 +46,9 @@ public final class MediaRoute2ProviderInfo implements Parcelable { }; @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 final class MediaRoute2ProviderInfo implements Parcelable { 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 1b6183e361c8..8d6f2551f9f6 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -27,8 +27,13 @@ import android.os.Handler; 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,14 @@ public abstract class MediaRoute2ProviderService extends Service { public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService"; private final Handler mHandler; + private final Object mSessionLock = new Object(); private ProviderStub mStub; private IMediaRoute2ProviderClient mClient; private MediaRoute2ProviderInfo mProviderInfo; + @GuardedBy("mSessionLock") + private ArrayMap<Integer, RouteSessionInfo> mSessionInfo = new ArrayMap<>(); + public MediaRoute2ProviderService() { mHandler = new Handler(Looper.getMainLooper()); } @@ -93,6 +102,7 @@ public abstract class MediaRoute2ProviderService extends Service { /** * 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 +110,164 @@ public abstract class MediaRoute2ProviderService extends Service { /** * 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. + */ + @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()); + } + } + + /** + * Sets the information of the session with the given id. + * If there is no session matched with the given id, 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 sessionId id of the session that should update its information + * @param sessionInfo new session information + */ + public final void setSessionInfo(int sessionId, @NonNull RouteSessionInfo sessionInfo) { + Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); + + synchronized (mSessionLock) { + if (mSessionInfo.containsKey(sessionId)) { + mSessionInfo.put(sessionId, sessionInfo); + } else { + Log.w(TAG, "Ignoring session info update."); + } + } + } + + /** + * 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 sessionId id of the session + * @param sessionInfo information of the new session. + * Pass {@code null} to reject the request or inform clients that + * session creation has failed. + * @param controlHints a {@link Bundle} that contains how to control the session. + */ + //TODO: fail reason? + public final void notifySessionCreated(int sessionId, @Nullable RouteSessionInfo sessionInfo, + @Nullable Bundle controlHints) { + //TODO: validate sessionId (it must be in "waiting list") + synchronized (mSessionLock) { + mSessionInfo.put(sessionId, sessionInfo); + //TODO: notify media router service of session creation. + } + } + + /** + * 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 setProviderInfo(MediaRoute2ProviderInfo info) { + 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(int, RouteSessionInfo, Bundle)} + * 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(int, RouteSessionInfo, Bundle)}. + * + * @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 sessionId the id of a new session + */ + public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId, + @NonNull String controlCategory, int sessionId); + + /** + * 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 #setSessionInfo(int, 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 #setSessionInfo(int, 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 #setSessionInfo(int, 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 #setSessionInfo(int, 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 +301,7 @@ public abstract class MediaRoute2ProviderService extends Service { } void publishState() { + //TODO: sends session info if (mClient == null) { return; } @@ -179,35 +339,35 @@ public abstract class MediaRoute2ProviderService extends Service { } @Override - public void requestSelectRoute(String packageName, String id, int seq) { + 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/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java index 0878e6b31ec6..53c8eec8bb9f 100644 --- a/media/java/android/media/RouteSessionInfo.java +++ b/media/java/android/media/RouteSessionInfo.java @@ -218,79 +218,116 @@ public class RouteSessionInfo implements Parcelable { } /** - * 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; + } + + /** + * Builds a route session info. */ @NonNull public RouteSessionInfo build() { diff --git a/media/java/android/media/tv/tuner/FilterSettings.java b/media/java/android/media/tv/tuner/FilterSettings.java index d5f100341dc6..673b3d9d8bcc 100644 --- a/media/java/android/media/tv/tuner/FilterSettings.java +++ b/media/java/android/media/tv/tuner/FilterSettings.java @@ -17,8 +17,7 @@ package android.media.tv.tuner; import android.annotation.Nullable; -import android.hardware.tv.tuner.V1_0.Constants; -import android.media.tv.tuner.TunerConstants.FilterSettingsType; +import android.media.tv.tuner.TunerConstants.FilterType; import java.util.List; @@ -38,7 +37,8 @@ public abstract class FilterSettings { /** * Gets filter settings type */ - @FilterSettingsType public abstract int getType(); + @FilterType + public abstract int getType(); // TODO: more builders and getters @@ -55,7 +55,7 @@ public abstract class FilterSettings { @Override public int getType() { - return TunerConstants.FILTER_SETTINGS_TS; + return TunerConstants.FILTER_TYPE_TS; } /** @@ -109,7 +109,7 @@ public abstract class FilterSettings { @Override public int getType() { - return TunerConstants.FILTER_SETTINGS_MMTP; + return TunerConstants.FILTER_TYPE_MMTP; } } @@ -130,7 +130,7 @@ public abstract class FilterSettings { @Override public int getType() { - return TunerConstants.FILTER_SETTINGS_IP; + return TunerConstants.FILTER_TYPE_IP; } } @@ -149,7 +149,7 @@ public abstract class FilterSettings { @Override public int getType() { - return TunerConstants.FILTER_SETTINGS_TLV; + return TunerConstants.FILTER_TYPE_TLV; } } @@ -167,7 +167,7 @@ public abstract class FilterSettings { @Override public int getType() { - return TunerConstants.FILTER_SETTINGS_ALP; + return TunerConstants.FILTER_TYPE_ALP; } } @@ -197,24 +197,7 @@ public abstract class FilterSettings { public static class SectionSettings extends Settings { private SectionSettings(int mainType) { - super(SectionSettings.findType(mainType)); - } - - private static int findType(int mainType) { - switch (mainType) { - case TunerConstants.FILTER_SETTINGS_TS: - return Constants.DemuxTsFilterType.SECTION; - case TunerConstants.FILTER_SETTINGS_MMTP: - return Constants.DemuxMmtpFilterType.SECTION; - case TunerConstants.FILTER_SETTINGS_IP: - return Constants.DemuxIpFilterType.SECTION; - case TunerConstants.FILTER_SETTINGS_TLV: - return Constants.DemuxTlvFilterType.SECTION; - case TunerConstants.FILTER_SETTINGS_ALP: - return Constants.DemuxAlpFilterType.SECTION; - } - // UNDEFINED - return 0; + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION)); } } @@ -251,22 +234,11 @@ public abstract class FilterSettings { private boolean mIsRaw; private PesSettings(int mainType, int streamId, boolean isRaw) { - super(PesSettings.findType(mainType)); + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES)); mStreamId = streamId; mIsRaw = isRaw; } - private static int findType(int mainType) { - switch (mainType) { - case TunerConstants.FILTER_SETTINGS_TS: - return Constants.DemuxTsFilterType.PES; - case TunerConstants.FILTER_SETTINGS_MMTP: - return Constants.DemuxMmtpFilterType.PES; - } - // UNDEFINED - return 0; - } - /** * Creates a builder for PesSettings. */ @@ -319,22 +291,11 @@ public abstract class FilterSettings { private boolean mIsPassthrough; private AvSettings(int mainType, boolean isAudio) { - super(AvSettings.findType(mainType, isAudio)); - } - - private static int findType(int mainType, boolean isAudio) { - switch (mainType) { - case TunerConstants.FILTER_SETTINGS_TS: - return isAudio - ? Constants.DemuxTsFilterType.AUDIO - : Constants.DemuxTsFilterType.VIDEO; - case TunerConstants.FILTER_SETTINGS_MMTP: - return isAudio - ? Constants.DemuxMmtpFilterType.AUDIO - : Constants.DemuxMmtpFilterType.VIDEO; - } - // UNDEFINED - return 0; + super(TunerUtils.getFilterSubtype( + mainType, + isAudio + ? TunerConstants.FILTER_SUBTYPE_AUDIO + : TunerConstants.FILTER_SUBTYPE_VIDEO)); } } @@ -345,15 +306,7 @@ public abstract class FilterSettings { private int mDownloadId; public DownloadSettings(int mainType) { - super(DownloadSettings.findType(mainType)); - } - - private static int findType(int mainType) { - if (mainType == TunerConstants.FILTER_SETTINGS_MMTP) { - return Constants.DemuxMmtpFilterType.DOWNLOAD; - } - // UNDEFINED - return 0; + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD)); } } @@ -365,18 +318,7 @@ public abstract class FilterSettings { private int mIndexMask; public RecordSettings(int mainType) { - super(RecordSettings.findType(mainType)); - } - - private static int findType(int mainType) { - switch (mainType) { - case TunerConstants.FILTER_SETTINGS_TS: - return Constants.DemuxTsFilterType.RECORD; - case TunerConstants.FILTER_SETTINGS_MMTP: - return Constants.DemuxMmtpFilterType.RECORD; - } - // UNDEFINED - return 0; + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD)); } } 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 000000000000..35f54f8447c0 --- /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 4947700f6952..f02c4aa10311 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -21,8 +21,10 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; -import android.content.pm.PackageManager; 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; @@ -43,7 +45,6 @@ public final class Tuner implements AutoCloseable { private static final String TAG = "MediaTvTuner"; private static final boolean DEBUG = false; - private static final String PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER; private static final int MSG_ON_FRONTEND_EVENT = 1; private static final int MSG_ON_FILTER_EVENT = 2; private static final int MSG_ON_FILTER_STATUS = 3; @@ -73,13 +74,6 @@ public final class Tuner implements AutoCloseable { nativeSetup(); } - private void checkPermission() { - if (mContext.checkCallingOrSelfPermission(PERMISSION) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Caller must have " + PERMISSION + " permission."); - } - } - private long mNativeContext; // used by native jMediaTuner /** @hide */ @@ -106,7 +100,10 @@ public final class Tuner implements AutoCloseable { */ 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 Filter nativeOpenFilter(int type, int subType, int bufferSize); private native List<Integer> nativeGetLnbIds(); @@ -127,6 +124,12 @@ public final class Tuner implements AutoCloseable { * Invoked when there is a frontend event. */ void onEvent(int frontendEventType); + + /** + * Invoked when there is a scan message. + * @param msg + */ + void onScanMessage(ScanMessage msg); } /** @@ -252,10 +255,58 @@ public final class Tuner implements AutoCloseable { */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull FrontendSettings settings) { - checkPermission(); + TunerUtils.checkTunerPermission(mContext); return nativeTune(settings.getType(), settings); } + /** + * Stops a previous tuning. + * + * If the method completes successfully the frontend is no longer tuned and no data + * will be sent to attached filters. + * + * @return result status of the operation. + * @hide + */ + public int stopTune() { + return nativeStopTune(); + } + + /** + * 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 + * called multiple times to update LNB assignment. + * + * @param lnb the LNB instance. + * + * @return result status of the operation. + * @hide + */ + public int setLnb(@NonNull Lnb lnb) { + return nativeSetLnb(lnb.mId); + } + + /** + * Enable or Disable Low Noise Amplifier (LNA). + * + * @param enable true to activate LNA module; false to deactivate LNA + * + * @return result status of the operation. + * @hide + */ + public int setLna(boolean enable) { + return nativeSetLna(enable); + } + private List<Integer> getFrontendIds() { mFrontendIds = nativeGetFrontendIds(); return mFrontendIds; @@ -327,8 +378,10 @@ public final class Tuner implements AutoCloseable { } } - private Filter openFilter(int type, int subType, int bufferSize, FilterCallback cb) { - Filter filter = nativeOpenFilter(type, subType, bufferSize); + private Filter openFilter(@FilterType int mainType, @FilterSubtype int subType, int bufferSize, + FilterCallback cb) { + Filter filter = nativeOpenFilter( + mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize); if (filter != null) { filter.mCallback = cb; if (mHandler == null) { @@ -416,7 +469,7 @@ public final class Tuner implements AutoCloseable { @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Nullable public Descrambler openDescrambler() { - checkPermission(); + TunerUtils.checkTunerPermission(mContext); return nativeOpenDescrambler(); } diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index 261b2deebac9..2c6d850b10da 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -75,6 +75,7 @@ final class TunerConstants { 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}) @@ -91,6 +92,75 @@ final class TunerConstants { public static final int FRONTEND_SETTINGS_ISDBT = 9; @Retention(RetentionPolicy.SOURCE) + @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP}) + public @interface FilterType {} + + public static final int FILTER_TYPE_TS = Constants.DemuxFilterMainType.TS; + public static final int FILTER_TYPE_MMTP = Constants.DemuxFilterMainType.MMTP; + public static final int FILTER_TYPE_IP = Constants.DemuxFilterMainType.IP; + public static final int FILTER_TYPE_TLV = Constants.DemuxFilterMainType.TLV; + public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES, + FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD, + FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI, + FILTER_SUBTYPE_MMPT, FILTER_SUBTYPE_NTP, FILTER_SUBTYPE_IP_PAYLOAD, FILTER_SUBTYPE_IP, + FILTER_SUBTYPE_PAYLOAD_THROUGH, FILTER_SUBTYPE_TLV, FILTER_SUBTYPE_PTP, }) + public @interface FilterSubtype {} + + public static final int FILTER_SUBTYPE_UNDEFINED = 0; + public static final int FILTER_SUBTYPE_SECTION = 1; + public static final int FILTER_SUBTYPE_PES = 2; + public static final int FILTER_SUBTYPE_AUDIO = 3; + public static final int FILTER_SUBTYPE_VIDEO = 4; + public static final int FILTER_SUBTYPE_DOWNLOAD = 5; + public static final int FILTER_SUBTYPE_RECORD = 6; + public static final int FILTER_SUBTYPE_TS = 7; + public static final int FILTER_SUBTYPE_PCR = 8; + public static final int FILTER_SUBTYPE_TEMI = 9; + public static final int FILTER_SUBTYPE_MMPT = 10; + public static final int FILTER_SUBTYPE_NTP = 11; + public static final int FILTER_SUBTYPE_IP_PAYLOAD = 12; + public static final int FILTER_SUBTYPE_IP = 13; + public static final int FILTER_SUBTYPE_PAYLOAD_THROUGH = 14; + public static final int FILTER_SUBTYPE_TLV = 15; + 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({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV, FILTER_SETTINGS_ALP}) public @interface FilterSettingsType {} diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java new file mode 100644 index 000000000000..b3bd78076b60 --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerUtils.java @@ -0,0 +1,135 @@ +/* + * 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.content.Context; +import android.content.pm.PackageManager; +import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.TunerConstants.FilterSubtype; +import android.media.tv.tuner.TunerConstants.FilterType; + +/** + * Utility class for tuner framework. + * + * @hide + */ +public final class TunerUtils { + private static final String PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER; + + static void checkTunerPermission(Context context) { + if (context.checkCallingOrSelfPermission(PERMISSION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller must have " + PERMISSION + " permission."); + } + } + + static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) { + if (mainType == TunerConstants.FILTER_TYPE_TS) { + switch (subtype) { + case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + return Constants.DemuxTsFilterType.UNDEFINED; + case TunerConstants.FILTER_SUBTYPE_SECTION: + return Constants.DemuxTsFilterType.SECTION; + case TunerConstants.FILTER_SUBTYPE_PES: + return Constants.DemuxTsFilterType.PES; + case TunerConstants.FILTER_SUBTYPE_TS: + return Constants.DemuxTsFilterType.TS; + case TunerConstants.FILTER_SUBTYPE_AUDIO: + return Constants.DemuxTsFilterType.AUDIO; + case TunerConstants.FILTER_SUBTYPE_VIDEO: + return Constants.DemuxTsFilterType.VIDEO; + case TunerConstants.FILTER_SUBTYPE_PCR: + return Constants.DemuxTsFilterType.PCR; + case TunerConstants.FILTER_SUBTYPE_RECORD: + return Constants.DemuxTsFilterType.RECORD; + case TunerConstants.FILTER_SUBTYPE_TEMI: + return Constants.DemuxTsFilterType.TEMI; + default: + break; + } + } else if (mainType == TunerConstants.FILTER_TYPE_MMTP) { + switch (subtype) { + case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + return Constants.DemuxMmtpFilterType.UNDEFINED; + case TunerConstants.FILTER_SUBTYPE_SECTION: + return Constants.DemuxMmtpFilterType.SECTION; + case TunerConstants.FILTER_SUBTYPE_PES: + return Constants.DemuxMmtpFilterType.PES; + case TunerConstants.FILTER_SUBTYPE_MMPT: + return Constants.DemuxMmtpFilterType.MMTP; + case TunerConstants.FILTER_SUBTYPE_AUDIO: + return Constants.DemuxMmtpFilterType.AUDIO; + case TunerConstants.FILTER_SUBTYPE_VIDEO: + return Constants.DemuxMmtpFilterType.VIDEO; + case TunerConstants.FILTER_SUBTYPE_RECORD: + return Constants.DemuxMmtpFilterType.RECORD; + case TunerConstants.FILTER_SUBTYPE_DOWNLOAD: + return Constants.DemuxMmtpFilterType.DOWNLOAD; + default: + break; + } + + } else if (mainType == TunerConstants.FILTER_TYPE_IP) { + switch (subtype) { + case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + return Constants.DemuxIpFilterType.UNDEFINED; + case TunerConstants.FILTER_SUBTYPE_SECTION: + return Constants.DemuxIpFilterType.SECTION; + case TunerConstants.FILTER_SUBTYPE_NTP: + return Constants.DemuxIpFilterType.NTP; + case TunerConstants.FILTER_SUBTYPE_IP_PAYLOAD: + return Constants.DemuxIpFilterType.IP_PAYLOAD; + case TunerConstants.FILTER_SUBTYPE_IP: + return Constants.DemuxIpFilterType.IP; + case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH: + return Constants.DemuxIpFilterType.PAYLOAD_THROUGH; + default: + break; + } + } else if (mainType == TunerConstants.FILTER_TYPE_TLV) { + switch (subtype) { + case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + return Constants.DemuxTlvFilterType.UNDEFINED; + case TunerConstants.FILTER_SUBTYPE_SECTION: + return Constants.DemuxTlvFilterType.SECTION; + case TunerConstants.FILTER_SUBTYPE_TLV: + return Constants.DemuxTlvFilterType.TLV; + case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH: + return Constants.DemuxTlvFilterType.PAYLOAD_THROUGH; + default: + break; + } + } else if (mainType == TunerConstants.FILTER_TYPE_ALP) { + switch (subtype) { + case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + return Constants.DemuxAlpFilterType.UNDEFINED; + case TunerConstants.FILTER_SUBTYPE_SECTION: + return Constants.DemuxAlpFilterType.SECTION; + case TunerConstants.FILTER_SUBTYPE_PTP: + return Constants.DemuxAlpFilterType.PTP; + case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH: + return Constants.DemuxAlpFilterType.PAYLOAD_THROUGH; + default: + break; + } + } + throw new IllegalArgumentException( + "Invalid filter types. Main type=" + mainType + ", subtype=" + subtype); + } + + private TunerUtils() {} +} diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp index 37aca08643b2..c064de299b19 100644 --- a/media/jni/android_media_MediaMetricsJNI.cpp +++ b/media/jni/android_media_MediaMetricsJNI.cpp @@ -18,6 +18,7 @@ #include <binder/Parcel.h> #include <jni.h> +#include <media/IMediaMetricsService.h> #include <media/MediaMetricsItem.h> #include <nativehelper/JNIHelp.h> #include <variant> @@ -120,15 +121,15 @@ jobject MediaMetricsJNI::writeMetricsToBundle( return nullptr; } - bh.put("__key", item->getKey().c_str()); + bh.put(mediametrics::BUNDLE_KEY, item->getKey().c_str()); if (item->getPid() != -1) { - bh.put("__pid", (int32_t)item->getPid()); + bh.put(mediametrics::BUNDLE_PID, (int32_t)item->getPid()); } if (item->getTimestamp() > 0) { - bh.put("__timestamp", (int64_t)item->getTimestamp()); + bh.put(mediametrics::BUNDLE_TIMESTAMP, (int64_t)item->getTimestamp()); } if (item->getUid() != -1) { - bh.put("__uid", (int32_t)item->getUid()); + bh.put(mediametrics::BUNDLE_UID, (int32_t)item->getUid()); } for (const auto &prop : *item) { const char *name = prop.getName(); @@ -150,14 +151,12 @@ static jint android_media_MediaMetrics_submit_bytebuffer( return (jint)BAD_VALUE; } - // TODO: directly record item to MediaMetrics service. - mediametrics::Item item; - if (item.readFromByteString((char *)buffer, length) != NO_ERROR) { - ALOGW("%s: cannot read from byte string", __func__); - return (jint)BAD_VALUE; + sp<IMediaMetricsService> service = mediametrics::BaseItem::getService(); + if (service == nullptr) { + ALOGW("Cannot retrieve mediametrics service"); + return (jint)NO_INIT; } - item.selfrecord(); - return (jint)NO_ERROR; + return (jint)service->submitBuffer((char *)buffer, length); } // Helper function to convert a native PersistableBundle to a Java diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp index f0aa4c3f1ab6..0c1e9a2ad7bd 100644 --- a/media/jni/android_media_MediaMuxer.cpp +++ b/media/jni/android_media_MediaMuxer.cpp @@ -26,11 +26,15 @@ #include <unistd.h> #include <fcntl.h> +#include <android/api-level.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaMuxer.h> +extern "C" int android_get_application_target_sdk_version(); + namespace android { struct fields_t { @@ -229,10 +233,31 @@ static void android_media_MediaMuxer_stop(JNIEnv *env, jclass /* clazz */, status_t err = muxer->stop(); - if (err != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to stop the muxer"); - return; + if (android_get_application_target_sdk_version() >= __ANDROID_API_R__) { + switch (err) { + case OK: + break; + case ERROR_IO: { + jniThrowException(env, "java/lang/UncheckedIOException", + "Muxer stopped unexpectedly"); + return; + } + case ERROR_MALFORMED: { + jniThrowException(env, "java/io/IOError", + "Failure of reading or writing operation"); + return; + } + default: { + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to stop the muxer"); + return; + } + } + } else { + if (err != OK) { + jniThrowException(env, "java/lang/IllegalStateException", "Failed to stop the muxer"); + return; + } } } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 9304450dc271..0cac2af9adc3 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -312,6 +312,15 @@ int JTuner::tune(const FrontendSettings& settings) { 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; @@ -600,6 +609,25 @@ static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, job return tuner->tune(getFrontendSettings(env, type, settings)); } +static int android_media_tv_Tuner_stop_tune(JNIEnv, jobject) { + 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; +} + +static int android_media_tv_Tuner_set_lna(JNIEnv, jobject, jint, jboolean) { + return 0; +} + static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->getLnbIds(); @@ -850,6 +878,11 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_open_frontend_by_id }, { "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 }, { "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;", (void *)android_media_tv_Tuner_open_filter }, { "nativeGetLnbIds", "()Ljava/util/List;", diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 9f9fb2748581..d37a2d96c60b 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::FrontendEventType; 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 @@ struct JTuner : public RefBase { 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/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java index 6facec4bfe5b..41914b887b2b 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraErrorCollector.java @@ -969,7 +969,7 @@ public class CameraErrorCollector extends ErrorCollector { */ private static class IntInMatcher extends InMatcher<Integer> { public IntInMatcher(int[] values) { - Preconditions.checkNotNull("values", values); + Objects.requireNonNull(values, "values"); mValues = new ArrayList<>(values.length); for (int i : values) { mValues.add(i); @@ -1005,7 +1005,7 @@ public class CameraErrorCollector extends ErrorCollector { */ private static class BooleanInMatcher extends InMatcher<Boolean> { public BooleanInMatcher(boolean[] values) { - Preconditions.checkNotNull("values", values); + Objects.requireNonNull(values, "values"); mValues = new ArrayList<>(values.length); for (boolean i : values) { mValues.add(i); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/InMatcher.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/InMatcher.java index e25a140378d8..c77a042ae38b 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/InMatcher.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/InMatcher.java @@ -36,7 +36,7 @@ public class InMatcher<T> extends BaseMatcher<T> { protected Collection<T> mValues; public InMatcher(Collection<T> values) { - Preconditions.checkNotNull("values", values); + Objects.requireNonNull(values, "values"); mValues = values; } 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 6650f9618638..df6345fb7db0 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -20,6 +20,7 @@ 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; @@ -159,10 +160,55 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService publishRoutes(); } + @Override + public void onCreateSession(String packageName, String routeId, String controlCategory, + int sessionId) { + RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( + sessionId, packageName, controlCategory) + .addSelectedRoute(routeId) + .build(); + notifySessionCreated(sessionId, sessionInfo, null); + } + + @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(); + setSessionInfo(sessionId, newSessionInfo); + publishRoutes(); + } + + @Override + public void onRemoveRoute(int sessionId, String routeId) { + RouteSessionInfo sessionInfo = getSessionInfo(sessionId); + RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + .removeSelectedRoute(routeId) + .build(); + setSessionInfo(sessionId, newSessionInfo); + publishRoutes(); + } + + @Override + public void onTransferRoute(int sessionId, String routeId) { + RouteSessionInfo sessionInfo = getSessionInfo(sessionId); + RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) + .clearSelectedRoutes() + .addSelectedRoute(routeId) + .build(); + setSessionInfo(sessionId, newSessionInfo); + publishRoutes(); + } + void publishRoutes() { MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder() .addRoutes(mRoutes.values()) .build(); - setProviderInfo(info); + updateProviderInfo(info); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java index ab1feefd8b3c..38d5211bd0d4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java @@ -27,7 +27,7 @@ import android.provider.Settings; import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import javax.inject.Inject; @@ -54,7 +54,7 @@ public class CarDeviceProvisionedControllerImpl extends DeviceProvisionedControl private final ContentResolver mContentResolver; @Inject - public CarDeviceProvisionedControllerImpl(Context context, @MainHandler Handler mainHandler, + public CarDeviceProvisionedControllerImpl(Context context, @Main Handler mainHandler, BroadcastDispatcher broadcastDispatcher) { super(context, mainHandler, broadcastDispatcher); mContentResolver = context.getContentResolver(); diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java index e0b0922e913f..59a084e337ad 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java @@ -35,7 +35,7 @@ import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarDeviceProvisionedListener; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NavigationBarController; @@ -93,7 +93,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks DeviceProvisionedController deviceProvisionedController, CommandQueue commandQueue, Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListenerLazy, - @MainHandler Handler mainHandler, + @Main Handler mainHandler, Lazy<KeyguardStateController> keyguardStateControllerLazy, Lazy<NavigationBarController> navigationBarControllerLazy, SuperStatusBarViewFactory superStatusBarViewFactory, 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 1b171e87bf4a..867100fe5351 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -59,7 +59,6 @@ import com.android.systemui.BatteryMeterView; import com.android.systemui.Dependency; 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,6 +67,7 @@ import com.android.systemui.car.CarDeviceProvisionedListener; 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; @@ -141,6 +141,7 @@ import java.io.FileDescriptor; 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 +274,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, - UiOffloadThread uiOffloadThread, + @UiBackground Executor uiBgExecutor, NotificationMediaManager notificationMediaManager, NotificationLockscreenUserManager lockScreenUserManager, NotificationRemoteInputManager remoteInputManager, @@ -353,7 +354,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt notificationAlertingManager, displayMetrics, metricsLogger, - uiOffloadThread, + uiBgExecutor, notificationMediaManager, lockScreenUserManager, remoteInputManager, 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 7b21d9d8a2d7..e3bb293fa6f9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -25,12 +25,12 @@ 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.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; @@ -96,6 +96,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; 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 +144,7 @@ public class CarStatusBarModule { NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, - UiOffloadThread uiOffloadThread, + @UiBackground Executor uiBgExecutor, NotificationMediaManager notificationMediaManager, NotificationLockscreenUserManager lockScreenUserManager, NotificationRemoteInputManager remoteInputManager, @@ -222,7 +223,7 @@ public class CarStatusBarModule { notificationAlertingManager, displayMetrics, metricsLogger, - uiOffloadThread, + uiBgExecutor, notificationMediaManager, lockScreenUserManager, remoteInputManager, diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java index 2d57be177333..b2f8aad77dd4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java @@ -37,7 +37,7 @@ import android.widget.TextView; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; import javax.inject.Singleton; @@ -68,7 +68,7 @@ class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{ private OnHideListener mOnHideListener; @Inject - CarTrustAgentUnlockDialogHelper(Context context, @MainResources Resources resources, + CarTrustAgentUnlockDialogHelper(Context context, @Main Resources resources, UserManager userManager, WindowManager windowManager) { mContext = context; mResources = resources; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index 3d7486859686..f8fc3bbefb01 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -38,7 +38,7 @@ import androidx.recyclerview.widget.GridLayoutManager; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; import com.android.systemui.car.CarServiceProvider; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener; import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord; @@ -78,7 +78,7 @@ public class FullscreenUserSwitcher { @Inject public FullscreenUserSwitcher( Context context, - @MainResources Resources resources, + @Main Resources resources, UserManager userManager, CarServiceProvider carServiceProvider, CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper) { diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index d11b5c573ca9..7aa997e39307 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -37,12 +37,12 @@ import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; +import android.companion.Association; import android.companion.AssociationRequest; import android.companion.BluetoothDeviceFilter; import android.companion.BluetoothLeDeviceFilter; import android.companion.DeviceFilter; import android.companion.ICompanionDeviceDiscoveryService; -import android.companion.ICompanionDeviceDiscoveryServiceCallback; import android.companion.IFindDeviceCallback; import android.companion.WifiDeviceFilter; import android.content.BroadcastReceiver; @@ -63,6 +63,7 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; +import com.android.internal.infra.AndroidFuture; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.Preconditions; @@ -97,7 +98,7 @@ public class DeviceDiscoveryService extends Service { DevicesAdapter mDevicesAdapter; IFindDeviceCallback mFindCallback; - ICompanionDeviceDiscoveryServiceCallback mServiceCallback; + AndroidFuture<Association> mServiceCallback; boolean mIsScanning = false; @Nullable DeviceChooserActivity mActivity = null; @@ -107,7 +108,7 @@ public class DeviceDiscoveryService extends Service { public void startDiscovery(AssociationRequest request, String callingPackage, IFindDeviceCallback findCallback, - ICompanionDeviceDiscoveryServiceCallback serviceCallback) { + AndroidFuture serviceCallback) { if (DEBUG) { Log.i(LOG_TAG, "startDiscovery() called with: filter = [" + request @@ -303,23 +304,12 @@ public class DeviceDiscoveryService extends Service { } void onDeviceSelected(String callingPackage, String deviceAddress) { - try { - mServiceCallback.onDeviceSelected( - //TODO is this the right userId? - callingPackage, getUserId(), deviceAddress); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed to record association: " - + callingPackage + " <-> " + deviceAddress); - } + mServiceCallback.complete(new Association(getUserId(), deviceAddress, callingPackage)); } void onCancel() { if (DEBUG) Log.i(LOG_TAG, "onCancel()"); - try { - mServiceCallback.onDeviceSelectionCancel(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + mServiceCallback.cancel(true); } class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> { diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp index de92fcd5b2e8..4e49302debcd 100644 --- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp +++ b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp @@ -177,8 +177,7 @@ private: android::dataloader::ServiceParamsPtr) final { CHECK(ifs) << "ifs can't be null"; CHECK(statusListener) << "statusListener can't be null"; - ALOGE("[AdbDataLoader] onCreate: %s/%s/%d", params.staticArgs().c_str(), - params.packageName().c_str(), (int)params.dynamicArgs().size()); + ALOGE("[AdbDataLoader] onCreate: %d/%s/%s/%s/%d", params.type(), params.packageName().c_str(), params.className().c_str(), params.arguments().c_str(), (int)params.dynamicArgs().size()); if (params.dynamicArgs().empty()) { ALOGE("[AdbDataLoader] Invalid DataLoaderParams. Need in/out FDs."); @@ -204,7 +203,7 @@ private: } std::string logFile; - if (const auto packageName = extractPackageName(params.staticArgs()); !packageName.empty()) { + if (const auto packageName = extractPackageName(params.arguments()); !packageName.empty()) { logFile = android::base::GetProperty("adb.readlog." + packageName, ""); } if (logFile.empty()) { @@ -288,8 +287,7 @@ private: "inode=%d. Ignore.", static_cast<int>(ino)); mRequestedFiles.erase(fileId); - mStatusListener->reportStatus( - INCREMENTAL_DATA_LOADER_NO_CONNECTION); + mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION); } } sendRequest(mOutFd, BLOCK_MISSING, fileId, blockIdx); @@ -337,7 +335,7 @@ private: } if (res < 0) { ALOGE("[AdbDataLoader] failed to poll. Abort."); - mStatusListener->reportStatus(INCREMENTAL_DATA_LOADER_NO_CONNECTION); + mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION); break; } if (res == mEventFd) { @@ -346,7 +344,7 @@ private: } if (!readChunk(mInFd, data)) { ALOGE("[AdbDataLoader] failed to read a message. Abort."); - mStatusListener->reportStatus(INCREMENTAL_DATA_LOADER_NO_CONNECTION); + mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION); break; } auto remainingData = std::span(data); diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java index 23e2949372aa..ebca962a918e 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 @@ public class DataUsageUtils { } 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/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java index 5cae6116a59c..d98f50beadf5 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 class DataUsageUtilsTest { 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 class DataUsageUtilsTest { 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 000000000000..b54b74ee1d3e --- /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 000000000000..dc41a77d0e2d --- /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 c19a340498e7..fb558abe3865 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.IOException; 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { @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 @@ public class SettingsBackupAgent extends BackupAgentHelper { } // 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { } } + 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { * 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 @@ public class SettingsBackupAgent extends BackupAgentHelper { 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 57e22db6a55c..e6508823c7e3 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 android.test.mock.MockContentResolver; 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 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { 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 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { 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 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { } } + 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/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 4fb3be2d588f..c238d7d49f9b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -42,6 +42,7 @@ android_library { "res", ], static_libs: [ + "WindowManager-Shell", "SystemUIPluginLib", "SystemUISharedLib", "SettingsLib", diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java index 18dc185c6fbe..0c7e56e0715e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java @@ -37,6 +37,13 @@ public class ActivityCompat { } /** + * @see Activity#unregisterRemoteAnimations + */ + public void unregisterRemoteAnimations() { + mWrapped.unregisterRemoteAnimations(); + } + + /** * @see android.view.ViewDebug#dumpv2(View, ByteArrayOutputStream) */ public boolean encodeViewHierarchy(ByteArrayOutputStream out) { diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index caf5ee02df06..255693bd6471 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -43,7 +43,7 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.settingslib.WirelessUtils; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.WakefulnessLifecycle; import java.util.List; @@ -608,7 +608,7 @@ public class CarrierTextController { private boolean mShowMissingSim; @Inject - public Builder(Context context, @MainResources Resources resources) { + public Builder(Context context, @Main Resources resources) { mContext = context; mSeparator = resources.getString( com.android.internal.R.string.kg_text_message_separator); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index fb7a269863a2..694c623ac11f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -101,7 +101,7 @@ import com.android.systemui.DumpController; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainLooper; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -1496,7 +1496,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting @Inject - protected KeyguardUpdateMonitor(Context context, @MainLooper Looper mainLooper, + protected KeyguardUpdateMonitor(Context context, @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher, DumpController dumpController) { mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 94e7c68c831d..a9ca04b499a5 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -39,10 +39,8 @@ 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.BgHandler; -import com.android.systemui.dagger.qualifiers.BgLooper; -import com.android.systemui.dagger.qualifiers.MainHandler; -import com.android.systemui.dagger.qualifiers.MainLooper; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; @@ -297,10 +295,10 @@ public class Dependency { @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager; @Inject Lazy<AutoHideController> mAutoHideController; @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener; - @Inject @BgLooper Lazy<Looper> mBgLooper; - @Inject @BgHandler Lazy<Handler> mBgHandler; - @Inject @MainLooper Lazy<Looper> mMainLooper; - @Inject @MainHandler Lazy<Handler> mMainHandler; + @Inject @Background Lazy<Looper> mBgLooper; + @Inject @Background Lazy<Handler> mBgHandler; + @Inject @Main Lazy<Looper> mMainLooper; + @Inject @Main Lazy<Handler> mMainHandler; @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler; @Nullable @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail; diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java index 41dd5bbf272b..82e665bdf5ac 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java @@ -24,7 +24,7 @@ import android.util.SparseArray; import com.android.internal.messages.nano.SystemMessageProto; import com.android.systemui.appops.AppOpsController; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.util.Assert; @@ -50,7 +50,7 @@ public class ForegroundServiceController { @Inject public ForegroundServiceController(NotificationEntryManager entryManager, - AppOpsController appOpsController, @MainHandler Handler mainHandler) { + AppOpsController appOpsController, @Main Handler mainHandler) { mEntryManager = entryManager; mMainHandler = mainHandler; appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 9ce277eabbe8..0e736dcd11a8 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -68,7 +68,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.plugins.qs.QS; @@ -147,7 +147,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Inject public ScreenDecorations(Context context, Lazy<StatusBar> statusBarLazy, - @MainHandler Handler handler, + @Main Handler handler, BroadcastDispatcher broadcastDispatcher, TunerService tunerService) { super(context); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index 164595742ae0..41d83148e093 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -27,7 +27,7 @@ import android.os.SystemProperties; import android.util.Slog; import com.android.internal.os.BinderInternal; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; @@ -41,7 +41,7 @@ public class SystemUIService extends Service { private final Handler mMainHandler; @Inject - public SystemUIService(@MainHandler Handler mainHandler) { + public SystemUIService(@Main Handler mainHandler) { super(); mMainHandler = mainHandler; } diff --git a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java index a726b42f714f..d5a46de980f9 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 class UiOffloadThread { 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/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 6178ff2ab2cb..895207d37816 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -22,7 +22,7 @@ import android.os.Handler; import android.provider.Settings; import com.android.systemui.SystemUI; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; import javax.inject.Singleton; @@ -36,7 +36,7 @@ public class WindowMagnification extends SystemUI { private final Handler mHandler; @Inject - public WindowMagnification(Context context, @MainHandler Handler mainHandler) { + public WindowMagnification(Context context, @Main Handler mainHandler) { super(context); mHandler = mainHandler; } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index bad6b5458aa3..b0831232bcd4 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -30,7 +30,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.Background; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -81,7 +81,7 @@ public class AppOpsControllerImpl implements AppOpsController, }; @Inject - public AppOpsControllerImpl(Context context, @BgLooper Looper bgLooper, + public AppOpsControllerImpl(Context context, @Background Looper bgLooper, DumpController dumpController) { this(context, bgLooper, new PermissionFlagsCache(context), dumpController); } diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt index 5cc70bc27c2a..8cb0cc5db1d3 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt @@ -28,8 +28,8 @@ import android.util.Log import android.util.SparseArray import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Dumpable -import com.android.systemui.dagger.qualifiers.BgLooper -import com.android.systemui.dagger.qualifiers.MainHandler +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject @@ -62,8 +62,8 @@ private const val DEBUG = true @Singleton open class BroadcastDispatcher @Inject constructor ( private val context: Context, - @MainHandler private val mainHandler: Handler, - @BgLooper private val bgLooper: Looper + @Main private val mainHandler: Handler, + @Background private val bgLooper: Looper ) : Dumpable { // Only modify in BG thread diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java index 20742d6d2358..eb014ed0fc48 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.internal.logging.MetricsLogger; 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.statusbar.StatusBarState; 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 @@ public class FalsingManagerImpl implements FalsingManager { 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 @@ public class FalsingManagerImpl implements FalsingManager { } }; - 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 @@ public class FalsingManagerImpl implements FalsingManager { } // 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 @@ public class FalsingManagerImpl implements FalsingManager { 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 db85fa0a3203..f475948b43f7 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -30,7 +30,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.classifier.brightline.FalsingDataProvider; -import com.android.systemui.dagger.qualifiers.MainHandler; +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.DeviceConfigProxy; 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 @@ public class FalsingManagerProxy implements FalsingManager { private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener; private final DeviceConfigProxy mDeviceConfig; private boolean mBrightlineEnabled; + private Executor mUiBgExecutor; @Inject FalsingManagerProxy(Context context, PluginManager pluginManager, - @MainHandler Handler handler, + @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 class FalsingManagerProxy implements FalsingManager { } public void onPluginDisconnected(FalsingPlugin plugin) { - mInternalFalsingManager = new FalsingManagerImpl(context); + mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor); } }; @@ -117,7 +122,7 @@ public class FalsingManagerProxy implements FalsingManager { 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/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 0d161ceeecd5..8d10552332ba 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -25,8 +25,6 @@ import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.os.HandlerThread; -import android.os.Looper; -import android.os.Process; import android.os.ServiceManager; import android.util.DisplayMetrics; import android.view.IWindowManager; @@ -35,10 +33,8 @@ import android.view.LayoutInflater; import com.android.internal.logging.MetricsLogger; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; -import com.android.systemui.dagger.qualifiers.BgHandler; -import com.android.systemui.dagger.qualifiers.BgLooper; -import com.android.systemui.dagger.qualifiers.MainHandler; -import com.android.systemui.dagger.qualifiers.MainLooper; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.PluginInitializerImpl; @@ -82,35 +78,6 @@ public class DependencyProvider { return new Handler(thread.getLooper()); } - @Singleton - @Provides - @BgLooper - public Looper provideBgLooper() { - HandlerThread thread = new HandlerThread("SysUiBg", - Process.THREAD_PRIORITY_BACKGROUND); - thread.start(); - return thread.getLooper(); - } - - /** Main Looper */ - @Provides - @MainLooper - public Looper provideMainLooper() { - return Looper.getMainLooper(); - } - - @Provides - @BgHandler - public Handler provideBgHandler(@BgLooper Looper bgLooper) { - return new Handler(bgLooper); - } - - @Provides - @MainHandler - public Handler provideMainHandler(@MainLooper Looper mainLooper) { - return new Handler(mainLooper); - } - /** */ @Provides public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) { @@ -175,7 +142,7 @@ public class DependencyProvider { @Singleton @Provides public NightDisplayListener provideNightDisplayListener(Context context, - @BgHandler Handler bgHandler) { + @Background Handler bgHandler) { return new NightDisplayListener(context, bgHandler); } @@ -188,7 +155,7 @@ public class DependencyProvider { @Singleton @Provides public NavigationBarController provideNavigationBarController(Context context, - @MainHandler Handler mainHandler, CommandQueue commandQueue) { + @Main Handler mainHandler, CommandQueue commandQueue) { return new NavigationBarController(context, mainHandler, commandQueue); } @@ -201,7 +168,7 @@ public class DependencyProvider { @Singleton @Provides public AutoHideController provideAutoHideController(Context context, - @MainHandler Handler mainHandler, + @Main Handler mainHandler, NotificationRemoteInputManager notificationRemoteInputManager, IWindowManager iWindowManager) { return new AutoHideController(context, mainHandler, notificationRemoteInputManager, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index 534f350e4898..0b73ab6f9136 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -44,8 +44,8 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.systemui.dagger.qualifiers.BgHandler; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.PackageManagerWrapper; import javax.inject.Singleton; @@ -126,7 +126,7 @@ public class SystemServicesModule { @Provides @Nullable static LocalBluetoothManager provideLocalBluetoothController(Context context, - @BgHandler Handler bgHandler) { + @Background Handler bgHandler) { return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); } @@ -150,7 +150,7 @@ public class SystemServicesModule { } @Provides - @MainResources + @Main static Resources provideResources(Context context) { return context.getResources(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java deleted file mode 100644 index 3daeda550b4c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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; - -@Qualifier -@Documented -@Retention(RUNTIME) -public @interface MainResources { - // TODO: use attribute to get other, non-main resources? -} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/UiBackground.java index 79661fa4a738..bf2237aa8c11 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/UiBackground.java @@ -23,8 +23,12 @@ 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 MainHandler { +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 25ac8f868435..f6f3b9985371 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 @@ import javax.inject.Singleton; 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 class DismissCallbackRegistry { 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 class DismissCallbackRegistry { 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 beba203b9702..9fcf0221e41a 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.Dependency; 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 com.android.systemui.util.InjectionInflationController; 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { BroadcastDispatcher broadcastDispatcher, StatusBarWindowController statusBarWindowController, Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManagerLazy, - DismissCallbackRegistry dismissCallbackRegistry) { + DismissCallbackRegistry dismissCallbackRegistry, + @UiBackground Executor uiBgExecutor) { super(context); mFalsingManager = falsingManager; mLockPatternUtils = lockPatternUtils; @@ -697,6 +699,7 @@ public class KeyguardViewMediator extends SystemUI { mStatusBarWindowController = statusBarWindowController; mStatusBarKeyguardViewManagerLazy = statusBarKeyguardViewManagerLazy; mDismissCallbackRegistry = dismissCallbackRegistry; + mUiBgExecutor = uiBgExecutor; } public void userActivity() { @@ -1662,7 +1665,7 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { } private void updateActivityLockScreenState(boolean showing, boolean aodShowing) { - mUiOffloadThread.submit(() -> { + mUiBgExecutor.execute(() -> { if (DEBUG) { Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")"); } @@ -1854,7 +1857,7 @@ public class KeyguardViewMediator extends SystemUI { // 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 @@ public class KeyguardViewMediator extends SystemUI { } }); 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 0a89017ff05d..f39d1ecb0585 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 @@ public class PipManager implements BasePipManager { mMenuController.onActivityPinned(); mAppOpsListener.onActivityPinned(packageName); - Dependency.get(UiOffloadThread.class).submit(() -> { + Dependency.get(UiOffloadThread.class).execute(() -> { WindowManagerWrapper.getInstance().setPipVisibility(true); }); } @@ -114,7 +114,7 @@ public class PipManager implements BasePipManager { 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 696db683a7d0..1d92375a7541 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 @@ public class PipManager implements BasePipManager { } 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/qs/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java index ac94858cff27..fb106425ec63 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java @@ -32,8 +32,8 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; import com.android.keyguard.CarrierTextController; -import com.android.systemui.dagger.qualifiers.BgHandler; -import com.android.systemui.dagger.qualifiers.MainLooper; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.NetworkController; @@ -111,7 +111,7 @@ public class QSCarrierGroupController { } private QSCarrierGroupController(QSCarrierGroup view, ActivityStarter activityStarter, - @BgHandler Handler bgHandler, @MainLooper Looper mainLooper, + @Background Handler bgHandler, @Main Looper mainLooper, NetworkController networkController, CarrierTextController.Builder carrierTextControllerBuilder) { mActivityStarter = activityStarter; @@ -308,8 +308,8 @@ public class QSCarrierGroupController { private final CarrierTextController.Builder mCarrierTextControllerBuilder; @Inject - public Builder(ActivityStarter activityStarter, @BgHandler Handler handler, - @MainLooper Looper looper, NetworkController networkController, + public Builder(ActivityStarter activityStarter, @Background Handler handler, + @Main Looper looper, NetworkController networkController, CarrierTextController.Builder carrierTextControllerBuilder) { mActivityStarter = activityStarter; mHandler = handler; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index c01bc8fe19e6..86ed274e9b5c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -34,8 +34,8 @@ import com.android.systemui.DumpController; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.BgLooper; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; @@ -96,8 +96,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D public QSTileHost(Context context, StatusBarIconController iconController, QSFactoryImpl defaultFactory, - @MainHandler Handler mainHandler, - @BgLooper Looper bgLooper, + @Main Handler mainHandler, + @Background Looper bgLooper, PluginManager pluginManager, TunerService tunerService, Provider<AutoTileManager> autoTiles, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index c9813db525f9..02c4beb7ac1b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -68,7 +68,7 @@ import android.widget.TextView; import android.widget.Toast; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.phone.StatusBar; @@ -186,7 +186,7 @@ public class GlobalScreenshot { */ @Inject public GlobalScreenshot( - Context context, @MainResources Resources resources, LayoutInflater layoutInflater, + Context context, @Main Resources resources, LayoutInflater layoutInflater, ScreenshotNotificationsController screenshotNotificationsController) { mContext = context; mNotificationsController = screenshotNotificationsController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 61043fbc1bc9..a8188b3ca35c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -35,7 +35,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.systemui.Dependency; import com.android.systemui.assist.AssistHandleViewController; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue.Callbacks; @@ -65,7 +65,7 @@ public class NavigationBarController implements Callbacks { SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>(); @Inject - public NavigationBarController(Context context, @MainHandler Handler handler, + public NavigationBarController(Context context, @Main Handler handler, CommandQueue commandQueue) { mContext = context; mHandler = handler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 97dd3daae341..8dd801b0484f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -30,7 +30,7 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.util.Log; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins; import java.util.ArrayList; @@ -58,7 +58,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { public NotificationListener( Context context, NotificationManager notificationManager, - @MainHandler Handler mainHandler) { + @Main Handler mainHandler) { mContext = context; mNotificationManager = notificationManager; mMainHandler = mainHandler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 0f3f6b7d9222..2e369b3295b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -48,7 +48,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.recents.OverviewProxyService; @@ -190,7 +190,7 @@ public class NotificationLockscreenUserManagerImpl implements IStatusBarService iStatusBarService, KeyguardManager keyguardManager, StatusBarStateController statusBarStateController, - @MainHandler Handler mainHandler, + @Main Handler mainHandler, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController) { mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index f6f3ac1b5aaf..43d0399c6d62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -50,7 +50,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -263,7 +263,7 @@ public class NotificationRemoteInputManager implements Dumpable { NotificationEntryManager notificationEntryManager, Lazy<StatusBar> statusBarLazy, StatusBarStateController statusBarStateController, - @MainHandler Handler mainHandler, + @Main Handler mainHandler, RemoteInputUriController remoteInputUriController) { mContext = context; mLockscreenUserManager = lockscreenUserManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 1648196ec0ea..6b0b5dfeaf40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -27,7 +27,7 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -93,7 +93,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private boolean mIsHandleDynamicPrivacyChangeScheduled; @Inject - public NotificationViewHierarchyManager(Context context, @MainHandler Handler mainHandler, + public NotificationViewHierarchyManager(Context context, @Main Handler mainHandler, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, 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 49bed15c79a8..93f58053f486 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.Dependency; 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 class InstantAppNotifier extends SystemUI 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 @@ public class InstantAppNotifier extends SystemUI 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/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java index 1b57308f1c0f..99718abb6492 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java @@ -23,7 +23,7 @@ import android.view.View; import androidx.collection.ArraySet; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -62,7 +62,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl @Inject public VisualStabilityManager( - NotificationEntryManager notificationEntryManager, @MainHandler Handler handler) { + NotificationEntryManager notificationEntryManager, @Main Handler handler) { mHandler = handler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java index ee841c2b4e14..62342b13f9cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java @@ -24,7 +24,7 @@ import android.util.ArraySet; import com.android.systemui.ForegroundServiceController; import com.android.systemui.appops.AppOpsController; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender; @@ -63,7 +63,7 @@ public class ForegroundCoordinator implements Coordinator { public ForegroundCoordinator( ForegroundServiceController foregroundServiceController, AppOpsController appOpsController, - @MainHandler Handler mainHandler) { + @Main Handler mainHandler) { mForegroundServiceController = foregroundServiceController; mAppOpsController = appOpsController; mMainHandler = mainHandler; 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 77ccf19f65ee..3e1b5bd0571e 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 androidx.annotation.Nullable; 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.Collection; 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 @@ public class NotificationLogger implements StateListener { // 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 @@ public class NotificationLogger implements StateListener { @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 @@ public class NotificationLogger implements StateListener { 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 @@ public class NotificationLogger implements StateListener { * 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 @@ public class NotificationLogger implements StateListener { } 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/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt index e9d6a0fcde83..ec1d6deb1b8f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt @@ -23,7 +23,7 @@ import android.os.Handler import android.os.UserHandle import android.provider.Settings import android.view.View -import com.android.systemui.dagger.qualifiers.MainHandler +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager import javax.inject.Inject @@ -164,7 +164,7 @@ private class PeopleHubViewModelFactoryImpl( @Singleton class PeopleHubSettingChangeDataSourceImpl @Inject constructor( - @MainHandler private val handler: Handler, + @Main private val handler: Handler, context: Context ) : DataSource<Boolean> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 9dd7f48086c9..6f2abba128d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -42,7 +42,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLifetimeExtender; @@ -108,7 +108,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx @Inject public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager, - Lazy<StatusBar> statusBarLazy, @MainHandler Handler mainHandler, + Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler, AccessibilityManager accessibilityManager) { mContext = context; mVisualStabilityManager = visualStabilityManager; 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 90ea6e344226..0a1a2fe3ee54 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 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp 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/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java index f9b936763308..3165597d4b9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java @@ -23,7 +23,7 @@ import android.util.Log; import android.view.IWindowManager; import android.view.MotionEvent; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.NotificationRemoteInputManager; import javax.inject.Inject; @@ -52,7 +52,7 @@ public class AutoHideController { }; @Inject - public AutoHideController(Context context, @MainHandler Handler handler, + public AutoHideController(Context context, @Main Handler handler, NotificationRemoteInputManager notificationRemoteInputManager, IWindowManager iWindowManager) { mHandler = handler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 837517e2cc9b..0680c7f70b97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -21,7 +21,7 @@ import android.os.Handler; import android.provider.Settings.Secure; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.SecureSetting; @@ -57,7 +57,7 @@ public class AutoTileManager { @Inject public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host, - @BgHandler Handler handler, + @Background Handler handler, HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 250f7300ba56..48805204160f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -37,7 +37,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -165,7 +165,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp StatusBarWindowController statusBarWindowController, KeyguardStateController keyguardStateController, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, - @MainResources Resources resources, + @Main Resources resources, KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters, MetricsLogger metricsLogger, DumpController dumpController) { mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index bc482353753d..f5999f5c8294 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -25,7 +25,7 @@ import android.provider.Settings; import android.util.MathUtils; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.doze.DozeScreenState; import com.android.systemui.tuner.TunerService; @@ -58,7 +58,7 @@ public class DozeParameters implements TunerService.Tunable, @Inject protected DozeParameters( - @MainResources Resources resources, + @Main Resources resources, AmbientDisplayConfiguration ambientDisplayConfiguration, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, PowerManager powerManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index 183adeb037d6..4ee13bf754dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -43,7 +43,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.NotificationMediaManager; import libcore.io.IoUtils; @@ -83,7 +83,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen KeyguardUpdateMonitor keyguardUpdateMonitor, DumpController dumpController, NotificationMediaManager mediaManager, - @MainHandler Handler mainHandler) { + @Main Handler mainHandler) { dumpController.registerDumpable(getClass().getSimpleName(), this); mWallpaperManager = wallpaperManager; mCurrentUserId = ActivityManager.getCurrentUser(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index ebe211754b2b..d4cf272e8077 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -93,7 +93,7 @@ import com.android.systemui.R; import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.model.SysUiState; @@ -274,7 +274,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback CommandQueue commandQueue, Divider divider, Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy, ShadeController shadeController, - @MainHandler Handler mainHandler) { + @Main Handler mainHandler) { mAccessibilityManagerWrapper = accessibilityManagerWrapper; mDeviceProvisionedController = deviceProvisionedController; mStatusBarStateController = statusBarStateController; 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 1df9411019d2..d6336ed3e18a 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.Log; 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.FileDescriptor; 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 @@ public class NavigationModeController implements Dumpable { 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 @@ public class NavigationModeController implements Dumpable { @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 @@ public class NavigationModeController implements Dumpable { 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 class NavigationModeController implements Dumpable { } 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/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 2f39c138bce1..5b34aa7781c1 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 android.util.Log; 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.UserInfoController; 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 @@ public class PhoneStatusBarPolicy 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 @@ public class PhoneStatusBarPolicy 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 @@ public class PhoneStatusBarPolicy 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 @@ public class PhoneStatusBarPolicy // 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/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 4c5bbce05261..2b9fc8d3bfbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -45,7 +45,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; @@ -192,7 +192,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo @Inject public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, - @MainResources Resources resources, + @Main Resources resources, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor, DockManager dockManager) { 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 312f9c181121..aaed61041c6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -136,13 +136,13 @@ import com.android.systemui.Prefs; 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 +232,7 @@ import java.io.PrintWriter; 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; @@ -471,7 +472,7 @@ public class StatusBar extends SystemUI implements DemoMode, private ViewMediatorCallback mKeyguardViewMediatorCallback; private final ScrimController mScrimController; protected DozeScrimController mDozeScrimController; - private final UiOffloadThread mUiOffloadThread; + private final Executor mUiBgExecutor; protected boolean mDozing; @@ -634,7 +635,7 @@ public class StatusBar extends SystemUI implements DemoMode, NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, - UiOffloadThread uiOffloadThread, + @UiBackground Executor uiBgExecutor, NotificationMediaManager notificationMediaManager, NotificationLockscreenUserManager lockScreenUserManager, NotificationRemoteInputManager remoteInputManager, @@ -708,7 +709,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationAlertingManager = notificationAlertingManager; mDisplayMetrics = displayMetrics; mMetricsLogger = metricsLogger; - mUiOffloadThread = uiOffloadThread; + mUiBgExecutor = uiBgExecutor; mMediaManager = notificationMediaManager; mLockscreenUserManager = lockScreenUserManager; mRemoteInputManager = remoteInputManager; @@ -885,7 +886,7 @@ public class StatusBar extends SystemUI implements DemoMode, // 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); @@ -2840,7 +2841,7 @@ public class StatusBar extends SystemUI implements DemoMode, notificationLoad = 1; } final int finalNotificationLoad = notificationLoad; - mUiOffloadThread.submit(() -> { + mUiBgExecutor.execute(() -> { try { mBarService.onPanelRevealed(clearNotificationEffects, finalNotificationLoad); @@ -2849,7 +2850,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }); } else { - mUiOffloadThread.submit(() -> { + mUiBgExecutor.execute(() -> { try { mBarService.onPanelHidden(); } catch (RemoteException ex) { @@ -4019,7 +4020,7 @@ public class StatusBar extends SystemUI implements DemoMode, } 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 e31c53abe988..ce563813953f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java @@ -27,11 +27,11 @@ import androidx.annotation.Nullable; 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.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; @@ -75,6 +75,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; 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 +123,7 @@ public class StatusBarModule { NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, - UiOffloadThread uiOffloadThread, + @UiBackground Executor uiBgExecutor, NotificationMediaManager notificationMediaManager, NotificationLockscreenUserManager lockScreenUserManager, NotificationRemoteInputManager remoteInputManager, @@ -197,7 +198,7 @@ public class StatusBarModule { notificationAlertingManager, displayMetrics, metricsLogger, - uiOffloadThread, + uiBgExecutor, notificationMediaManager, lockScreenUserManager, remoteInputManager, 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 3123f8dacada..661a7b1319a5 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.IStatusBarService; 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.BgHandler; -import com.android.systemui.dagger.qualifiers.MainHandler; +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.notification.row.ExpandableNotificationRow 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 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final Handler mBackgroundHandler; private final ActivityIntentHelper mActivityIntentHelper; private final BubbleController mBubbleController; + private final Executor mUiBgExecutor; private boolean mIsCollapsingToShowActivityOverLockscreen; @@ -133,7 +135,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit 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 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mGroupManager = groupManager; mLockPatternUtils = lockPatternUtils; mBackgroundHandler = backgroundHandler; + mUiBgExecutor = uiBgExecutor; mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onPendingEntryAdded(NotificationEntry entry) { @@ -418,7 +421,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } 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 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit 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; @@ -549,8 +553,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationInterruptionStateProvider notificationInterruptionStateProvider, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, - @MainHandler Handler mainThreadHandler, - @BgHandler Handler backgroundHandler, + @Main Handler mainThreadHandler, + @Background Handler backgroundHandler, + @UiBackground Executor uiBgExecutor, ActivityIntentHelper activityIntentHelper, BubbleController bubbleController, ShadeController shadeController, @@ -576,6 +581,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mLockPatternUtils = lockPatternUtils; mMainThreadHandler = mainThreadHandler; mBackgroundHandler = backgroundHandler; + mUiBgExecutor = uiBgExecutor; mActivityIntentHelper = activityIntentHelper; mBubbleController = bubbleController; mShadeController = shadeController; @@ -624,6 +630,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mLockPatternUtils, mMainThreadHandler, mBackgroundHandler, + mUiBgExecutor, mActivityIntentHelper, mBubbleController); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index f94b2eea8154..ce498a39d941 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -41,7 +41,7 @@ import android.view.WindowManager.LayoutParams; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; @@ -105,7 +105,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat ConfigurationController configurationController, KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor, SuperStatusBarViewFactory superStatusBarViewFactory, - @MainResources Resources resources) { + @Main Resources resources) { mContext = context; mWindowManager = windowManager; mActivityManager = activityManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index dc809066524f..f132058b5329 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -34,8 +34,8 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.utils.PowerUtil; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.BgHandler; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.power.EnhancedEstimates; import java.io.FileDescriptor; @@ -82,7 +82,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC @Inject BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates, PowerManager powerManager, BroadcastDispatcher broadcastDispatcher, - @MainHandler Handler mainHandler, @BgHandler Handler bgHandler) { + @Main Handler mainHandler, @Background Handler bgHandler) { mContext = context; mMainHandler = mainHandler; mBgHandler = bgHandler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 6ededd2460ce..0fc3d8481907 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -34,8 +34,8 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfile; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; -import com.android.systemui.dagger.qualifiers.BgLooper; -import com.android.systemui.dagger.qualifiers.MainLooper; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -75,8 +75,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa /** */ @Inject - public BluetoothControllerImpl(Context context, @BgLooper Looper bgLooper, - @MainLooper Looper mainLooper, @Nullable LocalBluetoothManager localBluetoothManager) { + public BluetoothControllerImpl(Context context, @Background Looper bgLooper, + @Main Looper mainLooper, @Nullable LocalBluetoothManager localBluetoothManager) { mLocalBluetoothManager = localBluetoothManager; mBgHandler = new Handler(bgLooper); mHandler = new H(mainLooper); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index f6b770cd16b9..a3e2e7619bac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -25,7 +25,7 @@ import android.provider.Settings.Secure; import android.util.Log; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; @@ -50,7 +50,7 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen /** */ @Inject - public DeviceProvisionedControllerImpl(Context context, @MainHandler Handler mainHandler, + public DeviceProvisionedControllerImpl(Context context, @Main Handler mainHandler, BroadcastDispatcher broadcastDispatcher) { super(broadcastDispatcher); mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index cd6ec05d90ec..df9c3f4d6e26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -26,7 +26,7 @@ import android.os.HandlerExecutor; import android.os.UserManager; import android.util.Log; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -59,7 +59,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof * Controller used to retrieve information related to a hotspot. */ @Inject - public HotspotControllerImpl(Context context, @MainHandler Handler mainHandler) { + public HotspotControllerImpl(Context context, @Main Handler mainHandler) { mContext = context; mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index d36bd75dfdba..570f153a62c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -37,7 +37,7 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.BootCompleteCache; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.util.Utils; import java.util.ArrayList; @@ -69,7 +69,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio private final H mHandler = new H(); @Inject - public LocationControllerImpl(Context context, @BgLooper Looper bgLooper, + public LocationControllerImpl(Context context, @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 24492bf3ca5b..f20a47babe5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -64,7 +64,7 @@ import com.android.systemui.DemoMode; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup; @@ -177,7 +177,7 @@ public class NetworkControllerImpl extends BroadcastReceiver * Construct this controller object and register for updates. */ @Inject - public NetworkControllerImpl(Context context, @BgLooper Looper bgLooper, + public NetworkControllerImpl(Context context, @Background Looper bgLooper, DeviceProvisionedController deviceProvisionedController, BroadcastDispatcher broadcastDispatcher) { this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index c1614587ea35..019ef3bca709 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -49,7 +49,7 @@ import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.settings.CurrentUserTracker; import java.io.FileDescriptor; @@ -101,7 +101,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi /** */ @Inject - public SecurityControllerImpl(Context context, @BgHandler Handler bgHandler, + public SecurityControllerImpl(Context context, @Background Handler bgHandler, BroadcastDispatcher broadcastDispatcher) { this(context, bgHandler, broadcastDispatcher, null); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java index 347d3009c3ec..86fe300820a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java @@ -28,7 +28,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; import javax.inject.Singleton; @@ -65,7 +65,7 @@ public final class SmartReplyConstants { private final KeyValueListParser mParser = new KeyValueListParser(','); @Inject - public SmartReplyConstants(@MainHandler Handler handler, Context context) { + public SmartReplyConstants(@Main Handler handler, Context context) { mHandler = handler; mContext = context; final Resources resources = mContext.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 13c0db938ca0..2907cd41a0db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -60,7 +60,7 @@ import com.android.systemui.Prefs.Key; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.qs.tiles.UserDetailView; @@ -113,7 +113,7 @@ public class UserSwitcherController implements Dumpable { @Inject public UserSwitcherController(Context context, KeyguardStateController keyguardStateController, - @MainHandler Handler handler, ActivityStarter activityStarter, + @Main Handler handler, ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index a2028e6c0c33..4376a0145826 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -40,7 +40,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.GlobalSetting; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.util.Utils; @@ -78,7 +78,7 @@ public class ZenModeControllerImpl extends CurrentUserTracker private NotificationManager.Policy mConsolidatedNotificationPolicy; @Inject - public ZenModeControllerImpl(Context context, @MainHandler Handler handler, + public ZenModeControllerImpl(Context context, @Main Handler handler, BroadcastDispatcher broadcastDispatcher) { super(broadcastDispatcher); mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index f9d39b0e6f96..7758aba52918 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -36,7 +36,7 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.Background; import com.google.android.collect.Sets; @@ -70,7 +70,7 @@ public class ThemeOverlayController extends SystemUI { @Inject public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDispatcher, - @BgHandler Handler bgHandler) { + @Background Handler bgHandler) { super(context); mBroadcastDispatcher = broadcastDispatcher; mBgHandler = bgHandler; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index ce0032e619e7..19f0ba24b0fd 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -35,7 +35,7 @@ import com.android.internal.util.ArrayUtils; import com.android.systemui.DejankUtils; import com.android.systemui.DemoMode; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -82,7 +82,7 @@ public class TunerServiceImpl extends TunerService { /** */ @Inject - public TunerServiceImpl(Context context, @MainHandler Handler mainHandler, + public TunerServiceImpl(Context context, @Main Handler mainHandler, LeakDetector leakDetector, BroadcastDispatcher broadcastDispatcher) { mContext = context; mContentResolver = mContext.getContentResolver(); 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 3e90581292ce..7cdba8607d86 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java @@ -18,14 +18,18 @@ package com.android.systemui.util.concurrency; import android.content.Context; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; +import android.os.Process; import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.dagger.qualifiers.BgLooper; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dagger.qualifiers.MainLooper; +import com.android.systemui.dagger.qualifiers.UiBackground; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import javax.inject.Singleton; import dagger.Module; import dagger.Provides; @@ -35,11 +39,51 @@ import dagger.Provides; */ @Module public abstract class ConcurrencyModule { + /** Background Looper */ + @Provides + @Singleton + @Background + public static Looper provideBgLooper() { + HandlerThread thread = new HandlerThread("SysUiBg", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + return thread.getLooper(); + } + + /** Main Looper */ + @Provides + @Main + public static Looper provideMainLooper() { + return Looper.getMainLooper(); + } + + /** + * Background Handler. + * + * Prefer the Background Executor when possible. + */ + @Provides + @Background + public static Handler provideBgHandler(@Background Looper bgLooper) { + return new Handler(bgLooper); + } + + /** + * Main Handler. + * + * Prefer the Main Executor when possible. + */ + @Provides + @Main + public static Handler provideMainHandler(@Main Looper mainLooper) { + return new Handler(mainLooper); + } + /** * Provide a Background-Thread Executor by default. */ @Provides - public static Executor provideExecutor(@BgLooper Looper looper) { + public static Executor provideExecutor(@Background Looper looper) { return new ExecutorImpl(new Handler(looper)); } @@ -48,7 +92,7 @@ public abstract class ConcurrencyModule { */ @Provides @Background - public static Executor provideBackgroundExecutor(@BgLooper Looper looper) { + public static Executor provideBackgroundExecutor(@Background Looper looper) { return new ExecutorImpl(new Handler(looper)); } @@ -65,7 +109,7 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor by default. */ @Provides - public static DelayableExecutor provideDelayableExecutor(@BgLooper Looper looper) { + public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) { return new ExecutorImpl(new Handler(looper)); } @@ -74,7 +118,7 @@ public abstract class ConcurrencyModule { */ @Provides @Background - public static DelayableExecutor provideBackgroundDelayableExecutor(@BgLooper Looper looper) { + public static DelayableExecutor provideBackgroundDelayableExecutor(@Background Looper looper) { return new ExecutorImpl(new Handler(looper)); } @@ -83,7 +127,19 @@ public abstract class ConcurrencyModule { */ @Provides @Main - public static DelayableExecutor provideMainDelayableExecutor(@MainLooper Looper looper) { + 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/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index bff405c0bee6..2c7c52e1c51f 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -47,7 +47,7 @@ import android.util.LongSparseArray; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SystemUI; -import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSHost; @@ -109,7 +109,7 @@ public class GarbageMonitor implements Dumpable { @Inject public GarbageMonitor( Context context, - @BgLooper Looper bgLooper, + @Background Looper bgLooper, LeakDetector leakDetector, LeakReporter leakReporter) { mContext = context.getApplicationContext(); diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index a96977a338a9..b5bede4ad31c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -26,7 +26,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.dagger.qualifiers.Main; import java.util.ArrayList; import java.util.List; @@ -65,7 +65,7 @@ public class ProximitySensor { }; @Inject - public ProximitySensor(@MainResources Resources resources, + public ProximitySensor(@Main Resources resources, AsyncSensorManager sensorManager) { mSensorManager = sensorManager; Sensor sensor = findBrightnessSensor(resources); 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 4316df1ced04..6fef59f6b995 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 @@ public interface SystemClock { /** @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 532ea050bfdd..f0c701490f13 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 class SystemClockImpl implements SystemClock { 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/src/com/android/systemui/wm/DisplayWindowController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java index aa56ffb18554..951d6dd4c3a3 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java @@ -31,7 +31,7 @@ import android.view.IDisplayWindowRotationController; import android.view.IWindowManager; import android.view.WindowContainerTransaction; -import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.Main; import java.util.ArrayList; @@ -99,13 +99,17 @@ public class DisplayWindowController { if (mDisplays.get(displayId) != null) { return; } + Display display = getDisplay(displayId); + if (display == null) { + // It's likely that the display is private to some app and thus not + // accessible by system-ui. + return; + } DisplayRecord record = new DisplayRecord(); record.mDisplayId = displayId; - // TODO(b/146566787): disabled for MultiDisplayActivityLaunchTests - // Display display = getDisplay(displayId); - // record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext - // : mContext.createDisplayContext(display); - // record.mDisplayLayout = new DisplayLayout(record.mContext, display); + record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext + : mContext.createDisplayContext(display); + record.mDisplayLayout = new DisplayLayout(record.mContext, display); mDisplays.put(displayId, record); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayAdded(displayId); @@ -124,14 +128,13 @@ public class DisplayWindowController { + " display."); return; } - // TODO(b/146566787): disabled for MultiDisplaySystemDecorationTests - // Display display = getDisplay(displayId); - // Context perDisplayContext = mContext; - // if (displayId != Display.DEFAULT_DISPLAY) { - // perDisplayContext = mContext.createDisplayContext(display); - // } - // dr.mContext = perDisplayContext.createConfigurationContext(newConfig); - // dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); + Display display = getDisplay(displayId); + Context perDisplayContext = mContext; + if (displayId != Display.DEFAULT_DISPLAY) { + perDisplayContext = mContext.createDisplayContext(display); + } + dr.mContext = perDisplayContext.createConfigurationContext(newConfig); + dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( displayId, newConfig); @@ -144,6 +147,9 @@ public class DisplayWindowController { public void onDisplayRemoved(int displayId) { mHandler.post(() -> { synchronized (mDisplays) { + if (mDisplays.get(displayId) == null) { + return; + } for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); } @@ -154,7 +160,7 @@ public class DisplayWindowController { }; @Inject - public DisplayWindowController(Context context, @MainHandler Handler mainHandler, + public DisplayWindowController(Context context, @Main Handler mainHandler, IWindowManager wmService) { mHandler = mainHandler; mContext = context; diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 819a7f6fde1a..c85d600e8447 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 @@ public abstract class SysuiTestCase { } 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 3561e3465898..a19d1df8a713 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.classifier.brightline.BrightLineFalsingManager; 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 @@ public class FalsingManagerProxyTest extends SysuiTestCase { private FalsingManagerProxy mProxy; private DeviceConfigProxy mDeviceConfig; private TestableLooper mTestableLooper; + private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @Before public void setup() { @@ -77,7 +80,7 @@ public class FalsingManagerProxyTest extends SysuiTestCase { @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 @@ public class FalsingManagerProxyTest extends SysuiTestCase { 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 7fa1dbeafced..a00cabc71a0c 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 androidx.test.runner.AndroidJUnit4; 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 @@ public class DismissCallbackRegistryTest extends SysuiTestCase { 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 class DismissCallbackRegistryTest extends SysuiTestCase { public void testCancelled() throws Exception { mDismissCallbackRegistry.addCallback(mMockCallback); mDismissCallbackRegistry.notifyDismissCancelled(); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mMockCallback).onDismissCancelled(); } @@ -61,7 +64,7 @@ public class DismissCallbackRegistryTest extends SysuiTestCase { mDismissCallbackRegistry.addCallback(mMockCallback); mDismissCallbackRegistry.addCallback(mMockCallback2); mDismissCallbackRegistry.notifyDismissCancelled(); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mMockCallback).onDismissCancelled(); verify(mMockCallback2).onDismissCancelled(); } @@ -70,7 +73,7 @@ public class DismissCallbackRegistryTest extends SysuiTestCase { public void testSucceeded() throws Exception { mDismissCallbackRegistry.addCallback(mMockCallback); mDismissCallbackRegistry.notifyDismissSucceeded(); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mMockCallback).onDismissSucceeded(); } @@ -79,7 +82,7 @@ public class DismissCallbackRegistryTest extends SysuiTestCase { mDismissCallbackRegistry.addCallback(mMockCallback); mDismissCallbackRegistry.addCallback(mMockCallback2); mDismissCallbackRegistry.notifyDismissSucceeded(); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mMockCallback).onDismissSucceeded(); verify(mMockCallback2).onDismissSucceeded(); } @@ -89,7 +92,7 @@ public class DismissCallbackRegistryTest extends SysuiTestCase { 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 a8a2b33215e5..64fbc1bac658 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.classifier.FalsingManagerFake; 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator = new KeyguardViewMediator( mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher, mStatusBarWindowController, () -> mStatusBarKeyguardViewManager, - mDismissCallbackRegistry); + mDismissCallbackRegistry, mUiBgExecutor); }); } 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 9f90396e08d5..6e9c2c8adb4a 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 @@ public class NotifListBuilderImplTest extends SysuiTestCase { 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 @@ public class NotifListBuilderImplTest extends SysuiTestCase { 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 4f1ffbe50fc1..2662c80dce1d 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 androidx.test.filters.SmallTest; 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 class ExpansionStateLoggerTest extends SysuiTestCase { 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { mLogger.onVisibilityChanged( Collections.singletonList(createNotificationVisibility(NOTIFICATION_KEY, true)), Collections.emptyList()); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mBarService).onNotificationExpansionChanged( NOTIFICATION_KEY, true, true, @@ -117,7 +117,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 @@ public class ExpansionStateLoggerTest extends SysuiTestCase { 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 e23d0ae89a09..d826ce1bbdd8 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 androidx.test.filters.SmallTest; 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.NotificationEntry; 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.Mockito; import org.mockito.MockitoAnnotations; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -82,6 +83,7 @@ public class NotificationLoggerTest extends SysuiTestCase { 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 @@ public class NotificationLoggerTest extends SysuiTestCase { .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 @@ public class NotificationLoggerTest extends SysuiTestCase { 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 @@ public class NotificationLoggerTest extends SysuiTestCase { Mockito.reset(mBarService); mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(); TestableLooper.get(this).processAllMessages(); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mBarService, never()).onNotificationVisibilityChanged(any(), any()); } @@ -152,11 +154,11 @@ public class NotificationLoggerTest extends SysuiTestCase { 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 @@ public class NotificationLoggerTest extends SysuiTestCase { 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/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 532192ba97ae..86b2a44a1acb 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.NotificationInterruptionState 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 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private SuperStatusBarViewFactory mSuperStatusBarViewFactory; @Mock private NotificationPanelView mNotificationPanelView; + private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private NotificationTestHelper mNotificationTestHelper; private ExpandableNotificationRow mNotificationRow; @@ -178,8 +181,9 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { 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/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index d3ae7a783404..ca229c9a6a57 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 @@ -78,7 +78,6 @@ 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; @@ -133,6 +132,8 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; 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; @@ -245,6 +246,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private StatusBarNotificationActivityStarter.Builder mStatusBarNotificationActivityStarterBuilder; private ShadeController mShadeController; + private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @Before public void setup() throws Exception { @@ -266,7 +268,7 @@ public class StatusBarTest extends SysuiTestCase { 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 +351,7 @@ public class StatusBarTest extends SysuiTestCase { mNotificationAlertingManager, new DisplayMetrics(), mMetricsLogger, - Dependency.get(UiOffloadThread.class), + mUiBgExecutor, mNotificationMediaManager, mLockscreenUserManager, mRemoteInputManager, @@ -641,7 +643,7 @@ public class StatusBarTest extends SysuiTestCase { 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 +661,7 @@ public class StatusBarTest extends SysuiTestCase { try { mStatusBar.handleVisibleToUserChanged(true); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mBarService, never()).onPanelHidden(); verify(mBarService, times(1)).onPanelRevealed(false, 1); } catch (RemoteException e) { @@ -678,7 +680,7 @@ public class StatusBarTest extends SysuiTestCase { try { mStatusBar.handleVisibleToUserChanged(true); - waitForUiOffloadThread(); + mUiBgExecutor.runAllReady(); verify(mBarService, never()).onPanelHidden(); verify(mBarService, times(1)).onPanelRevealed(true, 5); } catch (RemoteException e) { @@ -696,7 +698,7 @@ public class StatusBarTest extends SysuiTestCase { 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 f3c053058468..7c7ad5322d48 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 @@ public class FakeExecutor implements DelayableExecutor { 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 class FakeExecutor implements DelayableExecutor { */ 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 bd641243be12..abc283f40b1c 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 @@ public class FakeExecutorTest extends SysuiTestCase { 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 @@ public class FakeExecutorTest extends SysuiTestCase { 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 @@ public class FakeExecutorTest extends SysuiTestCase { 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 @@ public class FakeExecutorTest extends SysuiTestCase { 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 @@ public class FakeExecutorTest extends SysuiTestCase { // 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 @@ public class FakeExecutorTest extends SysuiTestCase { return null; }; - assertEquals(0, clock.uptimeMillis()); + assertEquals(10000, clock.uptimeMillis()); checkRunCounts.invoke(0, 0, 0, 0); fakeExecutor.execute(runnableA); @@ -227,8 +227,8 @@ public class FakeExecutorTest extends SysuiTestCase { 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 e94eaafdb692..601f88e0a9c6 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; - } - - @Override - public long currentThreadTimeMicro() { - long value = mCurrentThreadTimeMicro; - return value; + return mCurrentThreadTimeMillis; } - @Override - public long currentTimeMicro() { - long value = mCurrentTimeMicro; - return value; + public void setUptimeMillis(long uptime) { + advanceTime(uptime - mUptimeMillis); } - public void setUptimeMillis(long uptimeMillis) { - mUptimeMillis = uptimeMillis; - for (ClockTickListener listener : mListeners) { - listener.onUptimeMillis(mUptimeMillis); - } + public void advanceTime(long uptime) { + advanceTime(uptime, 0); } - public void setElapsedRealtime(long elapsedRealtime) { - mElapsedRealtime = elapsedRealtime; - for (ClockTickListener listener : mListeners) { - listener.onElapsedRealtime(mElapsedRealtime); + public void advanceTime(long uptime, long sleepTime) { + if (uptime < 0 || sleepTime < 0) { + throw new IllegalArgumentException("Time cannot go backwards"); } - } - public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) { - mElapsedRealtimeNanos = elapsedRealtimeNanos; - for (ClockTickListener listener : mListeners) { - listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos); - } - } + if (uptime > 0 || sleepTime > 0) { + mUptimeMillis += uptime; + mElapsedRealtime += uptime + sleepTime; - public void setCurrentThreadTimeMillis(long currentThreadTimeMillis) { - mCurrentThreadTimeMillis = currentThreadTimeMillis; - for (ClockTickListener listener : mListeners) { - listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis); - } - } + mCurrentThreadTimeMillis += Math.ceil(uptime * 0.5); - 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 @@ public class FakeSystemClock implements SystemClock { 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/WindowManager/OWNERS b/packages/WindowManager/OWNERS new file mode 100644 index 000000000000..063d4594f2fa --- /dev/null +++ b/packages/WindowManager/OWNERS @@ -0,0 +1,3 @@ +set noparent + +include ../../services/core/java/com/android/server/wm/OWNERS
\ No newline at end of file diff --git a/packages/WindowManager/Shell/Android.bp b/packages/WindowManager/Shell/Android.bp new file mode 100644 index 000000000000..b8934dc8c583 --- /dev/null +++ b/packages/WindowManager/Shell/Android.bp @@ -0,0 +1,29 @@ +// 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: "WindowManager-Shell", + srcs: [ + "src/**/*.java", + "src/**/I*.aidl", + ], + resource_dirs: [ + "res", + ], + manifest: "AndroidManifest.xml", + + platform_apis: true, + sdk_version: "current", + min_sdk_version: "system_current", +} diff --git a/packages/WindowManager/Shell/AndroidManifest.xml b/packages/WindowManager/Shell/AndroidManifest.xml new file mode 100644 index 000000000000..ea8a5c305029 --- /dev/null +++ b/packages/WindowManager/Shell/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?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.wm.shell"> +</manifest> diff --git a/packages/WindowManager/Shell/OWNERS b/packages/WindowManager/Shell/OWNERS new file mode 100644 index 000000000000..4390004f5f93 --- /dev/null +++ b/packages/WindowManager/Shell/OWNERS @@ -0,0 +1,4 @@ +# sysui owners +hwwang@google.com +mrenouf@google.com +winsonc@google.com
\ No newline at end of file diff --git a/packages/WindowManager/Shell/res/values/config.xml b/packages/WindowManager/Shell/res/values/config.xml new file mode 100644 index 000000000000..c894eb0133b5 --- /dev/null +++ b/packages/WindowManager/Shell/res/values/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** 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. +*/ +--> + +<resources> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java b/packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java index 2aadda1215d5..273bd27a221b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java +++ b/packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java @@ -14,17 +14,10 @@ * limitations under the License. */ -package com.android.systemui.dagger.qualifiers; +package com.android.wm.shell; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -import javax.inject.Qualifier; - -@Qualifier -@Documented -@Retention(RUNTIME) -public @interface BgLooper { +/** + * Interface for the shell. + */ +public class WindowManagerShell { } diff --git a/packages/WindowManager/Shell/tests/Android.bp b/packages/WindowManager/Shell/tests/Android.bp new file mode 100644 index 000000000000..78fa45ebdf94 --- /dev/null +++ b/packages/WindowManager/Shell/tests/Android.bp @@ -0,0 +1,45 @@ +// 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: "WindowManagerShellTests", + + srcs: ["**/*.java"], + + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-extended-minus-junit4", + "truth-prebuilt", + ], + libs: [ + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + sdk_version: "current", + platform_apis: true, + + optimize: { + enabled: false, + }, +} diff --git a/packages/WindowManager/Shell/tests/AndroidManifest.xml b/packages/WindowManager/Shell/tests/AndroidManifest.xml new file mode 100644 index 000000000000..a8f795ec8a8d --- /dev/null +++ b/packages/WindowManager/Shell/tests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?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" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.wm.shell.tests"> + + <application android:debuggable="true" android:largeHeap="true"> + <uses-library android:name="android.test.mock" /> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Tests for WindowManager-Shell" + android:targetPackage="com.android.wm.shell.tests"> + </instrumentation> +</manifest> diff --git a/packages/WindowManager/Shell/tests/AndroidTest.xml b/packages/WindowManager/Shell/tests/AndroidTest.xml new file mode 100644 index 000000000000..4dce4db360e4 --- /dev/null +++ b/packages/WindowManager/Shell/tests/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 Tests for WindowManagerShellLib"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="WindowManagerShellTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="WindowManagerShellTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.wm.shell.tests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/packages/WindowManager/Shell/tests/res/values/config.xml b/packages/WindowManager/Shell/tests/res/values/config.xml new file mode 100644 index 000000000000..c894eb0133b5 --- /dev/null +++ b/packages/WindowManager/Shell/tests/res/values/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** 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. +*/ +--> + +<resources> +</resources>
\ No newline at end of file diff --git a/packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java new file mode 100644 index 000000000000..376875b143a1 --- /dev/null +++ b/packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java @@ -0,0 +1,40 @@ +/* + * 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.wm.shell.tests; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.WindowManagerShell; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for the shell. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WindowManagerShellTest { + + WindowManagerShell mShell; + + @Test + public void testNothing() { + // Do nothing + } +} diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index 3eb90491f902..eecc10158e8a 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -42,7 +42,7 @@ LOCAL_REQUIRED_MODULES := \ IconPackRoundedLauncherOverlay \ IconPackRoundedSettingsOverlay \ IconPackRoundedSystemUIOverlay \ - IconPackRoundedThemePickerUIOverlay \ + IconPackRoundedThemePickerOverlay \ IconShapeRoundedRectOverlay \ IconShapeSquircleOverlay \ IconShapeTeardropOverlay \ diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index 9b7adc883dee..50d21ba59996 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -77,6 +77,8 @@ class GestureManifold implements GestureMatcher.StateChangeListener { // Start with double tap. mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this)); mGestures.add(new MultiTapAndHold(context, 2, GESTURE_DOUBLE_TAP_AND_HOLD, this)); + // Second-finger double tap. + mGestures.add(new SecondFingerMultiTap(context, 2, GESTURE_DOUBLE_TAP, this)); // One-direction swipes. mGestures.add(new Swipe(context, RIGHT, GESTURE_SWIPE_RIGHT, this)); mGestures.add(new Swipe(context, LEFT, GESTURE_SWIPE_LEFT, this)); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java new file mode 100644 index 000000000000..eb38b53dc52a --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java @@ -0,0 +1,167 @@ +/* + * 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.accessibility.gestures; + +import static android.view.MotionEvent.INVALID_POINTER_ID; + +import android.content.Context; +import android.os.Handler; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +/** + * This class matches second-finger multi-tap gestures. A second-finger multi-tap gesture is where + * one finger is held down and a second finger executes the taps. The number of taps for each + * instance is specified in the constructor. + */ +class SecondFingerMultiTap extends GestureMatcher { + final int mTargetTaps; + int mDoubleTapSlop; + int mTouchSlop; + int mTapTimeout; + int mDoubleTapTimeout; + int mCurrentTaps; + int mSecondFingerPointerId; + float mBaseX; + float mBaseY; + + SecondFingerMultiTap( + Context context, int taps, int gesture, GestureMatcher.StateChangeListener listener) { + super(gesture, new Handler(context.getMainLooper()), listener); + mTargetTaps = taps; + mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mTapTimeout = ViewConfiguration.getTapTimeout(); + mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); + clear(); + } + + @Override + protected void clear() { + mCurrentTaps = 0; + mBaseX = Float.NaN; + mBaseY = Float.NaN; + mSecondFingerPointerId = INVALID_POINTER_ID; + super.clear(); + } + + @Override + protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (event.getPointerCount() > 2) { + cancelGesture(event, rawEvent, policyFlags); + return; + } + // Second finger has gone down. + int index = getActionIndex(event); + mSecondFingerPointerId = event.getPointerId(index); + cancelAfterTapTimeout(event, rawEvent, policyFlags); + if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) { + mBaseX = event.getX(); + mBaseY = event.getY(); + } + if (!isSecondFingerInsideSlop(rawEvent, mDoubleTapSlop)) { + cancelGesture(event, rawEvent, policyFlags); + } + mBaseX = event.getX(); + mBaseY = event.getY(); + } + + @Override + protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (event.getPointerCount() > 2) { + cancelGesture(event, rawEvent, policyFlags); + return; + } + cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags); + if (!isSecondFingerInsideSlop(rawEvent, mTouchSlop)) { + cancelGesture(event, rawEvent, policyFlags); + } + if (getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) { + mCurrentTaps++; + if (mCurrentTaps == mTargetTaps) { + // Done. + completeGesture(event, rawEvent, policyFlags); + return; + } + // Needs more taps. + cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags); + } else { + // Nonsensical event stream. + cancelGesture(event, rawEvent, policyFlags); + } + } + + @Override + protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + switch (event.getPointerCount()) { + case 1: + // We don't need to track anything about one-finger movements. + break; + case 2: + if (!isSecondFingerInsideSlop(rawEvent, mTouchSlop)) { + cancelGesture(event, rawEvent, policyFlags); + } + break; + default: + // More than two fingers means we stop tracking. + cancelGesture(event, rawEvent, policyFlags); + break; + } + } + + @Override + public String getGestureName() { + switch (mTargetTaps) { + case 2: + return "Second Finger Double Tap"; + case 3: + return "Second Finger Triple Tap"; + default: + return "Second Finger " + Integer.toString(mTargetTaps) + " Taps"; + } + } + + private boolean isSecondFingerInsideSlop(MotionEvent rawEvent, int slop) { + int pointerIndex = rawEvent.findPointerIndex(mSecondFingerPointerId); + if (pointerIndex == -1) { + return false; + } + final float deltaX = mBaseX - rawEvent.getX(pointerIndex); + final float deltaY = mBaseY - rawEvent.getY(pointerIndex); + if (deltaX == 0 && deltaY == 0) { + return true; + } + final double moveDelta = Math.hypot(deltaX, deltaY); + return moveDelta <= slop; + } + + private int getActionIndex(MotionEvent event) { + return event.getAction() + & MotionEvent.ACTION_POINTER_INDEX_MASK << MotionEvent.ACTION_POINTER_INDEX_SHIFT; + } + + @Override + public String toString() { + return super.toString() + + ", Taps:" + + mCurrentTaps + + ", mBaseX: " + + Float.toString(mBaseX) + + ", mBaseY: " + + Float.toString(mBaseY); + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 5f4163880366..f6eb31b93f06 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -286,11 +286,6 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public void onDoubleTapAndHold() { - // Ignore the event if we aren't touch interacting. - if (!mState.isTouchInteracting()) { - return; - } - // Pointers should not be zero when running this command. if (mState.getLastReceivedEvent().getPointerCount() == 0) { return; @@ -304,10 +299,6 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onDoubleTap() { - if (!mState.isTouchInteracting()) { - return false; - } - mAms.onTouchInteractionEnd(); // Remove pending event deliveries. mSendHoverEnterAndMoveDelayed.cancel(); @@ -454,7 +445,7 @@ public class TouchExplorer extends BaseEventStreamTransformation handleActionDown(event, rawEvent, policyFlags); break; case MotionEvent.ACTION_POINTER_DOWN: - handleActionPointerDown(); + handleActionPointerDown(event, rawEvent, policyFlags); break; case MotionEvent.ACTION_MOVE: handleActionMoveStateTouchInteracting(event, rawEvent, policyFlags); @@ -479,7 +470,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // We should have already received ACTION_DOWN. Ignore. break; case MotionEvent.ACTION_POINTER_DOWN: - handleActionPointerDown(); + handleActionPointerDown(event, rawEvent, policyFlags); break; case MotionEvent.ACTION_MOVE: handleActionMoveStateTouchExploring(event, rawEvent, policyFlags); @@ -496,12 +487,19 @@ public class TouchExplorer extends BaseEventStreamTransformation * Handles ACTION_POINTER_DOWN when in the touch exploring state. This event represents an * additional finger touching the screen. */ - private void handleActionPointerDown() { + private void handleActionPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { // Another finger down means that if we have not started to deliver // hover events, we will not have to. The code for ACTION_MOVE will // decide what we will actually do next. - mSendHoverEnterAndMoveDelayed.cancel(); - mSendHoverExitDelayed.cancel(); + + if (mSendHoverEnterAndMoveDelayed.isPending()) { + mSendHoverEnterAndMoveDelayed.cancel(); + mSendHoverExitDelayed.cancel(); + } else { + // We have already delivered at least one hover event, so send hover exit to keep the + // stream consistent. + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); + } } /** * Handles ACTION_MOVE while in the touch interacting state. This is where transitions to diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 81ce359cc078..26245b15f92b 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -635,7 +635,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mPackageManagerInternal.getSuspendedDialogInfo(providerPackage, suspendingPackage, providerUserId); onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent( - providerPackage, suspendingPackage, dialogInfo, providerUserId); + providerPackage, suspendingPackage, dialogInfo, null, providerUserId); } } else if (provider.maskedByQuietProfile) { showBadge = true; diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 8f1e1568ead6..e976811a3094 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -18,6 +18,7 @@ package com.android.server.companion; import static com.android.internal.util.CollectionUtils.size; +import static com.android.internal.util.FunctionalUtils.uncheckExceptions; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkState; @@ -26,16 +27,15 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainRunna import android.annotation.CheckResult; import android.annotation.Nullable; import android.app.PendingIntent; +import android.companion.Association; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; import android.companion.ICompanionDeviceDiscoveryService; -import android.companion.ICompanionDeviceDiscoveryServiceCallback; import android.companion.ICompanionDeviceManager; import android.companion.IFindDeviceCallback; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; @@ -67,6 +67,9 @@ import android.util.Xml; import com.android.internal.app.IAppOpsService; import com.android.internal.content.PackageMonitor; +import com.android.internal.infra.AndroidFuture; +import com.android.internal.infra.PerUser; +import com.android.internal.infra.ServiceConnector; import com.android.internal.notification.NotificationAccessConfirmationActivityContract; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -118,12 +121,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private final CompanionDeviceManagerImpl mImpl; private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>(); private IDeviceIdleController mIdleController; - private ServiceConnection mServiceConnection; + private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors; private IAppOpsService mAppOpsManager; private IFindDeviceCallback mFindDeviceCallback; private AssociationRequest mRequest; private String mCallingPackage; + private AndroidFuture<Association> mOngoingDeviceDiscovery; private final Object mLock = new Object(); @@ -134,6 +138,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); mAppOpsManager = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); + + Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO); + mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() { + @Override + protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) { + return new ServiceConnector.Impl<>( + getContext(), + serviceIntent, 0/* bindingFlags */, userId, + ICompanionDeviceDiscoveryService.Stub::asInterface); + } + }; + + registerPackageMonitor(); } @@ -187,7 +204,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private void cleanup() { synchronized (mLock) { - mServiceConnection = unbind(mServiceConnection); + AndroidFuture<Association> ongoingDeviceDiscovery = mOngoingDeviceDiscovery; + if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) { + ongoingDeviceDiscovery.cancel(true); + } mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0); mRequest = null; mCallingPackage = null; @@ -207,15 +227,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return null; } - @Nullable - @CheckResult - private ServiceConnection unbind(@Nullable ServiceConnection conn) { - if (conn != null) { - getContext().unbindService(conn); - } - return null; - } - class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub { @Override @@ -243,13 +254,27 @@ public class CompanionDeviceManagerService extends SystemService implements Bind checkCallerIsSystemOr(callingPackage); int userId = getCallingUserId(); checkUsesFeature(callingPackage, userId); + + mFindDeviceCallback = callback; + mRequest = request; + mCallingPackage = callingPackage; + callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0); + final long callingIdentity = Binder.clearCallingIdentity(); try { - getContext().bindServiceAsUser( - new Intent().setComponent(SERVICE_TO_BIND_TO), - createServiceConnection(request, callback, callingPackage), - Context.BIND_AUTO_CREATE, - UserHandle.of(userId)); + mOngoingDeviceDiscovery = mServiceConnectors.forUser(userId).postAsync(service -> { + AndroidFuture<Association> future = new AndroidFuture<>(); + service.startDiscovery(request, callingPackage, callback, future); + return future; + }).whenComplete(uncheckExceptions((association, err) -> { + if (err == null) { + addAssociation(association); + } else { + Log.e(LOG_TAG, "Failed to discover device(s)", err); + callback.onFailure("No devices found: " + err.getMessage()); + } + cleanup(); + })); } finally { Binder.restoreCallingIdentity(callingIdentity); } @@ -386,82 +411,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return Binder.getCallingUid() == Process.SYSTEM_UID; } - private ServiceConnection createServiceConnection( - final AssociationRequest request, - final IFindDeviceCallback findDeviceCallback, - final String callingPackage) { - mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) { - Slog.i(LOG_TAG, - "onServiceConnected(name = " + name + ", service = " - + service + ")"); - } - - mFindDeviceCallback = findDeviceCallback; - mRequest = request; - mCallingPackage = callingPackage; - - try { - mFindDeviceCallback.asBinder().linkToDeath( - CompanionDeviceManagerService.this, 0); - } catch (RemoteException e) { - cleanup(); - return; - } - - try { - ICompanionDeviceDiscoveryService.Stub - .asInterface(service) - .startDiscovery( - request, - callingPackage, - findDeviceCallback, - getServiceCallback()); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Error while initiating device discovery", e); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Slog.i(LOG_TAG, "onServiceDisconnected(name = " + name + ")"); - } - }; - return mServiceConnection; - } - - private ICompanionDeviceDiscoveryServiceCallback.Stub getServiceCallback() { - return new ICompanionDeviceDiscoveryServiceCallback.Stub() { - - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - try { - return super.onTransact(code, data, reply, flags); - } catch (Throwable e) { - Slog.e(LOG_TAG, "Error during IPC", e); - throw ExceptionUtils.propagate(e, RemoteException.class); - } - } - - @Override - public void onDeviceSelected(String packageName, int userId, String deviceAddress) { - addAssociation(userId, packageName, deviceAddress); - cleanup(); - } - - @Override - public void onDeviceSelectionCancel() { - cleanup(); - } - }; + void addAssociation(int userId, String packageName, String deviceAddress) { + addAssociation(new Association(userId, deviceAddress, packageName)); } - void addAssociation(int userId, String packageName, String deviceAddress) { - updateSpecialAccessPermissionForAssociatedPackage(packageName, userId); - recordAssociation(packageName, deviceAddress); + void addAssociation(Association association) { + updateSpecialAccessPermissionForAssociatedPackage( + association.companionAppPackage, association.userId); + recordAssociation(association); } void removeAssociation(int userId, String pkg, String deviceMacAddress) { @@ -525,14 +482,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind }, getContext(), packageName, userId).recycleOnUse()); } - private void recordAssociation(String priviledgedPackage, String deviceAddress) { + private void recordAssociation(Association association) { if (DEBUG) { - Log.i(LOG_TAG, "recordAssociation(priviledgedPackage = " + priviledgedPackage - + ", deviceAddress = " + deviceAddress + ")"); + Log.i(LOG_TAG, "recordAssociation(" + association + ")"); } - int userId = getCallingUserId(); - updateAssociations(associations -> CollectionUtils.add(associations, - new Association(userId, deviceAddress, priviledgedPackage))); + updateAssociations(associations -> CollectionUtils.add(associations, association)); + } + + private void recordAssociation(String privilegedPackage, String deviceAddress) { + recordAssociation(new Association(getCallingUserId(), deviceAddress, privilegedPackage)); } private void updateAssociations(Function<Set<Association>, Set<Association>> update) { @@ -629,41 +587,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } - - - private class Association { - public final int uid; - public final String deviceAddress; - public final String companionAppPackage; - - private Association(int uid, String deviceAddress, String companionAppPackage) { - this.uid = uid; - this.deviceAddress = checkNotNull(deviceAddress); - this.companionAppPackage = checkNotNull(companionAppPackage); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Association that = (Association) o; - - if (uid != that.uid) return false; - if (!deviceAddress.equals(that.deviceAddress)) return false; - return companionAppPackage.equals(that.companionAppPackage); - - } - - @Override - public int hashCode() { - int result = uid; - result = 31 * result + deviceAddress.hashCode(); - result = 31 * result + companionAppPackage.hashCode(); - return result; - } - } - private class ShellCmd extends ShellCommand { public static final String USAGE = "help\n" + "list USER_ID\n" diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index f363a73b05af..40a7dfcfc18d 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -67,6 +67,27 @@ public abstract class PackageManagerInternal { public static final int PACKAGE_COMPANION = 14; @IntDef(value = { + INTEGRITY_VERIFICATION_ALLOW, + INTEGRITY_VERIFICATION_REJECT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IntegrityVerificationResult {} + + /** + * Used as the {@code verificationCode} argument for + * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the + * integrity component allows the install to proceed. + */ + public static final int INTEGRITY_VERIFICATION_ALLOW = 1; + + /** + * Used as the {@code verificationCode} argument for + * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the + * integrity component does not allow install to proceed. + */ + public static final int INTEGRITY_VERIFICATION_REJECT = 0; + + @IntDef(value = { PACKAGE_SYSTEM, PACKAGE_SETUP_WIZARD, PACKAGE_INSTALLER, @@ -842,13 +863,13 @@ public abstract class PackageManagerInternal { * {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification * broadcast} to respond to the package manager. The response must include * the {@code verificationCode} which is one of - * {@link PackageManager#VERIFICATION_ALLOW} or - * {@link PackageManager#VERIFICATION_REJECT}. + * {@link #INTEGRITY_VERIFICATION_ALLOW} and {@link #INTEGRITY_VERIFICATION_REJECT}. * * @param verificationId pending package identifier as passed via the * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra. - * @param verificationResult either {@link PackageManager#VERIFICATION_ALLOW} - * or {@link PackageManager#VERIFICATION_REJECT}. + * @param verificationResult either {@link #INTEGRITY_VERIFICATION_ALLOW} + * or {@link #INTEGRITY_VERIFICATION_REJECT}. */ - public abstract void setIntegrityVerificationResult(int verificationId, int verificationResult); + public abstract void setIntegrityVerificationResult(int verificationId, + @IntegrityVerificationResult int verificationResult); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 13eb55665741..46ff71831905 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -46,6 +46,7 @@ import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SrvccState; import android.telephony.CallAttributes; import android.telephony.CallQuality; +import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.CellLocation; import android.telephony.DataFailCause; @@ -207,7 +208,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // Connection state of default APN type data (i.e. internet) of phones private int[] mDataConnectionState; - private Bundle[] mCellLocation; + private CellIdentity[] mCellIdentity; private int[] mDataConnectionNetworkType; @@ -295,7 +296,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int numPhones = getTelephonyManager().getPhoneCount(); for (int sub = 0; sub < numPhones; sub++) { TelephonyRegistry.this.notifyCellLocationForSubscriber(sub, - mCellLocation[sub]); + mCellIdentity[sub]); } break; } @@ -404,7 +405,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSignalStrength = copyOf(mSignalStrength, mNumPhones); mMessageWaiting = copyOf(mMessageWaiting, mNumPhones); mCallForwarding = copyOf(mCallForwarding, mNumPhones); - mCellLocation = copyOf(mCellLocation, mNumPhones); + mCellIdentity = copyOf(mCellIdentity, mNumPhones); mSrvccState = copyOf(mSrvccState, mNumPhones); mPreciseCallState = copyOf(mPreciseCallState, mNumPhones); mForegroundCallState = copyOf(mForegroundCallState, mNumPhones); @@ -439,31 +440,22 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mUserMobileDataState[i] = false; mMessageWaiting[i] = false; mCallForwarding[i] = false; - mCellLocation[i] = new Bundle(); + mCellIdentity[i] = null; mCellInfo.add(i, null); mImsReasonInfo.add(i, null); mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE; mCallDisconnectCause[i] = DisconnectCause.NOT_VALID; mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID; mCallQuality[i] = createCallQuality(); - mCallAttributes[i] = new CallAttributes(new PreciseCallState(), + mCallAttributes[i] = new CallAttributes(createPreciseCallState(), TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality()); mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN; - mPreciseCallState[i] = new PreciseCallState(); + mPreciseCallState[i] = createPreciseCallState(); mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>()); } - - // Note that location can be null for non-phone builds like - // like the generic one. - CellLocation location = CellLocation.getEmpty(); - if (location != null) { - for (int i = oldNumPhones; i < mNumPhones; i++) { - location.fillInNotifierBundle(mCellLocation[i]); - } - } } private void cutListToSize(List list, int size) { @@ -503,7 +495,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSignalStrength = new SignalStrength[numPhones]; mMessageWaiting = new boolean[numPhones]; mCallForwarding = new boolean[numPhones]; - mCellLocation = new Bundle[numPhones]; + mCellIdentity = new CellIdentity[numPhones]; mSrvccState = new int[numPhones]; mPreciseCallState = new PreciseCallState[numPhones]; mForegroundCallState = new int[numPhones]; @@ -532,31 +524,23 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mUserMobileDataState[i] = false; mMessageWaiting[i] = false; mCallForwarding[i] = false; - mCellLocation[i] = new Bundle(); + mCellIdentity[i] = null; mCellInfo.add(i, null); mImsReasonInfo.add(i, null); mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE; mCallDisconnectCause[i] = DisconnectCause.NOT_VALID; mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID; mCallQuality[i] = createCallQuality(); - mCallAttributes[i] = new CallAttributes(new PreciseCallState(), + mCallAttributes[i] = new CallAttributes(createPreciseCallState(), TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality()); mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN; - mPreciseCallState[i] = new PreciseCallState(); + mPreciseCallState[i] = createPreciseCallState(); mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>()); } - // Note that location can be null for non-phone builds like - // like the generic one. - if (location != null) { - for (int i = 0; i < numPhones; i++) { - location.fillInNotifierBundle(mCellLocation[i]); - } - } - mAppOps = mContext.getSystemService(AppOpsManager.class); } @@ -837,11 +821,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { - if (DBG_LOC) log("listen: mCellLocation = " - + mCellLocation[phoneId]); + if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onCellLocationChanged( - new Bundle(mCellLocation[phoneId])); + // null will be translated to empty CellLocation object in client. + r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } } catch (RemoteException ex) { remove(r.binder); @@ -1629,11 +1612,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { broadcastDataConnectionFailed(apnType, subId); } - public void notifyCellLocation(Bundle cellLocation) { - notifyCellLocationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellLocation); + @Override + public void notifyCellLocation(CellIdentity cellLocation) { + notifyCellLocationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellLocation); } - public void notifyCellLocationForSubscriber(int subId, Bundle cellLocation) { + @Override + public void notifyCellLocationForSubscriber(int subId, CellIdentity cellLocation) { log("notifyCellLocationForSubscriber: subId=" + subId + " cellLocation=" + cellLocation); if (!checkNotifyPermission("notifyCellLocation()")) { @@ -1646,7 +1631,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { if (validatePhoneId(phoneId)) { - mCellLocation[phoneId] = cellLocation; + mCellIdentity[phoneId] = cellLocation; for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && @@ -1656,7 +1641,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyCellLocation: cellLocation=" + cellLocation + " r=" + r); } - r.callback.onCellLocationChanged(new Bundle(cellLocation)); + r.callback.onCellLocationChanged(cellLocation); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2093,7 +2078,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mCallForwarding=" + mCallForwarding[i]); pw.println("mDataActivity=" + mDataActivity[i]); pw.println("mDataConnectionState=" + mDataConnectionState[i]); - pw.println("mCellLocation=" + mCellLocation[i]); + pw.println("mCellIdentity=" + mCellIdentity[i]); pw.println("mCellInfo=" + mCellInfo.get(i)); pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i)); pw.println("mSrvccState=" + mSrvccState[i]); @@ -2583,10 +2568,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { - if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = " - + mCellLocation[phoneId]); + if (DBG_LOC) { + log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " + + mCellIdentity[phoneId]); + } if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId])); + // null will be translated to empty CellLocation object in client. + r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } } catch (RemoteException ex) { mRemoveList.add(r.binder); @@ -2678,6 +2666,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** Returns a new PreciseCallState object with default values. */ + private static PreciseCallState createPreciseCallState() { + return new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_NOT_VALID, + PreciseCallState.PRECISE_CALL_STATE_NOT_VALID, + PreciseCallState.PRECISE_CALL_STATE_NOT_VALID, + DisconnectCause.NOT_VALID, + PreciseDisconnectCause.NOT_VALID); + } + /** Returns a new CallQuality object with default values. */ private static CallQuality createCallQuality() { return new CallQuality(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6dc49b7ba375..d7a46fec7767 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7558,6 +7558,26 @@ public class ActivityManagerService extends IActivityManager.Stub }); } + @Override + public void appNotResponding(final String reason) { + final int callingPid = Binder.getCallingPid(); + + synchronized (mPidsSelfLocked) { + final ProcessRecord app = mPidsSelfLocked.get(callingPid); + if (app == null) { + throw new SecurityException("Unknown process: " + callingPid); + } + + mHandler.post(new Runnable() { + @Override + public void run() { + app.appNotResponding( + null, app.info, null, null, false, "App requested: " + reason); + } + }); + } + } + public final void installSystemProviders() { List<ProviderInfo> providers; synchronized (this) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 0b74840d850b..9f23cdaf3b86 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -2335,7 +2335,7 @@ public class AppOpsService extends IAppOpsService.Stub { } else { final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; final int mode = switchOp.evalMode(); - if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { + if (mode != AppOpsManager.MODE_ALLOWED) { if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 21c478417d87..00fc6d06245c 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -355,8 +355,15 @@ public class AudioDeviceInventory { mConnectedDevices.replace(key, di); } } - if (AudioSystem.handleDeviceConfigChange(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, - BtHelper.getName(btDevice), a2dpCodec) != AudioSystem.AUDIO_STATUS_OK) { + final int res = AudioSystem.handleDeviceConfigChange( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, + BtHelper.getName(btDevice), a2dpCodec); + + if (res != AudioSystem.AUDIO_STATUS_OK) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "APM handleDeviceConfigChange failed for A2DP device addr=" + + address + " codec=" + a2dpCodec).printLog(TAG)); + int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC); // force A2DP device disconnection in case of error so that AudioService state is // consistent with audio policy manager state @@ -364,6 +371,10 @@ public class AudioDeviceInventory { btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP, false /* suppressNoisyIntent */, musicDevice, -1 /* a2dpVolume */); + } else { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "APM handleDeviceConfigChange success for A2DP device addr=" + + address + " codec=" + a2dpCodec).printLog(TAG)); } } } @@ -702,8 +713,20 @@ public class AudioDeviceInventory { 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 - AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); + + if (res != AudioSystem.AUDIO_STATUS_OK) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "APM failed to make available A2DP device addr=" + address + + " error=" + res).printLog(TAG)); + // TODO: connection failed, stop here + // TODO: return; + } else { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "A2DP device addr=" + address + " now available").printLog(TAG)); + } + // Reset A2DP suspend state each time a new sink is connected AudioSystem.setParameters("A2dpSuspended=false"); @@ -738,8 +761,19 @@ public class AudioDeviceInventory { // device to remove was visible by APM, update APM mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false); - AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); + + if (res != AudioSystem.AUDIO_STATUS_OK) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "APM failed to make unavailable A2DP device addr=" + address + + " error=" + res).printLog(TAG)); + // TODO: failed to disconnect, stop here + // TODO: return; + } else { + AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( + "A2DP device addr=" + address + " made unavailable")).printLog(TAG)); + } mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); // Remove A2DP routes as well setCurrentAudioRouteNameIfPossible(null); diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 7ce63c5f89b2..d57ce5390a96 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -138,18 +138,13 @@ abstract class DisplayDevice { } /** - * Sets the refresh ranges, and display modes that the system is allowed to switch between. - * Display modes are roughly ordered by preference. + * Sets the display mode specs. * * Not all display devices will automatically switch between modes, so it's important that the - * most-desired modes are at the beginning of the allowed array. - * - * @param defaultModeId is used, if the device does not support multiple refresh - * rates, and to navigate other parameters. + * default modeId is set correctly. */ - public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, - float maxRefreshRate, int[] modes) { - } + public void setDesiredDisplayModeSpecsLocked( + DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) {} /** * Sets the requested color mode. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index d20191dd1f85..ea031318be89 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -431,7 +431,8 @@ public final class DisplayManagerService extends SystemService { recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY)); } - mDisplayModeDirector.setDisplayModeListener(new AllowedDisplayModeObserver()); + mDisplayModeDirector.setDesiredDisplayModeSpecsListener( + new DesiredDisplayModeSpecsObserver()); mDisplayModeDirector.start(mSensorManager); mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); @@ -1327,19 +1328,24 @@ public final class DisplayManagerService extends SystemService { return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp); } - private void onAllowedDisplayModesChangedInternal() { + private void onDesiredDisplayModeSpecsChangedInternal() { boolean changed = false; synchronized (mSyncRoot) { final int count = mLogicalDisplays.size(); for (int i = 0; i < count; i++) { LogicalDisplay display = mLogicalDisplays.valueAt(i); int displayId = mLogicalDisplays.keyAt(i); - int[] allowedModes = mDisplayModeDirector.getAllowedModes(displayId); - // Note that order is important here since not all display devices are capable of - // automatically switching, so we do actually want to check for equality and not - // just equivalent contents (regardless of order). - if (!Arrays.equals(allowedModes, display.getAllowedDisplayModesLocked())) { - display.setAllowedDisplayModesLocked(allowedModes); + DisplayModeDirector.DesiredDisplayModeSpecs desiredDisplayModeSpecs = + mDisplayModeDirector.getDesiredDisplayModeSpecs(displayId); + DisplayModeDirector.DesiredDisplayModeSpecs existingDesiredDisplayModeSpecs = + display.getDesiredDisplayModeSpecsLocked(); + if (DEBUG) { + Slog.i(TAG, + "Comparing display specs: " + desiredDisplayModeSpecs + + ", existing: " + existingDesiredDisplayModeSpecs); + } + if (!desiredDisplayModeSpecs.equals(existingDesiredDisplayModeSpecs)) { + display.setDesiredDisplayModeSpecsLocked(desiredDisplayModeSpecs); changed = true; } } @@ -2488,9 +2494,10 @@ public final class DisplayManagerService extends SystemService { } - class AllowedDisplayModeObserver implements DisplayModeDirector.DisplayModeListener { - public void onAllowedDisplayModesChanged() { - onAllowedDisplayModesChangedInternal(); + class DesiredDisplayModeSpecsObserver + implements DisplayModeDirector.DesiredDisplayModeSpecsListener { + public void onDesiredDisplayModeSpecsChanged() { + onDesiredDisplayModeSpecsChangedInternal(); } } } diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 2df682fa708c..ad728c18dd59 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -63,7 +63,7 @@ public class DisplayModeDirector { private static final String TAG = "DisplayModeDirector"; private static final boolean DEBUG = false; - private static final int MSG_ALLOWED_MODES_CHANGED = 1; + private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1; private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2; private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3; private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4; @@ -95,7 +95,7 @@ public class DisplayModeDirector { private final BrightnessObserver mBrightnessObserver; private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; - private DisplayModeListener mDisplayModeListener; + private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { mContext = context; @@ -125,23 +125,11 @@ public class DisplayModeDirector { synchronized (mLock) { // We may have a listener already registered before the call to start, so go ahead and // notify them to pick up our newly initialized state. - notifyAllowedModesChangedLocked(); + notifyDesiredDisplayModeSpecsChangedLocked(); } } - /** - * Calculates the modes the system is allowed to freely switch between based on global and - * display-specific constraints. - * - * @param displayId The display to query for. - * @return The IDs of the modes the system is allowed to freely switch between. - */ - @NonNull - public int[] getAllowedModes(int displayId) { - return getDesiredDisplayConfigSpecs(displayId).allowedConfigs; - } - @NonNull private SparseArray<Vote> getVotesLocked(int displayId) { SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId); @@ -173,16 +161,16 @@ public class DisplayModeDirector { * system is allowed to switch between. */ @NonNull - public DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(int displayId) { + public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) { synchronized (mLock) { SparseArray<Vote> votes = getVotesLocked(displayId); Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId); if (modes == null || defaultMode == null) { - Slog.e(TAG, "Asked about unknown display, returning empty desired configs!" - + "(id=" + displayId + ")"); - return new DesiredDisplayConfigSpecs(displayId, new RefreshRateRange(60, 60), - new int[0]); + Slog.e(TAG, + "Asked about unknown display, returning empty display mode specs!" + + "(id=" + displayId + ")"); + return new DesiredDisplayModeSpecs(); } int[] availableModes = new int[]{defaultMode.getModeId()}; @@ -255,9 +243,9 @@ public class DisplayModeDirector { } // filterModes function is going to filter the modes based on the voting system. If // the application requests a given mode with preferredModeId function, it will be - // stored as the first and only element in available modes array. - return new DesiredDisplayConfigSpecs(defaultModeId, - new RefreshRateRange(minRefreshRate, maxRefreshRate), availableModes); + // stored as defaultModeId. + return new DesiredDisplayModeSpecs( + defaultModeId, new RefreshRateRange(minRefreshRate, maxRefreshRate)); } } @@ -311,11 +299,13 @@ public class DisplayModeDirector { } /** - * Sets the modeListener for changes to allowed display modes. + * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate + * ranges. */ - public void setDisplayModeListener(@Nullable DisplayModeListener displayModeListener) { + public void setDesiredDisplayModeSpecsListener( + @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) { synchronized (mLock) { - mDisplayModeListener = displayModeListener; + mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener; } } @@ -389,16 +379,18 @@ public class DisplayModeDirector { mVotesByDisplay.remove(displayId); } - notifyAllowedModesChangedLocked(); + notifyDesiredDisplayModeSpecsChangedLocked(); } - private void notifyAllowedModesChangedLocked() { - if (mDisplayModeListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) { + private void notifyDesiredDisplayModeSpecsChangedLocked() { + if (mDesiredDisplayModeSpecsListener != null + && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) { // We need to post this to a handler to avoid calling out while holding the lock // since we know there are things that both listen for changes as well as provide - // information. If we did call out while holding the lock, then there's no guaranteed - // lock order and we run the real of risk deadlock. - Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mDisplayModeListener); + // information. If we did call out while holding the lock, then there's no + // guaranteed lock order and we run the real of risk deadlock. + Message msg = mHandler.obtainMessage( + MSG_REFRESH_RATE_RANGE_CHANGED, mDesiredDisplayModeSpecsListener); msg.sendToTarget(); } } @@ -430,13 +422,13 @@ public class DisplayModeDirector { } /** - * Listens for changes to display mode coordination. + * Listens for changes refresh rate coordination. */ - public interface DisplayModeListener { + public interface DesiredDisplayModeSpecsListener { /** - * Called when the allowed display modes may have changed. + * Called when the refresh rate range may have changed. */ - void onAllowedDisplayModesChanged(); + void onDesiredDisplayModeSpecsChanged(); } private final class DisplayModeDirectorHandler extends Handler { @@ -447,11 +439,6 @@ public class DisplayModeDirector { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_ALLOWED_MODES_CHANGED: - DisplayModeListener displayModeListener = (DisplayModeListener) msg.obj; - displayModeListener.onAllowedDisplayModesChanged(); - break; - case MSG_BRIGHTNESS_THRESHOLDS_CHANGED: Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; @@ -474,6 +461,12 @@ public class DisplayModeDirector { mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged( refreshRateInZone); break; + + case MSG_REFRESH_RATE_RANGE_CHANGED: + DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener = + (DesiredDisplayModeSpecsListener) msg.obj; + desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged(); + break; } } } @@ -485,11 +478,13 @@ public class DisplayModeDirector { /** * The lowest desired refresh rate. */ - public final float min; + public float min; /** * The highest desired refresh rate. */ - public final float max; + public float max; + + public RefreshRateRange() {} public RefreshRateRange(float min, float max) { if (min < 0 || max < 0 || min > max) { @@ -531,32 +526,32 @@ public class DisplayModeDirector { } /** - * Information about the desired configuration to be set by the system. Includes the default - * configuration ID, refresh rate range, and the list of policy decisions that influenced the - * choice. + * Information about the desired display mode to be set by the system. Includes the default + * mode ID and refresh rate range. + * + * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the + * distinction between the config ID / physical index that + * SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here. */ - public static final class DesiredDisplayConfigSpecs { + public static final class DesiredDisplayModeSpecs { /** - * Default configuration ID. This is what system defaults to for all other settings, or + * Default mode ID. This is what system defaults to for all other settings, or * if the refresh rate range is not available. */ - public final int defaultModeId; + public int defaultModeId; /** * The refresh rate range. */ public final RefreshRateRange refreshRateRange; - /** - * For legacy reasons, keep a list of allowed configs. - * TODO(b/142507213): Re-assess whether the list of allowed configs is still necessary. - */ - public final int[] allowedConfigs; - public DesiredDisplayConfigSpecs(int defaultModeId, - @NonNull RefreshRateRange refreshRateRange, - @NonNull int[] allowedConfigs) { + public DesiredDisplayModeSpecs() { + refreshRateRange = new RefreshRateRange(); + } + + public DesiredDisplayModeSpecs( + int defaultModeId, @NonNull RefreshRateRange refreshRateRange) { this.defaultModeId = defaultModeId; this.refreshRateRange = refreshRateRange; - this.allowedConfigs = allowedConfigs; } /** @@ -564,9 +559,8 @@ public class DisplayModeDirector { */ @Override public String toString() { - return "DesiredDisplayConfigSpecs(defaultModeId=" + defaultModeId - + ", refreshRateRange=" + refreshRateRange.toString() - + ", allowedConfigs=" + Arrays.toString(allowedConfigs) + ")"; + return String.format("defaultModeId=%d min=%.0f max=%.0f", defaultModeId, + refreshRateRange.min, refreshRateRange.max); } /** * Checks whether the two objects have the same values. @@ -577,17 +571,16 @@ public class DisplayModeDirector { return true; } - if (!(other instanceof DesiredDisplayConfigSpecs)) { + if (!(other instanceof DesiredDisplayModeSpecs)) { return false; } - DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = - (DesiredDisplayConfigSpecs) other; + DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other; - if (defaultModeId != desiredDisplayConfigSpecs.defaultModeId) { + if (defaultModeId != desiredDisplayModeSpecs.defaultModeId) { return false; } - if (!refreshRateRange.equals(desiredDisplayConfigSpecs.refreshRateRange)) { + if (!refreshRateRange.equals(desiredDisplayModeSpecs.refreshRateRange)) { return false; } return true; @@ -597,6 +590,15 @@ public class DisplayModeDirector { public int hashCode() { return Objects.hash(defaultModeId, refreshRateRange); } + + /** + * Copy values from the other object. + */ + public void copyFrom(DesiredDisplayModeSpecs other) { + defaultModeId = other.defaultModeId; + refreshRateRange.min = other.refreshRateRange.min; + refreshRateRange.max = other.refreshRateRange.max; + } } @VisibleForTesting @@ -932,7 +934,7 @@ public class DisplayModeDirector { mDefaultModeByDisplay.put(displayId, info.getDefaultMode()); } if (changed) { - notifyAllowedModesChangedLocked(); + notifyDesiredDisplayModeSpecsChangedLocked(); } } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 308c755a0868..bf58efeb2b02 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -112,18 +112,19 @@ final class LocalDisplayAdapter extends DisplayAdapter { activeColorMode = Display.COLOR_MODE_INVALID; } int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken); - int[] allowedConfigs = SurfaceControl.getAllowedDisplayConfigs(displayToken); + SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = + SurfaceControl.getDesiredDisplayConfigSpecs(displayToken); LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device == null) { // Display was added. final boolean isInternal = mDevices.size() == 0; - device = new LocalDisplayDevice(displayToken, physicalDisplayId, - configs, activeConfig, allowedConfigs, colorModes, activeColorMode, + device = new LocalDisplayDevice(displayToken, physicalDisplayId, configs, + activeConfig, desiredDisplayConfigSpecs, colorModes, activeColorMode, isInternal); mDevices.put(physicalDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig, - allowedConfigs, colorModes, activeColorMode)) { + desiredDisplayConfigSpecs, colorModes, activeColorMode)) { // Display properties changed. sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); } @@ -172,12 +173,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mDefaultModeId; private int mActiveModeId; private boolean mActiveModeInvalid; - private int[] mAllowedModeIds; - private float mMinRefreshRate; - private float mMaxRefreshRate; - private boolean mAllowedModeIdsInvalid; + private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs = + new DisplayModeDirector.DesiredDisplayModeSpecs(); + private boolean mDisplayModeSpecsInvalid; private int mActivePhysIndex; - private int[] mAllowedPhysIndexes; private int mActiveColorMode; private boolean mActiveColorModeInvalid; private Display.HdrCapabilities mHdrCapabilities; @@ -188,13 +187,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo, - int[] allowedDisplayInfos, int[] colorModes, int activeColorMode, - boolean isInternal) { + SurfaceControl.DesiredDisplayConfigSpecs physicalDisplayConfigSpecs, + int[] colorModes, int activeColorMode, boolean isInternal) { super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId); mPhysicalDisplayId = physicalDisplayId; mIsInternal = isInternal; updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo, - allowedDisplayInfos, colorModes, activeColorMode); + physicalDisplayConfigSpecs, colorModes, activeColorMode); updateColorModesLocked(colorModes, activeColorMode); mSidekickInternal = LocalServices.getService(SidekickInternal.class); if (mIsInternal) { @@ -213,10 +212,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { public boolean updatePhysicalDisplayInfoLocked( SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo, - int[] allowedDisplayInfos, int[] colorModes, int activeColorMode) { + SurfaceControl.DesiredDisplayConfigSpecs physicalDisplayConfigSpecs, + int[] colorModes, int activeColorMode) { mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length); mActivePhysIndex = activeDisplayInfo; - mAllowedPhysIndexes = Arrays.copyOf(allowedDisplayInfos, allowedDisplayInfos.length); // Build an updated list of all existing modes. ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>(); boolean modesAdded = false; @@ -264,6 +263,26 @@ final class LocalDisplayAdapter extends DisplayAdapter { sendTraversalRequestLocked(); } + // Check whether surface flinger spontaneously changed display config specs out from + // under us. If so, schedule a traversal to reapply our display config specs. + if (mDisplayModeSpecs.defaultModeId != 0) { + int activeDefaultMode = + findMatchingModeIdLocked(physicalDisplayConfigSpecs.defaultConfig); + // If we can't map the defaultConfig index to a mode, then the physical display + // configs must have changed, and the code below for handling changes to the + // list of available modes will take care of updating display config specs. + if (activeDefaultMode != 0) { + if (mDisplayModeSpecs.defaultModeId != activeDefaultMode + || mDisplayModeSpecs.refreshRateRange.min + != physicalDisplayConfigSpecs.minRefreshRate + || mDisplayModeSpecs.refreshRateRange.max + != physicalDisplayConfigSpecs.maxRefreshRate) { + mDisplayModeSpecsInvalid = true; + sendTraversalRequestLocked(); + } + } + } + boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded; // If the records haven't changed then we're done here. if (!recordsChanged) { @@ -286,6 +305,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { mDefaultModeId = activeRecord.mMode.getModeId(); } + // Determine whether the display mode specs' default mode is still there. + if (mSupportedModes.indexOfKey(mDisplayModeSpecs.defaultModeId) < 0) { + if (mDisplayModeSpecs.defaultModeId != 0) { + Slog.w(TAG, + "DisplayModeSpecs default mode no longer available, using currently" + + " active mode as default."); + } + mDisplayModeSpecs.defaultModeId = activeRecord.mMode.getModeId(); + mDisplayModeSpecsInvalid = true; + } + // Determine whether the active mode is still there. if (mSupportedModes.indexOfKey(mActiveModeId) < 0) { if (mActiveModeId != 0) { @@ -296,21 +326,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { mActiveModeInvalid = true; } - // Determine what the currently allowed modes are - mAllowedModeIds = new int[] { mActiveModeId }; - int[] allowedModeIds = new int[mAllowedPhysIndexes.length]; - int size = 0; - for (int physIndex : mAllowedPhysIndexes) { - int modeId = findMatchingModeIdLocked(physIndex); - if (modeId > 0) { - allowedModeIds[size++] = modeId; - } - } - - // If this is different from our desired allowed modes, then mark our current set as - // invalid so we correct this on the next traversal. - mAllowedModeIdsInvalid = !Arrays.equals(allowedModeIds, mAllowedModeIds); - // Schedule traversals so that we apply pending changes. sendTraversalRequestLocked(); return true; @@ -624,10 +639,40 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, - float maxRefreshRate, int[] modes) { - updateDesiredDisplayConfigSpecs(defaultModeId, minRefreshRate, maxRefreshRate); - updateAllowedModesLocked(modes); + public void setDesiredDisplayModeSpecsLocked( + DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) { + if (displayModeSpecs.defaultModeId == 0) { + // Bail if the caller is requesting a null mode. We'll get called again shortly with + // a valid mode. + return; + } + int defaultPhysIndex = findDisplayInfoIndexLocked(displayModeSpecs.defaultModeId); + if (defaultPhysIndex < 0) { + // When a display is hotplugged, it's possible for a mode to be removed that was + // previously valid. Because of the way display changes are propagated through the + // framework, and the caching of the display mode specs in LogicalDisplay, it's + // possible we'll get called with a stale mode id that no longer represents a valid + // mode. This should only happen in extremely rare cases. A followup call will + // contain a valid mode id. + Slog.w(TAG, + "Ignoring request for invalid default mode id " + + displayModeSpecs.defaultModeId); + updateDeviceInfoLocked(); + return; + } + if (mDisplayModeSpecsInvalid || !displayModeSpecs.equals(mDisplayModeSpecs)) { + mDisplayModeSpecsInvalid = false; + mDisplayModeSpecs.copyFrom(displayModeSpecs); + final IBinder token = getDisplayTokenLocked(); + SurfaceControl.setDesiredDisplayConfigSpecs(token, + new SurfaceControl.DesiredDisplayConfigSpecs(defaultPhysIndex, + mDisplayModeSpecs.refreshRateRange.min, + mDisplayModeSpecs.refreshRateRange.max)); + int activePhysIndex = SurfaceControl.getActiveConfig(token); + if (updateActiveModeLocked(activePhysIndex)) { + updateDeviceInfoLocked(); + } + } } @Override @@ -650,107 +695,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { mActiveModeInvalid = mActiveModeId == 0; if (mActiveModeInvalid) { Slog.w(TAG, "In unknown mode after setting allowed configs" - + ": allowedPhysIndexes=" + mAllowedPhysIndexes + ", activePhysIndex=" + mActivePhysIndex); } return true; } - // TODO(b/142507213): Remove once refresh rates are plummed through to kernel. - public void updateAllowedModesLocked(int[] allowedModes) { - if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) { - return; - } - if (updateAllowedModesInternalLocked(allowedModes)) { - updateDeviceInfoLocked(); - } - } - - public void updateDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, - float maxRefreshRate) { - if (minRefreshRate == mMinRefreshRate - && maxRefreshRate == mMaxRefreshRate - && defaultModeId == mDefaultModeId) { - return; - } - if (updateDesiredDisplayConfigSpecsInternalLocked(defaultModeId, minRefreshRate, - maxRefreshRate)) { - updateDeviceInfoLocked(); - } - } - - public boolean updateDesiredDisplayConfigSpecsInternalLocked(int defaultModeId, - float minRefreshRate, float maxRefreshRate) { - if (DEBUG) { - Slog.w(TAG, "updateDesiredDisplayConfigSpecsInternalLocked(" - + "defaultModeId=" - + Integer.toString(defaultModeId) - + ", minRefreshRate=" - + Float.toString(minRefreshRate) - + ", maxRefreshRate=" - + Float.toString(minRefreshRate)); - } - - final IBinder token = getDisplayTokenLocked(); - SurfaceControl.setDesiredDisplayConfigSpecs(token, - new SurfaceControl.DesiredDisplayConfigSpecs( - defaultModeId, minRefreshRate, maxRefreshRate)); - int activePhysIndex = SurfaceControl.getActiveConfig(token); - return updateActiveModeLocked(activePhysIndex); - } - - public boolean updateAllowedModesInternalLocked(int[] allowedModes) { - if (DEBUG) { - Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes=" - + Arrays.toString(allowedModes) + ")"); - } - int[] allowedPhysIndexes = new int[allowedModes.length]; - int size = 0; - for (int modeId : allowedModes) { - int physIndex = findDisplayInfoIndexLocked(modeId); - if (physIndex < 0) { - Slog.w(TAG, "Requested mode ID " + modeId + " not available," - + " dropping from allowed set."); - } else { - allowedPhysIndexes[size++] = physIndex; - } - } - - // If we couldn't find one or more of the suggested allowed modes then we need to - // shrink the array to its actual size. - if (size != allowedModes.length) { - allowedPhysIndexes = Arrays.copyOf(allowedPhysIndexes, size); - } - - // If we found no suitable modes, then we try again with the default mode which we - // assume has a suitable physical config. - if (size == 0) { - if (DEBUG) { - Slog.w(TAG, "No valid modes allowed, falling back to default mode (id=" - + mDefaultModeId + ")"); - } - allowedModes = new int[] { mDefaultModeId }; - allowedPhysIndexes = new int[] { findDisplayInfoIndexLocked(mDefaultModeId) }; - } - - mAllowedModeIds = allowedModes; - mAllowedModeIdsInvalid = false; - - if (Arrays.equals(mAllowedPhysIndexes, allowedPhysIndexes)) { - return false; - } - mAllowedPhysIndexes = allowedPhysIndexes; - - if (DEBUG) { - Slog.w(TAG, "Setting allowed physical configs: allowedPhysIndexes=" - + Arrays.toString(allowedPhysIndexes)); - } - - SurfaceControl.setAllowedDisplayConfigs(getDisplayTokenLocked(), allowedPhysIndexes); - int activePhysIndex = SurfaceControl.getActiveConfig(getDisplayTokenLocked()); - return updateActiveModeLocked(activePhysIndex); - } - public boolean requestColorModeLocked(int colorMode) { if (mActiveColorMode == colorMode) { return false; @@ -770,11 +719,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { public void dumpLocked(PrintWriter pw) { super.dumpLocked(pw); pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); - pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes)); - pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds)); - pw.println("mMinRefreshRate=" + mMinRefreshRate); - pw.println("mMaxRefreshRate=" + mMaxRefreshRate); - pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid); + pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}"); + pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid); pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActiveModeId=" + mActiveModeId); pw.println("mActiveColorMode=" + mActiveColorMode); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index f4b2dc8cfc98..b649a50f9443 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -87,9 +87,11 @@ final class LogicalDisplay { // True if the logical display has unique content. private boolean mHasContent; - private int[] mAllowedDisplayModes = new int[0]; private int mRequestedColorMode; + private DisplayModeDirector.DesiredDisplayModeSpecs mDesiredDisplayModeSpecs = + new DisplayModeDirector.DesiredDisplayModeSpecs(); + // The display offsets to apply to the display projection. private int mDisplayOffsetX; private int mDisplayOffsetY; @@ -352,12 +354,12 @@ final class LogicalDisplay { // Set the color mode and allowed display mode. if (device == mPrimaryDisplayDevice) { - // See ag/9588196 for correct values. - device.setDesiredDisplayConfigSpecs(0, 60, 60, mAllowedDisplayModes); + device.setDesiredDisplayModeSpecsLocked(mDesiredDisplayModeSpecs); device.setRequestedColorModeLocked(mRequestedColorMode); } else { // Reset to default for non primary displays - device.setDesiredDisplayConfigSpecs(0, 60, 60, new int[] {0}); + device.setDesiredDisplayModeSpecsLocked( + new DisplayModeDirector.DesiredDisplayModeSpecs()); device.setRequestedColorModeLocked(0); } @@ -462,17 +464,18 @@ final class LogicalDisplay { } /** - * Sets the display modes the system is free to switch between. + * Sets the display configs the system can use. */ - public void setAllowedDisplayModesLocked(int[] modes) { - mAllowedDisplayModes = modes; + public void setDesiredDisplayModeSpecsLocked( + DisplayModeDirector.DesiredDisplayModeSpecs specs) { + mDesiredDisplayModeSpecs = specs; } /** - * Returns the display modes the system is free to switch between. + * Returns the display configs the system can choose. */ - public int[] getAllowedDisplayModesLocked() { - return mAllowedDisplayModes; + public DisplayModeDirector.DesiredDisplayModeSpecs getDesiredDisplayModeSpecsLocked() { + return mDesiredDisplayModeSpecs; } /** @@ -531,7 +534,7 @@ final class LogicalDisplay { pw.println("mDisplayId=" + mDisplayId); pw.println("mLayerStack=" + mLayerStack); pw.println("mHasContent=" + mHasContent); - pw.println("mAllowedDisplayModes=" + Arrays.toString(mAllowedDisplayModes)); + pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}"); pw.println("mRequestedColorMode=" + mRequestedColorMode); pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")"); pw.println("mDisplayScalingDisabled=" + mDisplayScalingDisabled); diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 739dd64451e4..b6255d15795e 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -315,9 +315,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter { } @Override - public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, - float maxRefreshRate, int[] modes) { - final int id = defaultModeId; + public void setDesiredDisplayModeSpecsLocked( + DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) { + final int id = displayModeSpecs.defaultModeId; int index = -1; if (id == 0) { // Use the default. diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java index 3049522c2231..d673ec84c47e 100644 --- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java +++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java @@ -17,6 +17,7 @@ package com.android.server.incremental; import android.annotation.NonNull; +import android.content.ComponentName; import android.content.Context; import android.content.pm.DataLoaderManager; import android.content.pm.DataLoaderParamsParcel; @@ -85,7 +86,8 @@ public class IncrementalManagerService extends IIncrementalManager.Stub { DataLoaderParamsParcel params, IDataLoaderStatusListener listener) { Bundle dataLoaderParams = new Bundle(); - dataLoaderParams.putCharSequence("packageName", params.packageName); + dataLoaderParams.putParcelable("componentName", + new ComponentName(params.packageName, params.className)); dataLoaderParams.putParcelable("control", control); dataLoaderParams.putParcelable("params", params); DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); @@ -109,8 +111,7 @@ public class IncrementalManagerService extends IIncrementalManager.Stub { return false; } try { - // TODO: fix file list - dataLoader.start(null); + dataLoader.start(); return true; } catch (RemoteException ex) { return false; diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java index 6a8434aad88b..5c18f5880146 100644 --- a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java +++ b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java @@ -19,6 +19,7 @@ package com.android.server.incremental; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ComponentName; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; @@ -54,6 +55,8 @@ public final class IncrementalManagerShellCommand extends ShellCommand { private static final String TAG = "IncrementalShellCommand"; // Assuming the adb data loader is always installed on the device private static final String LOADER_PACKAGE_NAME = "com.android.incremental.nativeadb"; + private static final String LOADER_CLASS_NAME = + LOADER_PACKAGE_NAME + ".NativeAdbDataLoaderService"; private final @NonNull Context mContext; private static final int ERROR_INVALID_ARGUMENTS = -1; @@ -111,14 +114,15 @@ public final class IncrementalManagerShellCommand extends ShellCommand { pw.println("File names and sizes don't match."); return ERROR_DATA_LOADER_INIT; } - final DataLoaderParams params = new DataLoaderParams( - "", LOADER_PACKAGE_NAME, dataLoaderDynamicArgs); + final DataLoaderParams params = DataLoaderParams.forIncremental( + new ComponentName(LOADER_PACKAGE_NAME, LOADER_CLASS_NAME), "", + dataLoaderDynamicArgs); PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); sessionParams.installFlags |= PackageManager.INSTALL_ALL_USERS; // Replace existing if same package is already installed sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; - sessionParams.setIncrementalParams(params); + sessionParams.setDataLoaderParams(params); try { int sessionId = packageInstaller.createSession(sessionParams); diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index b77c85929dd6..bf73aa39c581 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -317,6 +317,7 @@ public class InputManagerService extends IInputManager.Stub this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); + mStaticAssociations = loadStaticInputPortAssociations(); mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" @@ -328,7 +329,6 @@ public class InputManagerService extends IInputManager.Stub mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null : new File(doubleTouchGestureEnablePath); - mStaticAssociations = loadStaticInputPortAssociations(); LocalServices.addService(InputManagerInternal.class, new LocalService()); } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java index 005fb696b089..3762ebb0b902 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 @@ public class AppIntegrityManagerService extends SystemService { @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 39c1b8535565..6c80a884edb6 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -17,40 +17,113 @@ 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 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); + try { + integrityVerificationFilter.addDataType(PACKAGE_MIME_TYPE); + } catch (IntentFilter.MalformedMimeTypeException e) { + throw new RuntimeException("Mime type malformed: should never happen.", e); + } mContext.registerReceiver( new BroadcastReceiver() { @@ -68,13 +141,418 @@ class AppIntegrityManagerServiceImpl { 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()); - mPackageManagerInternal.setIntegrityVerificationResult( - verificationId, PackageManager.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 toHexString(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 toHexString(publicKey); + } catch (NoSuchAlgorithmException | CertificateEncodingException e) { + throw new IllegalArgumentException("Error error computing fingerprint", e); + } + } + + private static String toHexString(byte[] bytes) { + // each byte is represented by two hex chars + StringBuffer hexString = new StringBuffer(bytes.length * 2); + for (int i = 0; i < bytes.length; i++) { + hexString.append(String.format("%02X", bytes[i])); + } + return new String(hexString); + } + + 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 000000000000..bdf027995fad --- /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/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java index b8202b6c0dc6..0ea6efc41f42 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 @@ package com.android.server.integrity.engine; 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 @@ import java.util.List; * <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 @@ public final class RuleEvaluationEngine { * 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 000000000000..6b582ae7b5f2 --- /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 000000000000..28d2e6914103 --- /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 000000000000..5c51f31ba8cc --- /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/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java index cfe50c6c8ac9..ebf6a2e20238 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java @@ -16,6 +16,10 @@ package com.android.server.integrity.serializer; +import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED; +import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED; +import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED; + import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; import android.content.integrity.Formula; @@ -29,6 +33,7 @@ import java.io.OutputStream; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map; import java.util.Optional; /** A helper class to serialize rules from the {@link Rule} model to Xml representation. */ @@ -75,13 +80,32 @@ public class RuleXmlSerializer implements RuleSerializer { } } - private void serializeRules(List<Rule> rules, XmlSerializer xmlSerializer) throws IOException { - xmlSerializer.startTag(NAMESPACE, RULE_LIST_TAG); + private void serializeRules(List<Rule> rules, XmlSerializer xmlSerializer) + throws RuleSerializeException { + try { + // Determine the indexing groups and the order of the rules within each indexed group. + Map<Integer, List<Rule>> indexedRules = + RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules); + + // Write the XML formatted rules in order. + xmlSerializer.startTag(NAMESPACE, RULE_LIST_TAG); + + serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED), xmlSerializer); + serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED), xmlSerializer); + serializeRuleList(indexedRules.get(NOT_INDEXED), xmlSerializer); + + xmlSerializer.endTag(NAMESPACE, RULE_LIST_TAG); + xmlSerializer.endDocument(); + } catch (Exception e) { + throw new RuleSerializeException(e.getMessage(), e); + } + } + + private void serializeRuleList(List<Rule> rules, XmlSerializer xmlSerializer) + throws IOException { for (Rule rule : rules) { serializeRule(rule, xmlSerializer); } - xmlSerializer.endTag(NAMESPACE, RULE_LIST_TAG); - xmlSerializer.endDocument(); } private void serializeRule(Rule rule, XmlSerializer xmlSerializer) throws IOException { diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java index eb706d7791d3..1d391775e550 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 @@ class AudioPlayerStateMonitor { 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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 83d0ecd33fbc..2ebca8870ec5 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } 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/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java index 0719797ed85a..0dfea7fd5f96 100644 --- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java +++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java @@ -76,13 +76,12 @@ public class DataLoaderManagerService extends SystemService { return false; } } - CharSequence packageNameSeq = params.getCharSequence("packageName"); - if (packageNameSeq == null) { - Slog.e(TAG, "Must specify package name."); + ComponentName componentName = params.getParcelable("componentName"); + if (componentName == null) { + Slog.e(TAG, "Must specify component name."); return false; } - String packageName = packageNameSeq.toString(); - ComponentName dataLoaderComponent = getDataLoaderServiceName(packageName); + ComponentName dataLoaderComponent = resolveDataLoaderComponentName(componentName); if (dataLoaderComponent == null) { return false; } @@ -103,22 +102,23 @@ public class DataLoaderManagerService extends SystemService { /** * Find the ComponentName of the data loader service provider, given its package name. * - * @param packageName the package name of the provider. + * @param componentName the name of the provider. * @return ComponentName of the data loader service provider. Null if provider not found. */ - private @Nullable ComponentName getDataLoaderServiceName(String packageName) { + private @Nullable ComponentName resolveDataLoaderComponentName( + ComponentName componentName) { final PackageManager pm = mContext.getPackageManager(); if (pm == null) { Slog.e(TAG, "PackageManager is not available."); return null; } Intent intent = new Intent(Intent.ACTION_LOAD_DATA); - intent.setPackage(packageName); + intent.setComponent(componentName); List<ResolveInfo> services = pm.queryIntentServicesAsUser(intent, 0, UserHandle.getCallingUserId()); if (services == null || services.isEmpty()) { Slog.e(TAG, - "Failed to find data loader service provider in package " + packageName); + "Failed to find data loader service provider in " + componentName); return null; } @@ -128,23 +128,21 @@ public class DataLoaderManagerService extends SystemService { int numServices = services.size(); for (int i = 0; i < numServices; i++) { ResolveInfo ri = services.get(i); - ComponentName componentName = new ComponentName( + ComponentName resolved = new ComponentName( ri.serviceInfo.packageName, ri.serviceInfo.name); // There should only be one matching provider inside the given package. // If there's more than one, return the first one found. try { - ApplicationInfo ai = pm.getApplicationInfo(componentName.getPackageName(), 0); + ApplicationInfo ai = pm.getApplicationInfo(resolved.getPackageName(), 0); if (checkLoader && !ai.isPrivilegedApp()) { Slog.w(TAG, - "Data loader: " + componentName.getPackageName() - + " is not a privileged app, skipping."); + "Data loader: " + resolved + " is not a privileged app, skipping."); continue; } - return componentName; + return resolved; } catch (PackageManager.NameNotFoundException ex) { Slog.w(TAG, - "Privileged data loader: " + componentName.getPackageName() - + " not found, skipping."); + "Privileged data loader: " + resolved + " not found, skipping."); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index a54534b461f1..e2dfa126225f 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 class PackageInstallerService extends IPackageInstaller.Stub implements public void systemReady() { mAppOps = mContext.getSystemService(AppOpsManager.class); + mStagingManager.systemReady(); synchronized (mSessions) { readSessionsLocked(); @@ -257,8 +258,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK // atomic install which needs to query sessions, which requires lock on mSessions. + boolean isDeviceUpgrading = mPm.isDeviceUpgrading(); for (PackageInstallerSession session : stagedSessionsToRestore) { - if (mPm.isDeviceUpgrading() && !session.isStagedAndInTerminalState()) { + if (isDeviceUpgrading && !session.isStagedAndInTerminalState()) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "Build fingerprint has changed"); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index c12395e28e28..ac183dcf6b7a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -16,6 +16,8 @@ package com.android.server.pm; +import static android.content.pm.DataLoaderType.INCREMENTAL; +import static android.content.pm.DataLoaderType.STREAMING; import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; @@ -49,12 +51,18 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManagerInternal; +import android.content.ComponentName; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; +import android.content.pm.DataLoaderManager; +import android.content.pm.DataLoaderParams; +import android.content.pm.FileSystemControlParcel; +import android.content.pm.IDataLoader; +import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.IPackageInstallerSessionFileSystemConnector; @@ -84,6 +92,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.Process; +import android.os.RemoteException; import android.os.RevocableFileDescriptor; import android.os.SystemProperties; import android.os.UserHandle; @@ -127,12 +136,12 @@ import java.io.FileDescriptor; import java.io.FileFilter; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String TAG = "PackageInstallerSession"; @@ -189,7 +198,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String ATTR_VOLUME_UUID = "volumeUuid"; private static final String ATTR_NAME = "name"; private static final String ATTR_INSTALL_REASON = "installRason"; - private static final String ATTR_DATA_LOADER_PACKAGE_NAME = "dataLoaderPackageName"; + private static final String ATTR_IS_DATALOADER = "isDataLoader"; + private static final String ATTR_DATALOADER_TYPE = "dataLoaderType"; + private static final String ATTR_DATALOADER_PACKAGE_NAME = "dataLoaderPackageName"; + private static final String ATTR_DATALOADER_CLASS_NAME = "dataLoaderClassName"; + private static final String ATTR_DATALOADER_ARGUMENTS = "dataLoaderArguments"; private static final String ATTR_LENGTH_BYTES = "lengthBytes"; private static final String ATTR_METADATA = "metadata"; @@ -414,7 +427,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { }; private boolean isDataLoaderInstallation() { - return !TextUtils.isEmpty(params.dataLoaderPackageName); + return params.dataLoaderParams != null; + } + + private boolean isStreamingInstallation() { + return isDataLoaderInstallation() && params.dataLoaderParams.getType() == STREAMING; + } + + private boolean isIncrementalInstallation() { + return isDataLoaderInstallation() && params.dataLoaderParams.getType() == INCREMENTAL; } /** @@ -525,14 +546,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; // TODO(b/136132412): sanity check if session should not be incremental - if (!params.isStaged && params.incrementalParams != null - && !params.incrementalParams.getPackageName().isEmpty()) { + if (!params.isStaged && isIncrementalInstallation()) { IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService( Context.INCREMENTAL_SERVICE); if (incrementalManager != null) { mIncrementalFileStorages = new IncrementalFileStorages(mPackageName, stageDir, incrementalManager, - params.incrementalParams); + params.dataLoaderParams); } } } @@ -714,7 +734,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { public void removeSplit(String splitName) { if (isDataLoaderInstallation()) { throw new IllegalStateException( - "Cannot remove splits in a callback installation session."); + "Cannot remove splits in a data loader installation session."); } if (TextUtils.isEmpty(params.appPackageName)) { throw new IllegalStateException("Must specify package name to remove a split"); @@ -753,7 +773,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void assertCanWrite(boolean reverseMode) { if (isDataLoaderInstallation()) { throw new IllegalStateException( - "Cannot write regular files in a callback installation session."); + "Cannot write regular files in a data loader installation session."); } synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); @@ -894,7 +914,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { public ParcelFileDescriptor openRead(String name) { if (isDataLoaderInstallation()) { throw new IllegalStateException( - "Cannot read regular files in a callback installation session."); + "Cannot read regular files in a data loader installation session."); } synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); @@ -1663,7 +1683,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { computeProgressLocked(true); // Unpack native libraries for non-incremental installation - if (params.incrementalParams == null) { + if (isIncrementalInstallation()) { extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs()); } } @@ -2382,7 +2402,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (!isDataLoaderInstallation()) { throw new IllegalStateException( - "Cannot add files to non-callback installation session."); + "Cannot add files to non-data loader installation session."); } // Use installer provided name for now; we always rename later if (!FileUtils.isValidExtFilename(name)) { @@ -2401,7 +2421,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { public void removeFile(String name) { if (!isDataLoaderInstallation()) { throw new IllegalStateException( - "Cannot add files to non-callback installation session."); + "Cannot add files to non-data loader installation session."); } if (TextUtils.isEmpty(params.appPackageName)) { throw new IllegalStateException("Must specify package name to remove a split"); @@ -2415,76 +2435,121 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + static class Notificator { + private int mValue = 0; + + void setValue(int value) { + synchronized (this) { + mValue = value; + this.notify(); + } + } + int waitForValue() { + synchronized (this) { + while (mValue == 0) { + try { + this.wait(); + } catch (InterruptedException e) { + // Happens if someone interrupts your thread. + } + } + return mValue; + } + } + } + /** * Makes sure files are present in staging location. */ private void prepareDataLoader() throws PackageManagerException, StreamingException { - if (!isDataLoaderInstallation()) { + if (!isStreamingInstallation()) { return; } FileSystemConnector connector = new FileSystemConnector(); - FileInfo[] addedFiles = mFiles.stream().filter( - file -> sAddedFilter.accept(new File(file.name))).toArray(FileInfo[]::new); - String[] removedFiles = mFiles.stream().filter( + List<InstallationFile> addedFiles = mFiles.stream().filter( + file -> sAddedFilter.accept(new File(file.name))).map( + file -> new InstallationFile( + file.name, file.lengthBytes, file.metadata)).collect( + Collectors.toList()); + List<String> removedFiles = mFiles.stream().filter( file -> sRemovedFilter.accept(new File(file.name))).map( - file -> file.name.substring(0, - file.name.length() - REMOVE_MARKER_EXTENSION.length())).toArray( - String[]::new); + file -> file.name.substring( + 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect( + Collectors.toList()); - DataLoader dataLoader = new DataLoader(); - try { - dataLoader.onCreate(connector); + DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); + if (dataLoaderManager == null) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to find data loader manager service"); + } - if (!dataLoader.onPrepareImage(addedFiles, removedFiles)) { - throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, - "Failed to prepare image."); + // TODO(b/146080380): make this code async. + final Notificator created = new Notificator(); + final Notificator started = new Notificator(); + final Notificator imageReady = new Notificator(); + + IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() { + @Override + public void onStatusChanged(int dataLoaderId, int status) { + switch (status) { + case IDataLoaderStatusListener.DATA_LOADER_CREATED: { + created.setValue(1); + break; + } + case IDataLoaderStatusListener.DATA_LOADER_STARTED: { + started.setValue(1); + break; + } + case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: { + imageReady.setValue(1); + break; + } + case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: { + imageReady.setValue(2); + break; + } + } } - } catch (IOException e) { - throw new StreamingException(e); - } finally { - dataLoader.onDestroy(); - } - } + }; + + final DataLoaderParams params = this.params.dataLoaderParams; - static class DataLoader { - private ParcelFileDescriptor mInFd = null; - private FileSystemConnector mConnector = null; + final FileSystemControlParcel control = new FileSystemControlParcel(); + control.callback = connector; - void onCreate(FileSystemConnector connector) throws IOException { - mConnector = connector; + Bundle dataLoaderParams = new Bundle(); + dataLoaderParams.putParcelable("componentName", params.getComponentName()); + dataLoaderParams.putParcelable("control", control); + dataLoaderParams.putParcelable("params", params.getData()); + + if (!dataLoaderManager.initializeDataLoader(sessionId, dataLoaderParams, listener)) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to initialize data loader"); } + created.waitForValue(); - void onDestroy() { - IoUtils.closeQuietly(mInFd); + IDataLoader dataLoader = dataLoaderManager.getDataLoader(sessionId); + if (dataLoader == null) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failure to obtain data loader"); } - private static final String STDIN_PATH = "-"; - boolean onPrepareImage(FileInfo[] addedFiles, String[] removedFiles) throws IOException { - for (FileInfo fileInfo : addedFiles) { - String filePath = new String(fileInfo.metadata, StandardCharsets.UTF_8); - if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { - if (mInFd == null) { - Slog.e(TAG, "Invalid stdin file descriptor."); - return false; - } - ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(mInFd.getFileDescriptor()); - mConnector.writeData(fileInfo.name, 0, fileInfo.lengthBytes, inFd); - } else { - File localFile = new File(filePath); - ParcelFileDescriptor incomingFd = null; - try { - incomingFd = ParcelFileDescriptor.open(localFile, - ParcelFileDescriptor.MODE_READ_ONLY); - mConnector.writeData(fileInfo.name, 0, localFile.length(), incomingFd); - } finally { - IoUtils.closeQuietly(incomingFd); - } - } + try { + dataLoader.start(); + started.waitForValue(); + + dataLoader.prepareImage(addedFiles, removedFiles); + if (imageReady.waitForValue() == 2) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to prepare image."); } - return true; + + dataLoader.destroy(); + } catch (RemoteException e) { + throw new StreamingException(e); } } @@ -2846,7 +2911,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason); - writeStringAttribute(out, ATTR_DATA_LOADER_PACKAGE_NAME, params.dataLoaderPackageName); + final boolean isDataLoader = params.dataLoaderParams != null; + writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader); + if (isDataLoader) { + writeIntAttribute(out, ATTR_DATALOADER_TYPE, params.dataLoaderParams.getType()); + writeStringAttribute(out, ATTR_DATALOADER_PACKAGE_NAME, + params.dataLoaderParams.getComponentName().getPackageName()); + writeStringAttribute(out, ATTR_DATALOADER_CLASS_NAME, + params.dataLoaderParams.getComponentName().getClassName()); + writeStringAttribute(out, ATTR_DATALOADER_ARGUMENTS, + params.dataLoaderParams.getArguments()); + } writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); writeWhitelistedRestrictedPermissionsLocked(out, @@ -2957,7 +3032,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON); - params.dataLoaderPackageName = readStringAttribute(in, ATTR_DATA_LOADER_PACKAGE_NAME); + if (readBooleanAttribute(in, ATTR_IS_DATALOADER)) { + params.dataLoaderParams = new DataLoaderParams( + readIntAttribute(in, ATTR_DATALOADER_TYPE), + new ComponentName( + readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME), + readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)), + readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS), + null); + } final File appIconFile = buildAppIconFile(sessionId, sessionsDir); if (appIconFile.exists()) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index da104eea8e5c..785ca7dd1fe8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -27,6 +27,8 @@ import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.content.Intent.ACTION_MAIN; import static android.content.Intent.CATEGORY_DEFAULT; import static android.content.Intent.CATEGORY_HOME; +import static android.content.Intent.EXTRA_PACKAGE_NAME; +import static android.content.Intent.EXTRA_VERSION_CODE; import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509; import static android.content.pm.PackageManager.CERT_INPUT_SHA256; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; @@ -34,6 +36,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; @@ -558,6 +561,11 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean DEFAULT_VERIFY_ENABLE = true; /** + * Whether integrity verification is enabled by default. + */ + private static final boolean DEFAULT_INTEGRITY_VERIFY_ENABLE = true; + + /** * The default maximum time to wait for the verification agent to return in * milliseconds. */ @@ -1444,6 +1452,7 @@ public class PackageManagerService extends IPackageManager.Stub static final int DEFERRED_NO_KILL_POST_DELETE = 23; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24; static final int INTEGRITY_VERIFICATION_COMPLETE = 25; + static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26; static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500; @@ -1707,13 +1716,13 @@ public class PackageManagerService extends IPackageManager.Stub final int verificationId = msg.arg1; final PackageVerificationState state = mPendingVerification.get(verificationId); - if ((state != null) && !state.timeoutExtended()) { + if ((state != null) && !state.isVerificationComplete() + && !state.timeoutExtended()) { final InstallParams params = state.getInstallParams(); final InstallArgs args = params.mArgs; final Uri originUri = Uri.fromFile(args.origin.resolvedFile); Slog.i(TAG, "Verification timed out for " + originUri); - mPendingVerification.remove(verificationId); final UserHandle user = args.getUser(); if (getDefaultVerificationResponse(user) @@ -1728,11 +1737,54 @@ public class PackageManagerService extends IPackageManager.Stub PackageManager.VERIFICATION_REJECT, user); params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); + state.setVerifierResponse(Binder.getCallingUid(), + PackageManager.VERIFICATION_REJECT); + } + + if (state.areAllVerificationsComplete()) { + mPendingVerification.remove(verificationId); } Trace.asyncTraceEnd( TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); + params.handleVerificationFinished(); + + } + break; + } + case CHECK_PENDING_INTEGRITY_VERIFICATION: { + final int verificationId = msg.arg1; + final PackageVerificationState state = mPendingVerification.get(verificationId); + + if (state != null && !state.isIntegrityVerificationComplete()) { + final InstallParams params = state.getInstallParams(); + final InstallArgs args = params.mArgs; + final Uri originUri = Uri.fromFile(args.origin.resolvedFile); + + Slog.i(TAG, "Integrity verification timed out for " + originUri); + + state.setIntegrityVerificationResult( + getDefaultIntegrityVerificationResponse()); + + if (getDefaultIntegrityVerificationResponse() + == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) { + Slog.i(TAG, "Integrity check times out, continuing with " + originUri); + } else { + params.setReturnCode( + PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); + } + + if (state.areAllVerificationsComplete()) { + mPendingVerification.remove(verificationId); + } + + Trace.asyncTraceEnd( + TRACE_TAG_PACKAGE_MANAGER, + "integrity_verification", + verificationId); + + params.handleIntegrityVerificationFinished(); } break; } @@ -1741,7 +1793,9 @@ public class PackageManagerService extends IPackageManager.Stub final PackageVerificationState state = mPendingVerification.get(verificationId); if (state == null) { - Slog.w(TAG, "Invalid verification token " + verificationId + " received"); + Slog.w(TAG, "Verification with id " + verificationId + + " not found." + + " It may be invalid or overridden by integrity verification"); break; } @@ -1750,8 +1804,6 @@ public class PackageManagerService extends IPackageManager.Stub state.setVerifierResponse(response.callerUid, response.code); if (state.isVerificationComplete()) { - mPendingVerification.remove(verificationId); - final InstallParams params = state.getInstallParams(); final InstallArgs args = params.mArgs; final Uri originUri = Uri.fromFile(args.origin.resolvedFile); @@ -1764,6 +1816,10 @@ public class PackageManagerService extends IPackageManager.Stub PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); } + if (state.areAllVerificationsComplete()) { + mPendingVerification.remove(verificationId); + } + Trace.asyncTraceEnd( TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); @@ -1773,7 +1829,40 @@ public class PackageManagerService extends IPackageManager.Stub break; } case INTEGRITY_VERIFICATION_COMPLETE: { - // TODO: implement this case. + final int verificationId = msg.arg1; + + final PackageVerificationState state = mPendingVerification.get(verificationId); + if (state == null) { + Slog.w(TAG, "Integrity verification with id " + verificationId + + " not found. It may be invalid or overridden by verifier"); + break; + } + + final int response = (Integer) msg.obj; + + final InstallParams params = state.getInstallParams(); + final InstallArgs args = params.mArgs; + final Uri originUri = Uri.fromFile(args.origin.resolvedFile); + + state.setIntegrityVerificationResult(response); + + if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) { + Slog.i(TAG, "Integrity check passed for " + originUri); + } else { + params.setReturnCode( + PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); + } + + if (state.areAllVerificationsComplete()) { + mPendingVerification.remove(verificationId); + } + + Trace.asyncTraceEnd( + TRACE_TAG_PACKAGE_MANAGER, + "integrity_verification", + verificationId); + + params.handleIntegrityVerificationFinished(); break; } case START_INTENT_FILTER_VERIFICATIONS: { @@ -13099,6 +13188,15 @@ public class PackageManagerService extends IPackageManager.Stub } /** + * Get the default integrity verification response code. + */ + private int getDefaultIntegrityVerificationResponse() { + // We are not exposing this as a user-configurable setting because we don't want to provide + // an easy way to get around the integrity check. + return PackageManager.VERIFICATION_REJECT; + } + + /** * Check whether or not package verification has been enabled. * * @return true if verification should be performed @@ -13141,6 +13239,15 @@ public class PackageManagerService extends IPackageManager.Stub } } + /** + * Check whether or not integrity verification has been enabled. + */ + private boolean isIntegrityVerificationEnabled() { + // We are not exposing this as a user-configurable setting because we don't want to provide + // an easy way to get around the integrity check. + return DEFAULT_INTEGRITY_VERIFY_ENABLE; + } + @Override public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) throws RemoteException { @@ -13851,6 +13958,7 @@ public class PackageManagerService extends IPackageManager.Stub @NonNull final InstallSource installSource; final String volumeUuid; private boolean mVerificationCompleted; + private boolean mIntegrityVerificationCompleted; private boolean mEnableRollbackCompleted; private InstallArgs mArgs; int mRet; @@ -14112,155 +14220,30 @@ public class PackageManagerService extends IPackageManager.Stub final InstallArgs args = createInstallArgs(this); mVerificationCompleted = true; + mIntegrityVerificationCompleted = true; mEnableRollbackCompleted = true; mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { - // TODO: http://b/22976637 - // Apps installed for "all" users use the device owner to verify the app - UserHandle verifierUser = getUser(); - if (verifierUser == UserHandle.ALL) { - verifierUser = UserHandle.SYSTEM; - } - - /* - * Determine if we have any installed package verifiers. If we - * do, then we'll defer to them to verify the packages. - */ - final int requiredUid = mRequiredVerifierPackage == null ? -1 - : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, - verifierUser.getIdentifier()); - final int installerUid = - verificationInfo == null ? -1 : verificationInfo.installerUid; - if (!origin.existing && requiredUid != -1 - && isVerificationEnabled( - verifierUser.getIdentifier(), installFlags, installerUid)) { - final Intent verification = new Intent( - Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); - verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), - PACKAGE_MIME_TYPE); - verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - - // Query all live verifiers based on current user state - final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification, - PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier(), - false /*allowDynamicSplits*/); - - if (DEBUG_VERIFY) { - Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent " - + verification.toString() + " with " + pkgLite.verifiers.length - + " optional verifiers"); - } - - final int verificationId = mPendingVerificationToken++; - - verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId); - - verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE, - installSource.initiatingPackageName); - - verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, - installFlags); - - verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, - pkgLite.packageName); - - verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE, - pkgLite.versionCode); - - verification.putExtra(PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE, - pkgLite.getLongVersionCode()); - - if (verificationInfo != null) { - if (verificationInfo.originatingUri != null) { - verification.putExtra(Intent.EXTRA_ORIGINATING_URI, - verificationInfo.originatingUri); - } - if (verificationInfo.referrer != null) { - verification.putExtra(Intent.EXTRA_REFERRER, - verificationInfo.referrer); - } - if (verificationInfo.originatingUid >= 0) { - verification.putExtra(Intent.EXTRA_ORIGINATING_UID, - verificationInfo.originatingUid); - } - if (verificationInfo.installerUid >= 0) { - verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, - verificationInfo.installerUid); - } - } - - final PackageVerificationState verificationState = new PackageVerificationState( - requiredUid, this); + final int verificationId = mPendingVerificationToken++; + // Perform package verification (unless we are simply moving the package). + if (!origin.existing) { + PackageVerificationState verificationState = + new PackageVerificationState(this); mPendingVerification.append(verificationId, verificationState); - final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite, - receivers, verificationState); - - DeviceIdleInternal idleController = - mInjector.getLocalDeviceIdleController(); - final long idleDuration = getVerificationTimeout(); + sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState); + ret = sendPackageVerificationRequest( + verificationId, pkgLite, verificationState); - /* - * If any sufficient verifiers were listed in the package - * manifest, attempt to ask them. - */ - if (sufficientVerifiers != null) { - final int N = sufficientVerifiers.size(); - if (N == 0) { - Slog.i(TAG, "Additional verifiers required, but none installed."); - ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; - } else { - for (int i = 0; i < N; i++) { - final ComponentName verifierComponent = sufficientVerifiers.get(i); - idleController.addPowerSaveTempWhitelistApp(Process.myUid(), - verifierComponent.getPackageName(), idleDuration, - verifierUser.getIdentifier(), false, "package verifier"); - - final Intent sufficientIntent = new Intent(verification); - sufficientIntent.setComponent(verifierComponent); - mContext.sendBroadcastAsUser(sufficientIntent, verifierUser); - } - } - } - - final ComponentName requiredVerifierComponent = matchComponentForVerifier( - mRequiredVerifierPackage, receivers); - if (ret == PackageManager.INSTALL_SUCCEEDED - && mRequiredVerifierPackage != null) { - Trace.asyncTraceBegin( - TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); - /* - * Send the intent to the required verification agent, - * but only start the verification timeout after the - * target BroadcastReceivers have run. - */ - verification.setComponent(requiredVerifierComponent); - idleController.addPowerSaveTempWhitelistApp(Process.myUid(), - mRequiredVerifierPackage, idleDuration, - verifierUser.getIdentifier(), false, "package verifier"); - mContext.sendOrderedBroadcastAsUser(verification, verifierUser, - android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final Message msg = mHandler - .obtainMessage(CHECK_PENDING_VERIFICATION); - msg.arg1 = verificationId; - mHandler.sendMessageDelayed(msg, getVerificationTimeout()); - } - }, null, 0, null, null); - - /* - * We don't want the copy to proceed until verification - * succeeds. - */ - mVerificationCompleted = false; + // If both verifications are skipped, we should remove the state. + if (verificationState.areAllVerificationsComplete()) { + mPendingVerification.remove(verificationId); } } + if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { // TODO(ruhler) b/112431924: Don't do this in case of 'move'? final int enableRollbackToken = mPendingEnableRollbackToken++; @@ -14316,6 +14299,228 @@ public class PackageManagerService extends IPackageManager.Stub mRet = ret; } + /** + * Send a request to check the integrity of the package. + */ + void sendIntegrityVerificationRequest( + int verificationId, + PackageInfoLite pkgLite, + PackageVerificationState verificationState) { + if (!isIntegrityVerificationEnabled()) { + // Consider the integrity check as passed. + verificationState.setIntegrityVerificationResult( + PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); + return; + } + + final Intent integrityVerification = + new Intent(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION); + + integrityVerification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), + PACKAGE_MIME_TYPE); + + final int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND; + integrityVerification.addFlags(flags); + + integrityVerification.putExtra(EXTRA_VERIFICATION_ID, verificationId); + integrityVerification.putExtra(EXTRA_PACKAGE_NAME, pkgLite.packageName); + integrityVerification.putExtra(EXTRA_VERSION_CODE, pkgLite.versionCode); + populateInstallerExtras(integrityVerification); + + // send to integrity component only. + integrityVerification.setPackage("android"); + + DeviceIdleInternal idleController = + mInjector.getLocalDeviceIdleController(); + final long idleDuration = getVerificationTimeout(); + + idleController.addPowerSaveTempWhitelistAppDirect(Process.myUid(), + idleDuration, + false, "integrity component"); + mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM, + /* receiverPermission= */ null, + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final Message msg = + mHandler.obtainMessage(CHECK_PENDING_INTEGRITY_VERIFICATION); + msg.arg1 = verificationId; + // TODO: do we want to use the same timeout? + mHandler.sendMessageDelayed(msg, getVerificationTimeout()); + } + }, /* scheduler= */ null, + /* initialCode= */ 0, + /* initialData= */ null, + /* initialExtras= */ null); + + Trace.asyncTraceBegin( + TRACE_TAG_PACKAGE_MANAGER, "integrity_verification", verificationId); + + // stop the copy until verification succeeds. + mIntegrityVerificationCompleted = false; + } + + /** + * Send a request to verifier(s) to verify the package if necessary, and return + * {@link PackageManager#INSTALL_SUCCEEDED} if succeeded. + */ + int sendPackageVerificationRequest( + int verificationId, + PackageInfoLite pkgLite, + PackageVerificationState verificationState) { + int ret = INSTALL_SUCCEEDED; + + // TODO: http://b/22976637 + // Apps installed for "all" users use the device owner to verify the app + UserHandle verifierUser = getUser(); + if (verifierUser == UserHandle.ALL) { + verifierUser = UserHandle.SYSTEM; + } + + /* + * Determine if we have any installed package verifiers. If we + * do, then we'll defer to them to verify the packages. + */ + final int requiredUid = mRequiredVerifierPackage == null ? -1 + : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + verifierUser.getIdentifier()); + verificationState.setRequiredVerifierUid(requiredUid); + final int installerUid = + verificationInfo == null ? -1 : verificationInfo.installerUid; + if (!origin.existing && requiredUid != -1 + && isVerificationEnabled( + verifierUser.getIdentifier(), installFlags, installerUid)) { + final Intent verification = new Intent( + Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); + verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), + PACKAGE_MIME_TYPE); + verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + // Query all live verifiers based on current user state + final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification, + PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier(), + false /*allowDynamicSplits*/); + + if (DEBUG_VERIFY) { + Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent " + + verification.toString() + " with " + pkgLite.verifiers.length + + " optional verifiers"); + } + + verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId); + + verification.putExtra( + PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, installFlags); + + verification.putExtra( + PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, pkgLite.packageName); + + verification.putExtra( + PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode); + + verification.putExtra( + PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE, + pkgLite.getLongVersionCode()); + + populateInstallerExtras(verification); + + final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite, + receivers, verificationState); + + DeviceIdleInternal idleController = + mInjector.getLocalDeviceIdleController(); + final long idleDuration = getVerificationTimeout(); + + /* + * If any sufficient verifiers were listed in the package + * manifest, attempt to ask them. + */ + if (sufficientVerifiers != null) { + final int n = sufficientVerifiers.size(); + if (n == 0) { + Slog.i(TAG, "Additional verifiers required, but none installed."); + ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; + } else { + for (int i = 0; i < n; i++) { + final ComponentName verifierComponent = sufficientVerifiers.get(i); + idleController.addPowerSaveTempWhitelistApp(Process.myUid(), + verifierComponent.getPackageName(), idleDuration, + verifierUser.getIdentifier(), false, "package verifier"); + + final Intent sufficientIntent = new Intent(verification); + sufficientIntent.setComponent(verifierComponent); + mContext.sendBroadcastAsUser(sufficientIntent, verifierUser); + } + } + } + + final ComponentName requiredVerifierComponent = matchComponentForVerifier( + mRequiredVerifierPackage, receivers); + if (mRequiredVerifierPackage != null) { + /* + * Send the intent to the required verification agent, + * but only start the verification timeout after the + * target BroadcastReceivers have run. + */ + verification.setComponent(requiredVerifierComponent); + idleController.addPowerSaveTempWhitelistApp(Process.myUid(), + mRequiredVerifierPackage, idleDuration, + verifierUser.getIdentifier(), false, "package verifier"); + mContext.sendOrderedBroadcastAsUser(verification, verifierUser, + android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final Message msg = mHandler + .obtainMessage(CHECK_PENDING_VERIFICATION); + msg.arg1 = verificationId; + mHandler.sendMessageDelayed(msg, getVerificationTimeout()); + } + }, null, 0, null, null); + + Trace.asyncTraceBegin( + TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); + + /* + * We don't want the copy to proceed until verification + * succeeds. + */ + mVerificationCompleted = false; + } + } else { + verificationState.setVerifierResponse( + requiredUid, PackageManager.VERIFICATION_ALLOW); + } + return ret; + } + + void populateInstallerExtras(Intent intent) { + intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE, + installSource.initiatingPackageName); + + if (verificationInfo != null) { + if (verificationInfo.originatingUri != null) { + intent.putExtra(Intent.EXTRA_ORIGINATING_URI, + verificationInfo.originatingUri); + } + if (verificationInfo.referrer != null) { + intent.putExtra(Intent.EXTRA_REFERRER, + verificationInfo.referrer); + } + if (verificationInfo.originatingUid >= 0) { + intent.putExtra(Intent.EXTRA_ORIGINATING_UID, + verificationInfo.originatingUid); + } + if (verificationInfo.installerUid >= 0) { + intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, + verificationInfo.installerUid); + } + } + } + void setReturnCode(int ret) { if (mRet == PackageManager.INSTALL_SUCCEEDED) { // Only update mRet if it was previously INSTALL_SUCCEEDED to @@ -14325,10 +14530,28 @@ public class PackageManagerService extends IPackageManager.Stub } void handleVerificationFinished() { - mVerificationCompleted = true; - handleReturnCode(); + if (!mVerificationCompleted) { + mVerificationCompleted = true; + if (mIntegrityVerificationCompleted || mRet != INSTALL_SUCCEEDED) { + mIntegrityVerificationCompleted = true; + handleReturnCode(); + } + // integrity verification still pending. + } } + void handleIntegrityVerificationFinished() { + if (!mIntegrityVerificationCompleted) { + mIntegrityVerificationCompleted = true; + if (mVerificationCompleted || mRet != INSTALL_SUCCEEDED) { + mVerificationCompleted = true; + handleReturnCode(); + } + // verifier still pending + } + } + + void handleRollbackEnabled() { // TODO(ruhler) b/112431924: Consider halting the install if we // couldn't enable rollback. @@ -14338,7 +14561,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override void handleReturnCode() { - if (mVerificationCompleted && mEnableRollbackCompleted) { + if (mVerificationCompleted + && mIntegrityVerificationCompleted && mEnableRollbackCompleted) { if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) { String packageName = ""; try { @@ -22673,7 +22897,6 @@ public class PackageManagerService extends IPackageManager.Stub if (ps != null) { return ps.getCeDataInode(userId); } - Slog.e(TAG, "failed to find package " + packageName); return 0; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index dfffbd6cabef..10e2780863d8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -34,6 +34,7 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; +import android.content.pm.DataLoaderParams; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageInstaller; @@ -136,7 +137,9 @@ class PackageManagerShellCommand extends ShellCommand { private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; private static final int DEFAULT_WAIT_MS = 60 * 1000; - private static final String PM_SHELL_DATALOADER = "com.android.pm.dataloader"; + private static final String DATA_LOADER_PACKAGE = "android"; + private static final String DATA_LOADER_CLASS = + "com.android.server.pm.PackageManagerShellCommandDataLoader"; final IPackageManager mInterface; final IPermissionManager mPermissionManager; @@ -1159,8 +1162,10 @@ class PackageManagerShellCommand extends ShellCommand { private int runStreamingInstall() throws RemoteException { final InstallParams params = makeInstallParams(); - if (TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName)) { - params.sessionParams.setDataLoaderPackageName(PM_SHELL_DATALOADER); + if (params.sessionParams.dataLoaderParams == null) { + final DataLoaderParams dataLoaderParams = DataLoaderParams.forStreaming( + new ComponentName(DATA_LOADER_PACKAGE, DATA_LOADER_CLASS), ""); + params.sessionParams.setDataLoaderParams(dataLoaderParams); } return doRunInstall(params); } @@ -1171,7 +1176,7 @@ class PackageManagerShellCommand extends ShellCommand { private int doRunInstall(final InstallParams params) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); - final boolean streaming = !TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName); + final boolean streaming = params.sessionParams.dataLoaderParams != null; ArrayList<String> inPaths = getRemainingArgs(); if (inPaths.isEmpty()) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java new file mode 100644 index 000000000000..1ee9ab8927bb --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java @@ -0,0 +1,92 @@ +/* + * 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.pm; + +import android.annotation.NonNull; +import android.content.pm.DataLoaderParams; +import android.content.pm.InstallationFile; +import android.os.ParcelFileDescriptor; +import android.service.dataloader.DataLoaderService; +import android.text.TextUtils; +import android.util.Slog; + +import libcore.io.IoUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; + +/** + * Callback data loader for PackageManagerShellCommand installations. + */ +public class PackageManagerShellCommandDataLoader extends DataLoaderService { + public static final String TAG = "PackageManagerShellCommandDataLoader"; + + static class DataLoader implements DataLoaderService.DataLoader { + private ParcelFileDescriptor mInFd = null; + private FileSystemConnector mConnector = null; + + private static final String STDIN_PATH = "-"; + + @Override + public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams, + @NonNull FileSystemConnector connector) { + mConnector = connector; + return true; + } + @Override + public boolean onPrepareImage(Collection<InstallationFile> addedFiles, + Collection<String> removedFiles) { + try { + for (InstallationFile fileInfo : addedFiles) { + String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8); + if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { + // TODO(b/146080380): add support for STDIN installations. + if (mInFd == null) { + Slog.e(TAG, "Invalid stdin file descriptor."); + return false; + } + ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( + mInFd.getFileDescriptor()); + mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd); + } else { + File localFile = new File(filePath); + ParcelFileDescriptor incomingFd = null; + try { + // TODO(b/146080380): open files via callback into shell command. + incomingFd = ParcelFileDescriptor.open(localFile, + ParcelFileDescriptor.MODE_READ_ONLY); + mConnector.writeData(fileInfo.getName(), 0, localFile.length(), + incomingFd); + } finally { + IoUtils.closeQuietly(incomingFd); + } + } + } + return true; + } catch (IOException e) { + return false; + } + } + } + + @Override + public DataLoaderService.DataLoader onCreateDataLoader() { + return new DataLoader(); + } +} diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java index c50bf59f0206..ea7af9031bd0 100644 --- a/services/core/java/com/android/server/pm/PackageVerificationState.java +++ b/services/core/java/com/android/server/pm/PackageVerificationState.java @@ -22,18 +22,17 @@ import android.util.SparseBooleanArray; import com.android.server.pm.PackageManagerService.InstallParams; /** - * Tracks the package verification state for a particular package. Each package - * verification has a required verifier and zero or more sufficient verifiers. - * Only one of the sufficient verifier list must return affirmative to allow the - * package to be considered verified. If there are zero sufficient verifiers, - * then package verification is considered complete. + * Tracks the package verification state for a particular package. Each package verification has a + * required verifier and zero or more sufficient verifiers. Only one of the sufficient verifier list + * must return affirmative to allow the package to be considered verified. If there are zero + * sufficient verifiers, then package verification is considered complete. */ class PackageVerificationState { private final InstallParams mParams; private final SparseBooleanArray mSufficientVerifierUids; - private final int mRequiredVerifierUid; + private int mRequiredVerifierUid; private boolean mSufficientVerificationComplete; @@ -45,16 +44,13 @@ class PackageVerificationState { private boolean mExtendedTimeout; + private boolean mIntegrityVerificationComplete; + /** - * Create a new package verification state where {@code requiredVerifierUid} - * is the user ID for the package that must reply affirmative before things - * can continue. - * - * @param requiredVerifierUid user ID of required package verifier - * @param args + * Create a new package verification state where {@code requiredVerifierUid} is the user ID for + * the package that must reply affirmative before things can continue. */ - PackageVerificationState(int requiredVerifierUid, InstallParams params) { - mRequiredVerifierUid = requiredVerifierUid; + PackageVerificationState(InstallParams params) { mParams = params; mSufficientVerifierUids = new SparseBooleanArray(); mExtendedTimeout = false; @@ -64,6 +60,11 @@ class PackageVerificationState { return mParams; } + /** Sets the user ID of the required package verifier. */ + void setRequiredVerifierUid(int uid) { + mRequiredVerifierUid = uid; + } + /** * Add a verifier which is added to our sufficient list. * @@ -74,8 +75,8 @@ class PackageVerificationState { } /** - * Should be called when a verification is received from an agent so the - * state of the package verification can be tracked. + * Should be called when a verification is received from an agent so the state of the package + * verification can be tracked. * * @param uid user ID of the verifying agent * @return {@code true} if the verifying agent actually exists in our list @@ -114,9 +115,8 @@ class PackageVerificationState { } /** - * Returns whether verification is considered complete. This means that the - * required verifier and at least one of the sufficient verifiers has - * returned a positive verification. + * Returns whether verification is considered complete. This means that the required verifier + * and at least one of the sufficient verifiers has returned a positive verification. * * @return {@code true} when verification is considered complete */ @@ -133,8 +133,8 @@ class PackageVerificationState { } /** - * Returns whether installation should be allowed. This should only be - * called after {@link #isVerificationComplete()} returns {@code true}. + * Returns whether installation should be allowed. This should only be called after {@link + * #isVerificationComplete()} returns {@code true}. * * @return {@code true} if installation should be allowed */ @@ -150,9 +150,7 @@ class PackageVerificationState { return true; } - /** - * Extend the timeout for this Package to be verified. - */ + /** Extend the timeout for this Package to be verified. */ void extendTimeout() { if (!mExtendedTimeout) { mExtendedTimeout = true; @@ -167,4 +165,16 @@ class PackageVerificationState { boolean timeoutExtended() { return mExtendedTimeout; } + + void setIntegrityVerificationResult(int code) { + mIntegrityVerificationComplete = true; + } + + boolean isIntegrityVerificationComplete() { + return mIntegrityVerificationComplete; + } + + boolean areAllVerificationsComplete() { + return mIntegrityVerificationComplete && isVerificationComplete(); + } } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 688c34fed44f..6c3eb31d22af 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.ApexInfo; 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 @@ public class StagingManager { } } + 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 @@ public class StagingManager { } 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 @@ public class StagingManager { } } + // 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 603b01a17e14..6d6ec250e4cc 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 @@ public final class DefaultPermissionGrantPolicy { 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 @@ public final class DefaultPermissionGrantPolicy { 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/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java new file mode 100644 index 000000000000..0bbb17950856 --- /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/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index fb33f3ad977a..834e924bca59 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6060,6 +6060,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void registerRemoteAnimations(RemoteAnimationDefinition definition) { mRemoteAnimationDefinition = definition; + if (definition != null) { + definition.linkToDeath(this::unregisterRemoteAnimations); + } + } + + void unregisterRemoteAnimations() { + mRemoteAnimationDefinition = null; } RemoteAnimationDefinition getRemoteAnimationDefinition() { @@ -6301,6 +6308,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A resolveSizeCompatModeConfiguration(newParentConfiguration); } else { super.resolveOverrideConfiguration(newParentConfiguration); + // We ignore activities' requested orientation in multi-window modes. Task level may + // take them into consideration when calculating bounds. + if (getParent() != null && getParent().inMultiWindowMode()) { + resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED; + } applyAspectRatio(resolvedConfig.windowConfiguration.getBounds(), newParentConfiguration.windowConfiguration.getAppBounds(), newParentConfiguration.windowConfiguration.getBounds()); diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index be3a61383de1..13554244fec2 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -252,7 +252,8 @@ class ActivityStartInterceptor { final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, suspendingPackage, mUserId); mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage, - suspendingPackage, dialogInfo, mUserId); + suspendingPackage, dialogInfo, deferCrossProfileAppsAnimationIfNecessary(), + mUserId); mCallingPid = mRealCallingPid; mCallingUid = mRealCallingUid; mResolvedType = null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 45b4818add11..60f051c37377 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4700,6 +4700,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public void unregisterRemoteAnimations(IBinder token) { + mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "unregisterRemoteAnimations"); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + final long origId = Binder.clearCallingIdentity(); + try { + r.unregisterRemoteAnimations(); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override public void registerRemoteAnimationForNextActivityStart(String packageName, RemoteAnimationAdapter adapter) { mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 7dd9790403fc..f778e4d7be0b 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 @@ class RootActivityContainer extends ConfigurationContainer * @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 @@ class RootActivityContainer extends ConfigurationContainer // 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer } // 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 @@ class RootActivityContainer extends ConfigurationContainer // 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer } } - 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer /** @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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer /** 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer // 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer } // 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 @@ class RootActivityContainer extends ConfigurationContainer * @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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer * 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer } 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 @@ class RootActivityContainer extends ConfigurationContainer // 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer */ 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 @@ class RootActivityContainer extends ConfigurationContainer 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 @@ class RootActivityContainer extends ConfigurationContainer @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/Task.java b/services/core/java/com/android/server/wm/Task.java index fd95ac500370..45c012e6cd86 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1881,7 +1881,10 @@ class Task extends WindowContainer<WindowContainer> { if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - if (insideParentBounds && mStack != null) { + if (insideParentBounds && WindowConfiguration.isFloating(windowingMode)) { + mTmpNonDecorBounds.set(mTmpFullBounds); + mTmpStableBounds.set(mTmpFullBounds); + } else if (insideParentBounds && mStack != null) { final DisplayInfo di = new DisplayInfo(); mStack.getDisplay().mDisplay.getDisplayInfo(di); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 3182a7243e7f..e4744dba85d3 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -179,7 +179,7 @@ class TaskSnapshotController { } void snapshotTasks(ArraySet<Task> tasks) { - snapshotTasks(mTmpTasks, false /* allowSnapshotHome */); + snapshotTasks(tasks, false /* allowSnapshotHome */); } private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 1313eebb446b..3039d69212f8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2368,43 +2368,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final Region region = inputWindowHandle.touchableRegion; setTouchableRegionCropIfNeeded(inputWindowHandle); - if (modal && mActivityRecord != null) { - // Limit the outer touch to the activity stack region. + if (modal) { flags |= FLAG_NOT_TOUCH_MODAL; - // If the inner bounds of letterbox is available, then it will be used as the touchable - // region so it won't cover the touchable letterbox and the touch events can slip to - // activity from letterbox. - mActivityRecord.getLetterboxInnerBounds(mTmpRect); - if (mTmpRect.isEmpty()) { - // If this is a modal window we need to dismiss it if it's not full screen and the - // touch happens outside of the frame that displays the content. This means we need - // to intercept touches outside of that window. The dim layer user associated with - // the window (task or stack) will give us the good bounds, as they would be used to - // display the dim layer. - final Task task = getTask(); - if (task != null) { - task.getDimBounds(mTmpRect); - } else { - getStack().getDimBounds(mTmpRect); - } - } - if (inFreeformWindowingMode()) { - // For freeform windows we the touch region to include the whole surface for the - // shadows. - final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics(); - final int delta = WindowManagerService.dipToPixel( - RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics); - mTmpRect.inset(-delta, -delta); - } - region.set(mTmpRect); - cropRegionToStackBoundsIfNeeded(region); - subtractTouchExcludeRegionIfNeeded(region); - } else if (modal && mTapExcludeRegionHolder != null) { - final Region touchExcludeRegion = Region.obtain(); - amendTapExcludeRegion(touchExcludeRegion); - if (!touchExcludeRegion.isEmpty()) { - // Remove touch modal because there are some areas that cannot be touched. - flags |= FLAG_NOT_TOUCH_MODAL; + if (mActivityRecord != null) { + // Limit the outer touch to the activity stack region. + updateRegionForModalActivityWindow(region); + } else { // Give it a large touchable region at first because it was touch modal. The window // might be moved on the display, so the touchable region should be large enough to // ensure it covers the whole display, no matter where it is moved. @@ -2412,15 +2381,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int dw = mTmpRect.width(); final int dh = mTmpRect.height(); region.set(-dw, -dh, dw + dw, dh + dh); - // Subtract the area that cannot be touched. - region.op(touchExcludeRegion, Region.Op.DIFFERENCE); - inputWindowHandle.setTouchableRegionCrop(null); } - touchExcludeRegion.recycle(); + subtractTouchExcludeRegionIfNeeded(region); } else { - // Not modal or full screen modal + // Not modal getTouchableRegion(region); } + // Translate to surface based coordinates. region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top); @@ -2436,6 +2403,41 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return flags; } + /** + * Updates the region for a window in an Activity that was a touch modal. This will limit + * the outer touch to the activity stack region. + * @param outRegion The region to update. + */ + private void updateRegionForModalActivityWindow(Region outRegion) { + // If the inner bounds of letterbox is available, then it will be used as the + // touchable region so it won't cover the touchable letterbox and the touch + // events can slip to activity from letterbox. + mActivityRecord.getLetterboxInnerBounds(mTmpRect); + if (mTmpRect.isEmpty()) { + // If this is a modal window we need to dismiss it if it's not full screen + // and the touch happens outside of the frame that displays the content. This + // means we need to intercept touches outside of that window. The dim layer + // user associated with the window (task or stack) will give us the good + // bounds, as they would be used to display the dim layer. + final Task task = getTask(); + if (task != null) { + task.getDimBounds(mTmpRect); + } else { + getStack().getDimBounds(mTmpRect); + } + } + if (inFreeformWindowingMode()) { + // For freeform windows, we need the touch region to include the whole + // surface for the shadows. + final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics(); + final int delta = WindowManagerService.dipToPixel( + RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics); + mTmpRect.inset(-delta, -delta); + } + outRegion.set(mTmpRect); + cropRegionToStackBoundsIfNeeded(outRegion); + } + void checkPolicyVisibilityChange() { if (isLegacyPolicyVisibility() != mLegacyPolicyVisibilityAfterAnim) { if (DEBUG_VISIBILITY) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index eda69a99a3a3..153681677d78 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.Settings.Global.PRIVATE_DNS_SPECIFIER; 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index c43328fcdf9d..afce260ed41e 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -351,10 +351,13 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint, { metadata::Mount m; m.mutable_storage()->set_id(ifs->mountId); + m.mutable_loader()->set_type((int)dataLoaderParams.type); m.mutable_loader()->set_package_name(dataLoaderParams.packageName); - m.mutable_loader()->set_arguments(dataLoaderParams.staticArgs); + m.mutable_loader()->set_class_name(dataLoaderParams.className); + m.mutable_loader()->set_arguments(dataLoaderParams.arguments); const auto metadata = m.SerializeAsString(); m.mutable_loader()->release_arguments(); + m.mutable_loader()->release_class_name(); m.mutable_loader()->release_package_name(); if (auto err = mIncFs->makeFile(ifs->control, constants().infoMdName, INCFS_ROOT_INODE, 0, metadata); @@ -794,7 +797,7 @@ bool IncrementalService::startLoading(StorageId storage) const { } bool started = false; std::unique_lock l(ifs->lock); - if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_READY) { + if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) { if (ifs->dataLoaderReady.wait_for(l, Seconds(5)) == std::cv_status::timeout) { LOG(ERROR) << "Timeout waiting for data loader to be ready"; return false; @@ -917,8 +920,10 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v } DataLoaderParamsParcel dlParams; + dlParams.type = (DataLoaderType)m.loader().type(); dlParams.packageName = std::move(*m.mutable_loader()->mutable_package_name()); - dlParams.staticArgs = std::move(*m.mutable_loader()->mutable_arguments()); + dlParams.className = std::move(*m.mutable_loader()->mutable_class_name()); + dlParams.arguments = std::move(*m.mutable_loader()->mutable_arguments()); if (!prepareDataLoader(*ifs, &dlParams)) { deleteStorage(*ifs); return false; @@ -955,7 +960,7 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, } std::unique_lock l(ifs.lock); - if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_READY) { + if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_CREATED) { LOG(INFO) << "Skipped data loader preparation because it already exists"; return true; } @@ -1008,20 +1013,20 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange } break; } - case IDataLoaderStatusListener::DATA_LOADER_READY: { + case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: { + ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STARTED; + break; + } + case IDataLoaderStatusListener::DATA_LOADER_CREATED: { ifs->dataLoaderReady.notify_one(); break; } - case IDataLoaderStatusListener::DATA_LOADER_NOT_READY: { + case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: { ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED; incrementalService.deleteStorageLocked(*ifs, std::move(l)); break; } - case IDataLoaderStatusListener::DATA_LOADER_RUNNING: { - break; - } - case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: { - ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_RUNNING; + case IDataLoaderStatusListener::DATA_LOADER_STARTED: { break; } case IDataLoaderStatusListener::DATA_LOADER_STOPPED: { diff --git a/services/incremental/Metadata.proto b/services/incremental/Metadata.proto index 0ff3c3234ffa..79f1bf8d29a9 100644 --- a/services/incremental/Metadata.proto +++ b/services/incremental/Metadata.proto @@ -10,7 +10,9 @@ message BindPoint { message DataLoader { string package_name = 1; + string class_name = 3; string arguments = 2; + int32 type = 4; } message Storage { diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index f6b123d68212..ca1e1a972047 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -138,10 +138,10 @@ public: .WillByDefault(Invoke(this, &MockIncrementalManager::startDataLoaderOk)); } void setDataLoaderStatusNotReady() { - mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_NOT_READY); + mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_DESTROYED); } void setDataLoaderStatusReady() { - mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_READY); + mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_CREATED); } private: @@ -235,7 +235,7 @@ public: MockServiceManager serviceManager = MockServiceManager(mVold, mIncrementalManager, mIncFs); mIncrementalService = std::make_unique<IncrementalService>(serviceManager, mRootDir.path); mDataLoaderParcel.packageName = "com.test"; - mDataLoaderParcel.staticArgs = "uri"; + mDataLoaderParcel.arguments = "uri"; mIncrementalService->onSystemReady(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 50ae3761b255..cfe131899987 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.recoverysystem.RecoverySystemService; 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 @@ public final class SystemServer { 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/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 05b655af65ed..9e1ddb1f8a80 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -217,9 +217,8 @@ public class LocalDisplayAdapterTest { doReturn(new int[]{ 0 }).when(() -> SurfaceControl.getDisplayColorModes(config.displayToken)); - doReturn(new int[]{ - 0 - }).when(() -> SurfaceControl.getAllowedDisplayConfigs(config.displayToken)); + doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f)) + .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(config.displayToken)); } private void updateAvailableDisplays() { diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk Binary files differnew file mode 100644 index 000000000000..6345c986efc2 --- /dev/null +++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 269f9180d8a6..ebca240819e8 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -78,10 +78,9 @@ public class DisplayModeDirectorTest { int displayId = 0; // With no votes present, DisplayModeDirector should allow any refresh rate. - assertEquals(new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/60, - new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY), - intRange(60, 90)), - createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayConfigSpecs( + assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*defaultModeId=*/60, + new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY)), + createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs( displayId)); int numPriorities = @@ -105,11 +104,10 @@ public class DisplayModeDirectorTest { priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i)); director.injectVotesByDisplay(votesByDisplay); assertEquals( - new DisplayModeDirector.DesiredDisplayConfigSpecs( + new DisplayModeDirector.DesiredDisplayModeSpecs( /*defaultModeId=*/minFps + i, - new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i), - intRange(minFps + i, maxFps - i)), - director.getDesiredDisplayConfigSpecs(displayId)); + new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i)), + director.getDesiredDisplayModeSpecs(displayId)); } } @@ -128,10 +126,9 @@ public class DisplayModeDirectorTest { votes.put(DisplayModeDirector.Vote.MIN_PRIORITY, DisplayModeDirector.Vote.forRefreshRates(70, 80)); director.injectVotesByDisplay(votesByDisplay); - assertEquals( - new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/70, - new DisplayModeDirector.RefreshRateRange(70, 80), intRange(70, 80)), - director.getDesiredDisplayConfigSpecs(displayId)); + assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*defaultModeId=*/70, + new DisplayModeDirector.RefreshRateRange(70, 80)), + director.getDesiredDisplayModeSpecs(displayId)); } } } 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 37ff06a18492..a2376a6bef1c 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -16,57 +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"; + + 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"; - @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + 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() { + 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( + 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 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(); - mService = new AppIntegrityManagerServiceImpl(InstrumentationRegistry.getContext()); + 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 integrityVerification_allow() { - int verificationId = 2; - Intent integrityVerificationIntent = new Intent(); - integrityVerificationIntent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION); - integrityVerificationIntent.putExtra(EXTRA_VERIFICATION_ID, verificationId); + 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)); - // We cannot send the broadcast using the context since it is a protected broadcast and - // we will get a security exception. - mService.handleIntegrityVerification(integrityVerificationIntent); + 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(verificationId, PackageManager.VERIFICATION_ALLOW); + .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/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java index ad74901ece2c..0bb2d4489b6b 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java @@ -44,48 +44,66 @@ import java.util.Optional; @RunWith(JUnit4.class) public class RuleXmlSerializerTest { + private static final String SAMPLE_INSTALLER_NAME = "com.test.installer"; + private static final String SAMPLE_INSTALLER_CERT = "installer_cert"; + @Test - public void testXmlString_serializeEmptyRule() throws Exception { - Rule rule = null; + public void testXmlString_serializeEmptyRuleList() throws Exception { RuleSerializer xmlSerializer = new RuleXmlSerializer(); String expectedRules = "<RL />"; byte[] actualRules = xmlSerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); + Collections.emptyList(), /* formatVersion= */ Optional.empty()); assertEquals(expectedRules, new String(actualRules, StandardCharsets.UTF_8)); } @Test - public void testXmlString_serializeMultipleRules_oneEmpty() throws Exception { - Rule rule1 = null; - Rule rule2 = + public void testXmlString_serializeMultipleRules_indexingOrderPreserved() throws Exception { + String packageNameA = "aaa"; + String packageNameB = "bbb"; + String packageNameC = "ccc"; + String appCert1 = "cert1"; + String appCert2 = "cert2"; + String appCert3 = "cert3"; + Rule installerRule = new Rule( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - "com.app.test", - /* isHashedValue= */ false), + new CompoundFormula( + CompoundFormula.AND, + Arrays.asList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.INSTALLER_NAME, + SAMPLE_INSTALLER_NAME, + /* isHashedValue= */ false), + new AtomicFormula.StringAtomicFormula( + AtomicFormula.INSTALLER_CERTIFICATE, + SAMPLE_INSTALLER_CERT, + /* isHashedValue= */ false))), Rule.DENY); - RuleSerializer xmlSerializer = new RuleXmlSerializer(); - Map<String, String> packageNameAttrs = new LinkedHashMap<>(); - packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); - packageNameAttrs.put("V", "com.app.test"); - packageNameAttrs.put("H", "false"); - String expectedRules = - "<RL>" - + generateTagWithAttribute( - /* tag= */ "R", - Collections.singletonMap("E", String.valueOf(Rule.DENY)), - /* closed= */ false) - + generateTagWithAttribute( - /* tag= */ "AF", packageNameAttrs, /* closed= */ true) - + "</R>" - + "</RL>"; + RuleSerializer xmlSerializer = new RuleXmlSerializer(); byte[] actualRules = xmlSerializer.serialize( - Arrays.asList(rule1, rule2), /* formatVersion= */ Optional.empty()); + Arrays.asList( + installerRule, + getRuleWithAppCertificateAndSampleInstallerName(appCert1), + getRuleWithPackageNameAndSampleInstallerName(packageNameB), + getRuleWithAppCertificateAndSampleInstallerName(appCert3), + getRuleWithPackageNameAndSampleInstallerName(packageNameC), + getRuleWithAppCertificateAndSampleInstallerName(appCert2), + getRuleWithPackageNameAndSampleInstallerName(packageNameA)), + /* formatVersion= */ Optional.empty()); + + String expectedRules = "<RL>" + + getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(packageNameA) + + getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(packageNameB) + + getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(packageNameC) + + getSerializedCompoundRuleWithAppCertificateAndSampleInstallerName(appCert1) + + getSerializedCompoundRuleWithAppCertificateAndSampleInstallerName(appCert2) + + getSerializedCompoundRuleWithAppCertificateAndSampleInstallerName(appCert3) + + getSerializedCompoundRuleWithSampleInstallerNameAndCert() + + "</RL>"; assertEquals(expectedRules, new String(actualRules, StandardCharsets.UTF_8)); } @@ -371,7 +389,7 @@ public class RuleXmlSerializerTest { assertExpectException( RuleSerializeException.class, - /* expectedExceptionMessageRegex */ "Invalid formula type", + /* expectedExceptionMessageRegex */ "Malformed rule identified.", () -> xmlSerializer.serialize( Collections.singletonList(rule), @@ -393,6 +411,124 @@ public class RuleXmlSerializerTest { return res.toString(); } + private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) { + return new Rule( + new CompoundFormula( + CompoundFormula.AND, + Arrays.asList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + packageName, + /* isHashedValue= */ false), + new AtomicFormula.StringAtomicFormula( + AtomicFormula.INSTALLER_NAME, + SAMPLE_INSTALLER_NAME, + /* isHashedValue= */ false))), + Rule.DENY); + } + + private String getSerializedCompoundRuleWithPackageNameAndSampleInstallerName( + String packageName) { + + Map<String, String> packageNameAttrs = new LinkedHashMap<>(); + packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME)); + packageNameAttrs.put("V", packageName); + packageNameAttrs.put("H", "false"); + + Map<String, String> installerNameAttrs = new LinkedHashMap<>(); + installerNameAttrs.put("K", String.valueOf(AtomicFormula.INSTALLER_NAME)); + installerNameAttrs.put("V", SAMPLE_INSTALLER_NAME); + installerNameAttrs.put("H", "false"); + + return generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + generateTagWithAttribute( + /* tag= */ "AF", installerNameAttrs, /* closed= */ true) + + "</OF>" + + "</R>"; + } + + + private Rule getRuleWithAppCertificateAndSampleInstallerName(String certificate) { + return new Rule( + new CompoundFormula( + CompoundFormula.AND, + Arrays.asList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.APP_CERTIFICATE, + certificate, + /* isHashedValue= */ false), + new AtomicFormula.StringAtomicFormula( + AtomicFormula.INSTALLER_NAME, + SAMPLE_INSTALLER_NAME, + /* isHashedValue= */ false))), + Rule.DENY); + } + + private String getSerializedCompoundRuleWithAppCertificateAndSampleInstallerName( + String appCert) { + + Map<String, String> packageNameAttrs = new LinkedHashMap<>(); + packageNameAttrs.put("K", String.valueOf(AtomicFormula.APP_CERTIFICATE)); + packageNameAttrs.put("V", appCert); + packageNameAttrs.put("H", "false"); + + Map<String, String> installerNameAttrs = new LinkedHashMap<>(); + installerNameAttrs.put("K", String.valueOf(AtomicFormula.INSTALLER_NAME)); + installerNameAttrs.put("V", SAMPLE_INSTALLER_NAME); + installerNameAttrs.put("H", "false"); + + return generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", packageNameAttrs, /* closed= */ true) + + generateTagWithAttribute( + /* tag= */ "AF", installerNameAttrs, /* closed= */ true) + + "</OF>" + + "</R>"; + } + + private String getSerializedCompoundRuleWithSampleInstallerNameAndCert() { + Map<String, String> installerNameAttrs = new LinkedHashMap<>(); + installerNameAttrs.put("K", String.valueOf(AtomicFormula.INSTALLER_NAME)); + installerNameAttrs.put("V", SAMPLE_INSTALLER_NAME); + installerNameAttrs.put("H", "false"); + + Map<String, String> installerCertAttrs = new LinkedHashMap<>(); + installerCertAttrs.put("K", String.valueOf(AtomicFormula.INSTALLER_CERTIFICATE)); + installerCertAttrs.put("V", SAMPLE_INSTALLER_CERT); + installerCertAttrs.put("H", "false"); + + return generateTagWithAttribute( + /* tag= */ "R", + Collections.singletonMap("E", String.valueOf(Rule.DENY)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "OF", + Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)), + /* closed= */ false) + + generateTagWithAttribute( + /* tag= */ "AF", installerNameAttrs, /* closed= */ true) + + generateTagWithAttribute( + /* tag= */ "AF", installerCertAttrs, /* closed= */ true) + + "</OF>" + + "</R>"; + } + private Formula getInvalidFormula() { return new Formula() { @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java index ebd363386fa3..1fff4f084fc4 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java @@ -17,8 +17,7 @@ package com.android.server.pm; import android.content.pm.PackageManager; -import com.android.server.pm.PackageVerificationState; - +import android.content.pm.PackageManagerInternal; import android.test.AndroidTestCase; public class PackageVerificationStateTest extends AndroidTestCase { @@ -29,7 +28,8 @@ public class PackageVerificationStateTest extends AndroidTestCase { private static final int SUFFICIENT_UID_2 = 8938; public void testPackageVerificationState_OnlyRequiredVerifier_AllowedInstall() { - PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null); + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); assertFalse("Verification should not be marked as complete yet", state.isVerificationComplete()); @@ -44,7 +44,8 @@ public class PackageVerificationStateTest extends AndroidTestCase { } public void testPackageVerificationState_OnlyRequiredVerifier_DeniedInstall() { - PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null); + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); assertFalse("Verification should not be marked as complete yet", state.isVerificationComplete()); @@ -59,7 +60,8 @@ public class PackageVerificationStateTest extends AndroidTestCase { } public void testPackageVerificationState_RequiredAndOneSufficient_RequiredDeniedInstall() { - PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null); + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); assertFalse("Verification should not be marked as complete yet", state.isVerificationComplete()); @@ -84,7 +86,8 @@ public class PackageVerificationStateTest extends AndroidTestCase { } public void testPackageVerificationState_RequiredAndOneSufficient_SufficientDeniedInstall() { - PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null); + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); assertFalse("Verification should not be marked as complete yet", state.isVerificationComplete()); @@ -109,7 +112,8 @@ public class PackageVerificationStateTest extends AndroidTestCase { } public void testPackageVerificationState_RequiredAndTwoSufficient_OneSufficientIsEnough() { - PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null); + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); assertFalse("Verification should not be marked as complete yet", state.isVerificationComplete()); @@ -135,7 +139,8 @@ public class PackageVerificationStateTest extends AndroidTestCase { } public void testPackageVerificationState_RequiredAndTwoSufficient_SecondSufficientIsEnough() { - PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null); + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); assertFalse("Verification should not be marked as complete yet", state.isVerificationComplete()); @@ -166,7 +171,8 @@ public class PackageVerificationStateTest extends AndroidTestCase { } public void testPackageVerificationState_RequiredAndTwoSufficient_RequiredOverrides() { - PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null); + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); assertFalse("Verification should not be marked as complete yet", state.isVerificationComplete()); @@ -202,4 +208,55 @@ public class PackageVerificationStateTest extends AndroidTestCase { assertTrue("Installation should be marked as allowed still", state.isInstallAllowed()); } + + public void testAreAllVerificationsComplete_onlyVerificationPasses() { + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); + assertFalse(state.areAllVerificationsComplete()); + + state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW); + + assertFalse(state.areAllVerificationsComplete()); + } + + public void testAreAllVerificationsComplete_onlyIntegrityCheckPasses() { + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); + assertFalse(state.areAllVerificationsComplete()); + + state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); + + assertFalse(state.areAllVerificationsComplete()); + } + + public void testAreAllVerificationsComplete_bothPasses() { + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); + assertFalse(state.areAllVerificationsComplete()); + + state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); + state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW); + + assertTrue(state.areAllVerificationsComplete()); + } + + public void testAreAllVerificationsComplete_onlyVerificationFails() { + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); + assertFalse(state.areAllVerificationsComplete()); + + state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT); + + assertFalse(state.areAllVerificationsComplete()); + } + + public void testAreAllVerificationsComplete_onlyIntegrityCheckFails() { + PackageVerificationState state = new PackageVerificationState(null); + state.setRequiredVerifierUid(REQUIRED_UID); + assertFalse(state.areAllVerificationsComplete()); + + state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT); + + assertFalse(state.areAllVerificationsComplete()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 89723d14090b..65704c891318 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.os.Process.NOBODY_UID; @@ -62,6 +63,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import android.app.ActivityOptions; +import android.app.WindowConfiguration; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.PauseActivityItem; @@ -70,6 +72,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Rect; import android.os.Bundle; import android.os.PersistableBundle; import android.platform.test.annotations.Presubmit; @@ -376,6 +379,64 @@ public class ActivityRecordTests extends ActivityTestsBase { } @Test + public void ignoreRequestedOrientationInFreeformWindows() { + mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + final Rect stableRect = new Rect(); + mStack.getDisplay().mDisplayContent.getStableRect(stableRect); + final boolean isScreenPortrait = stableRect.width() <= stableRect.height(); + final Rect bounds = new Rect(stableRect); + if (isScreenPortrait) { + // Landscape bounds + final int newHeight = stableRect.width() - 10; + bounds.top = stableRect.top + (stableRect.height() - newHeight) / 2; + bounds.bottom = bounds.top + newHeight; + } else { + // Portrait bounds + final int newWidth = stableRect.height() - 10; + bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2; + bounds.right = bounds.left + newWidth; + } + mTask.setBounds(bounds); + + // Requests orientation that's different from its bounds. + mActivity.setRequestedOrientation( + isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE); + + // Asserts it has orientation derived from bounds. + assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT, + mActivity.getConfiguration().orientation); + } + + @Test + public void ignoreRequestedOrientationInSplitWindows() { + mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + final Rect stableRect = new Rect(); + mStack.getDisplay().mDisplayContent.getStableRect(stableRect); + final boolean isScreenPortrait = stableRect.width() <= stableRect.height(); + final Rect bounds = new Rect(stableRect); + if (isScreenPortrait) { + // Landscape bounds + final int newHeight = stableRect.width() - 10; + bounds.top = stableRect.top + (stableRect.height() - newHeight) / 2; + bounds.bottom = bounds.top + newHeight; + } else { + // Portrait bounds + final int newWidth = stableRect.height() - 10; + bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2; + bounds.right = bounds.left + newWidth; + } + mTask.setBounds(bounds); + + // Requests orientation that's different from its bounds. + mActivity.setRequestedOrientation( + isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE); + + // Asserts it has orientation derived from bounds. + assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT, + mActivity.getConfiguration().orientation); + } + + @Test public void testShouldMakeActive_deferredResume() { mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 1a18df5b6fcf..de6d75235777 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -427,6 +427,61 @@ public class TaskRecordTests extends ActivityTestsBase { assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp); } + @Test + public void testInsetDisregardedWhenFreeformOverlapsNavBar() { + DisplayContent display = mService.mRootActivityContainer.getDefaultDisplay(); + ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + DisplayInfo displayInfo = new DisplayInfo(); + mService.mContext.getDisplay().getDisplayInfo(displayInfo); + final int displayHeight = displayInfo.logicalHeight; + final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + final Configuration inOutConfig = new Configuration(); + final Configuration parentConfig = new Configuration(); + final int longSide = 1200; + final int shortSide = 600; + parentConfig.densityDpi = 400; + parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px + parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px + parentConfig.windowConfiguration.setRotation(ROTATION_0); + + final float density = 2.5f; // densityDpi / DENSITY_DEFAULT_SCALE = 400 / 160.0f + final int longSideDp = 480; // longSide / density = 1200 / 400 * 160 + final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160 + final int screenLayout = parentConfig.screenLayout + & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK); + final int reducedScreenLayout = + Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp); + + // Portrait bounds overlapping with navigation bar, without insets. + inOutConfig.windowConfiguration.getBounds().set(0, + displayHeight - 10 - longSide, + shortSide, + displayHeight - 10); + // Set to freeform mode to verify bug fix. + inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); + assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); + assertEquals(reducedScreenLayout, inOutConfig.screenLayout); + + inOutConfig.setToDefaults(); + // Landscape bounds overlapping with navigtion bar, without insets. + inOutConfig.windowConfiguration.getBounds().set(0, + displayHeight - 10 - shortSide, + longSide, + displayHeight - 10); + inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); + assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); + assertEquals(reducedScreenLayout, inOutConfig.screenLayout); + } + /** Ensures that the alias intent won't have target component resolved. */ @Test public void testTaskIntentActivityAlias() { diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index 3940a3b0a1cf..9b9997f9b737 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -485,6 +485,86 @@ public class Annotation { PreciseCallState.PRECISE_CALL_STATE_DISCONNECTING}) public @interface PreciseCallStates {} + @IntDef(value = { + DisconnectCause.NOT_VALID, + DisconnectCause.NOT_DISCONNECTED, + DisconnectCause.INCOMING_MISSED, + DisconnectCause.NORMAL, + DisconnectCause.LOCAL, + DisconnectCause.BUSY, + DisconnectCause.CONGESTION, + DisconnectCause.MMI, + DisconnectCause.INVALID_NUMBER, + DisconnectCause.NUMBER_UNREACHABLE, + DisconnectCause.SERVER_UNREACHABLE, + DisconnectCause.INVALID_CREDENTIALS, + DisconnectCause.OUT_OF_NETWORK, + DisconnectCause.SERVER_ERROR, + DisconnectCause.TIMED_OUT, + DisconnectCause.LOST_SIGNAL, + DisconnectCause.LIMIT_EXCEEDED, + DisconnectCause.INCOMING_REJECTED, + DisconnectCause.POWER_OFF, + DisconnectCause.OUT_OF_SERVICE, + DisconnectCause.ICC_ERROR, + DisconnectCause.CALL_BARRED, + DisconnectCause.FDN_BLOCKED, + DisconnectCause.CS_RESTRICTED, + DisconnectCause.CS_RESTRICTED_NORMAL, + DisconnectCause.CS_RESTRICTED_EMERGENCY, + DisconnectCause.UNOBTAINABLE_NUMBER, + DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE, + DisconnectCause.CDMA_DROP, + DisconnectCause.CDMA_INTERCEPT, + DisconnectCause.CDMA_REORDER, + DisconnectCause.CDMA_SO_REJECT, + DisconnectCause.CDMA_RETRY_ORDER, + DisconnectCause.CDMA_ACCESS_FAILURE, + DisconnectCause.CDMA_PREEMPTED, + DisconnectCause.CDMA_NOT_EMERGENCY, + DisconnectCause.CDMA_ACCESS_BLOCKED, + DisconnectCause.ERROR_UNSPECIFIED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DisconnectCauses { + } + + @IntDef(value = { + PreciseDisconnectCause.NOT_VALID, + PreciseDisconnectCause.NO_DISCONNECT_CAUSE_AVAILABLE, + PreciseDisconnectCause.UNOBTAINABLE_NUMBER, + PreciseDisconnectCause.NORMAL, + PreciseDisconnectCause.BUSY, + PreciseDisconnectCause.NUMBER_CHANGED, + PreciseDisconnectCause.STATUS_ENQUIRY, + PreciseDisconnectCause.NORMAL_UNSPECIFIED, + PreciseDisconnectCause.NO_CIRCUIT_AVAIL, + PreciseDisconnectCause.TEMPORARY_FAILURE, + PreciseDisconnectCause.SWITCHING_CONGESTION, + PreciseDisconnectCause.CHANNEL_NOT_AVAIL, + PreciseDisconnectCause.QOS_NOT_AVAIL, + PreciseDisconnectCause.BEARER_NOT_AVAIL, + PreciseDisconnectCause.ACM_LIMIT_EXCEEDED, + PreciseDisconnectCause.CALL_BARRED, + PreciseDisconnectCause.FDN_BLOCKED, + PreciseDisconnectCause.IMSI_UNKNOWN_IN_VLR, + PreciseDisconnectCause.IMEI_NOT_ACCEPTED, + PreciseDisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE, + PreciseDisconnectCause.CDMA_DROP, + PreciseDisconnectCause.CDMA_INTERCEPT, + PreciseDisconnectCause.CDMA_REORDER, + PreciseDisconnectCause.CDMA_SO_REJECT, + PreciseDisconnectCause.CDMA_RETRY_ORDER, + PreciseDisconnectCause.CDMA_ACCESS_FAILURE, + PreciseDisconnectCause.CDMA_PREEMPTED, + PreciseDisconnectCause.CDMA_NOT_EMERGENCY, + PreciseDisconnectCause.CDMA_ACCESS_BLOCKED, + PreciseDisconnectCause.ERROR_UNSPECIFIED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PreciseDisconnectCauses { + } + @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"RIL_RADIO_TECHNOLOGY_" }, value = { ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN, diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 813b9fa3e698..4bb237f6c27c 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4050,13 +4050,28 @@ public class CarrierConfigManager { } } - /** {@hide} */ + /** + * Gets the package name for a default carrier service. + * @return the package name for a default carrier service; empty string if not available. + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName() { try { - return getICarrierConfigLoader().getDefaultCarrierServicePackageName(); - } catch (Throwable t) { - return null; + ICarrierConfigLoader loader = getICarrierConfigLoader(); + if (loader == null) { + Rlog.w(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null"); + return ""; + } + return loader.getDefaultCarrierServicePackageName(); + } catch (RemoteException ex) { + Rlog.e(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null" + + ex.toString()); } + return ""; } /** diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java index b7dab161c331..e523fbab2bb0 100644 --- a/telephony/java/android/telephony/CellIdentity.java +++ b/telephony/java/android/telephony/CellIdentity.java @@ -19,6 +19,7 @@ package android.telephony; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -181,7 +182,8 @@ public abstract class CellIdentity implements Parcelable { * @return a CellLocation object for this CellIdentity * @hide */ - public abstract CellLocation asCellLocation(); + @SystemApi + public abstract @NonNull CellLocation asCellLocation(); @Override public boolean equals(Object other) { diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java index 880d3db681b5..54236b426d98 100644 --- a/telephony/java/android/telephony/CellIdentityCdma.java +++ b/telephony/java/android/telephony/CellIdentityCdma.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.os.Parcel; import android.telephony.cdma.CdmaCellLocation; @@ -198,6 +199,7 @@ public final class CellIdentityCdma extends CellIdentity { } /** @hide */ + @NonNull @Override public CdmaCellLocation asCellLocation() { CdmaCellLocation cl = new CdmaCellLocation(); diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 25c6577bdcf5..4e4454d6a1c2 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; @@ -200,6 +201,7 @@ public final class CellIdentityGsm extends CellIdentity { } /** @hide */ + @NonNull @Override public GsmCellLocation asCellLocation() { GsmCellLocation cl = new GsmCellLocation(); diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index 997b19f3d4eb..c3fc73b775d7 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.os.Build; @@ -232,6 +233,7 @@ public final class CellIdentityLte extends CellIdentity { * * @hide */ + @NonNull @Override public GsmCellLocation asCellLocation() { GsmCellLocation cl = new GsmCellLocation(); diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index edc838c163db..e3fec7b7f820 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -17,6 +17,7 @@ package android.telephony; import android.annotation.IntRange; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; @@ -77,6 +78,7 @@ public final class CellIdentityNr extends CellIdentity { * @return a CellLocation object for this CellIdentity. * @hide */ + @NonNull @Override public CellLocation asCellLocation() { return new GsmCellLocation(); diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 558e346284ea..8f812b6b892e 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -171,6 +171,7 @@ public final class CellIdentityTdscdma extends CellIdentity { } /** @hide */ + @NonNull @Override public GsmCellLocation asCellLocation() { GsmCellLocation cl = new GsmCellLocation(); diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 031fed13d9f1..556bc32e7c36 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; @@ -196,6 +197,7 @@ public final class CellIdentityWcdma extends CellIdentity { } /** @hide */ + @NonNull @Override public GsmCellLocation asCellLocation() { GsmCellLocation cl = new GsmCellLocation(); diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java index 9f75332c4a03..bfa6326c3c18 100644 --- a/telephony/java/android/telephony/PreciseCallState.java +++ b/telephony/java/android/telephony/PreciseCallState.java @@ -16,19 +16,18 @@ package android.telephony; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Annotation.DisconnectCauses; import android.telephony.Annotation.PreciseCallStates; +import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.DisconnectCause; import android.telephony.PreciseDisconnectCause; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -73,19 +72,26 @@ public final class PreciseCallState implements Parcelable { private @PreciseCallStates int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID; private @PreciseCallStates int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID; private @PreciseCallStates int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID; - private int mDisconnectCause = DisconnectCause.NOT_VALID; - private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID; + private @DisconnectCauses int mDisconnectCause = DisconnectCause.NOT_VALID; + private @PreciseDisconnectCauses int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID; /** - * Constructor + * Construct PreciseCallState with parameters + * + * @param ringingCall ring call state + * @param foregroundCall foreground call state + * @param backgroundCall background call state + * @param disconnectCause disconnect cause + * @param preciseDisconnectCause precise disconnect cause * * @hide */ - @UnsupportedAppUsage + @SystemApi public PreciseCallState(@PreciseCallStates int ringingCall, @PreciseCallStates int foregroundCall, - @PreciseCallStates int backgroundCall, int disconnectCause, - int preciseDisconnectCause) { + @PreciseCallStates int backgroundCall, + @DisconnectCauses int disconnectCause, + @PreciseDisconnectCauses int preciseDisconnectCause) { mRingingCallState = ringingCall; mForegroundCallState = foregroundCall; mBackgroundCallState = backgroundCall; diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index b705d71c61d9..392d3eb37686 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -344,10 +344,12 @@ public class SmsMessage { * @param use7bitOnly if true, characters that are not part of the radio-specific 7-bit encoding * are counted as single space chars. If false, and if the messageBody contains non-7-bit * encodable characters, length is calculated using a 16-bit encoding. - * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code + * @return an int[6] with int[0] being the number of SMS's required, int[1] the number of code * units used, and int[2] is the number of code units remaining until the next message. * int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in - * SmsConstants). + * SmsConstants). int[4] is the GSM national language table to use, or 0 for the default + * 7-bit alphabet. int[5] The GSM national language shift table to use, or 0 for the default + * 7-bit extension table. */ public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) { return calculateLength(msgBody, use7bitOnly, SmsManager.getDefaultSmsSubscriptionId()); @@ -362,10 +364,12 @@ public class SmsMessage { * are counted as single space chars. If false, and if the messageBody contains non-7-bit * encodable characters, length is calculated using a 16-bit encoding. * @param subId Subscription to take SMS format. - * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code + * @return an int[6] with int[0] being the number of SMS's required, int[1] the number of code * units used, and int[2] is the number of code units remaining until the next message. * int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in - * SmsConstants). + * SmsConstants). int[4] is the GSM national language table to use, or 0 for the default + * 7-bit alphabet. int[5] The GSM national language shift table to use, or 0 for the default + * 7-bit extension table. * @hide */ public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly, int subId) { @@ -376,11 +380,13 @@ public class SmsMessage { msgBody, use7bitOnly, true) : com.android.internal.telephony.gsm.SmsMessage.calculateLength( msgBody, use7bitOnly); - int ret[] = new int[4]; + int[] ret = new int[6]; ret[0] = ted.msgCount; ret[1] = ted.codeUnitCount; ret[2] = ted.codeUnitsRemaining; ret[3] = ted.codeUnitSize; + ret[4] = ted.languageTable; + ret[5] = ted.languageShiftTable; return ret; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 60fda0906948..39e57b775a8d 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1950,14 +1950,9 @@ public class TelephonyManager { return null; } - Bundle bundle = telephony.getCellLocation(mContext.getOpPackageName(), + CellIdentity cellIdentity = telephony.getCellLocation(mContext.getOpPackageName(), mContext.getFeatureId()); - if (bundle == null || bundle.isEmpty()) { - Rlog.d(TAG, "getCellLocation returning null because CellLocation is unavailable"); - return null; - } - - CellLocation cl = CellLocation.newFromBundle(bundle); + CellLocation cl = cellIdentity.asCellLocation(); if (cl == null || cl.isEmpty()) { Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or" + " phone type doesn't match CellLocation type"); @@ -4248,7 +4243,7 @@ public class TelephonyManager { * 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. * @@ -4269,25 +4264,27 @@ public class TelephonyManager { } /** - * 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/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 97b24aef4af5..b99fe904bbe8 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -30,6 +30,7 @@ import android.service.carrier.CarrierIdentifier; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.CarrierRestrictionRules; +import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.ClientRequestStats; import android.telephony.IccOpenLogicalChannelResponse; @@ -305,7 +306,8 @@ interface ITelephony { */ boolean isDataConnectivityPossible(int subId); - Bundle getCellLocation(String callingPkg, String callingFeatureId); + // Uses CellIdentity which is Parcelable here; will convert to CellLocation in client. + CellIdentity getCellLocation(String callingPkg, String callingFeatureId); /** * Returns the ISO country code equivalent of the current registered @@ -1134,7 +1136,7 @@ interface ITelephony { /** * @hide */ - String[] getMergedSubscriberIdsFromGroup(int subId, String callingPackage); + String[] getMergedImsisFromGroup(int subId, String callingPackage); /** * Override the operator branding for the current ICCID. diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml index 51bcdea21f49..55704eda905e 100644 --- a/tests/ApkVerityTest/AndroidTest.xml +++ b/tests/ApkVerityTest/AndroidTest.xml @@ -22,7 +22,7 @@ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> <!-- Disable package verifier prevents it holding the target APK's fd that prevents cache eviction. --> - <option name="set-global-setting" key="package_verifier_enable" value="0" /> + <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" /> <option name="restore-settings" value="true" /> <!-- Skip in order to prevent reboot that confuses the test flow. --> diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java index b8c82fd9e0ae..4fa93ee271e9 100644 --- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -17,32 +17,46 @@ package android.net.wifi; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.os.Handler; +import android.util.SparseArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * 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, Handler, EasyConnectStatusCallback)} or - * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, - * Handler, EasyConnectStatusCallback)} + * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Executor, + * EasyConnectStatusCallback)} or {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, + * Executor, EasyConnectStatusCallback)} * * @hide */ @SystemApi public abstract class EasyConnectStatusCallback { /** - * Easy Connect Success event: Configuration sent (Configurator mode). + * 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. */ 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. + */ + public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1; + /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_SUCCESS_"}, value = { EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT, + EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectSuccessStatusCode { @@ -58,10 +72,22 @@ public abstract class EasyConnectStatusCallback { */ public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; + /** + * Easy Connect R2 Progress event: Configuration sent to Enrollee, waiting for response + */ + 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 + */ + public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3; + /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_PROGRESS_"}, value = { EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS, EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING, + EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE, + EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectProgressStatusCode { @@ -114,6 +140,20 @@ public abstract class EasyConnectStatusCallback { */ public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; + /** + * Easy Connect R2 Failure event: Enrollee cannot find the network. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10; + + /** + * Easy Connect R2 Failure event: Enrollee failed to authenticate with the network. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11; + + /** + * Easy Connect R2 Failure event: Enrollee rejected the configuration. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = { @@ -126,6 +166,9 @@ public abstract class EasyConnectStatusCallback { EASY_CONNECT_EVENT_FAILURE_GENERIC, EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED, EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, + EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK, + EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION, + EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectFailureStatusCode { @@ -138,9 +181,8 @@ public abstract class EasyConnectStatusCallback { * current Easy Connect * session, and no further callbacks will be called. This callback is the successful outcome * of a Easy Connect flow starting with - * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, - * Handler, - * EasyConnectStatusCallback)}. + * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, Executor, + * EasyConnectStatusCallback)} . * * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator */ @@ -148,13 +190,11 @@ public abstract class EasyConnectStatusCallback { /** * Called when a Easy Connect success event takes place, except for when configuration is - * received from - * an external Configurator. The callback onSuccessConfigReceived will be used in this case. - * This callback marks the successful end of the current Easy Connect session, and no further - * callbacks will be called. This callback is the successful outcome of a Easy Connect flow - * starting with - * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Handler, - * EasyConnectStatusCallback)}. + * received from an external Configurator. The callback onSuccessConfigReceived will be used in + * this case. This callback marks the successful end of the current Easy Connect session, and no + * further callbacks will be called. This callback is the successful outcome of a Easy Connect + * flow starting with {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, + * int, Executor,EasyConnectStatusCallback)}. * * @param code Easy Connect success status code. */ @@ -162,12 +202,36 @@ public abstract class EasyConnectStatusCallback { /** * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful - * end of the - * current Easy Connect session, and no further callbacks will be called. + * end of the current Easy Connect session, and no further callbacks will be called. * * @param code Easy Connect failure status code. */ - public abstract void onFailure(@EasyConnectFailureStatusCode int code); + public void onFailure(@EasyConnectFailureStatusCode int code) {} + + /** + * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful + * end of the current Easy Connect session, and no further callbacks will be called. + * + * Note: Easy Connect (DPP) R2, provides additional details for the Configurator when the + * remote Enrollee is unable to connect to a network. The ssid, channelList and bandList + * inputs are initialized only for the EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK failure + * code, and the ssid and bandList are initialized for the + * EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION failure code. + * + * @param code Easy Connect failure status code. + * @param ssid SSID of the network the Enrollee tried to connect to. + * @param channelListArray List of Global Operating classes and channel sets the Enrollee used + * to scan to find the network, see the "DPP Connection Status Object" + * section in the specification for the format, and Table E-4 in + * IEEE Std 802.11-2016 - Global operating classes for more details. + * @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. + */ + public void onFailure(@EasyConnectFailureStatusCode int code, @Nullable String ssid, + @NonNull SparseArray<int[]> channelListArray, @NonNull int[] operatingClassArray) { + onFailure(code); + } /** * Called when Easy Connect events that indicate progress take place. Can be used by UI elements diff --git a/wifi/java/android/net/wifi/IDppCallback.aidl b/wifi/java/android/net/wifi/IDppCallback.aidl index c452c7664c12..d7a958a5b4b1 100644 --- a/wifi/java/android/net/wifi/IDppCallback.aidl +++ b/wifi/java/android/net/wifi/IDppCallback.aidl @@ -38,7 +38,7 @@ oneway interface IDppCallback /** * Called when DPP Failure events take place. */ - void onFailure(int status); + void onFailure(int status, String ssid, String channelList, in int[] bandArray); /** * Called when DPP events that indicate progress take place. Can be used by UI elements diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index b98d64dc78f1..5ab05834cd78 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -54,6 +54,7 @@ import android.text.TextUtils; import android.util.CloseGuard; import android.util.Log; import android.util.Pair; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -70,6 +71,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.StringTokenizer; import java.util.concurrent.Executor; /** @@ -5177,6 +5179,7 @@ public class WifiManager { @Override public void onSuccessConfigReceived(int newNetworkId) { Log.d(TAG, "Easy Connect onSuccessConfigReceived callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId); }); @@ -5185,22 +5188,28 @@ public class WifiManager { @Override public void onSuccess(int status) { Log.d(TAG, "Easy Connect onSuccess callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onConfiguratorSuccess(status); }); } @Override - public void onFailure(int status) { + public void onFailure(int status, String ssid, String channelList, + int[] operatingClassArray) { Log.d(TAG, "Easy Connect onFailure callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { - mEasyConnectStatusCallback.onFailure(status); + SparseArray<int[]> channelListArray = parseDppChannelList(channelList); + mEasyConnectStatusCallback.onFailure(status, ssid, channelListArray, + operatingClassArray); }); } @Override public void onProgress(int status) { Log.d(TAG, "Easy Connect onProgress callback"); + Binder.clearCallingIdentity(); mExecutor.execute(() -> { mEasyConnectStatusCallback.onProgress(status); }); @@ -5532,4 +5541,77 @@ public class WifiManager { throw e.rethrowFromSystemServer(); } } + + /** + * Parse the list of channels the DPP enrollee reports when it fails to find an AP. + * + * @param channelList List of channels in the format defined in the DPP specification. + * @return A parsed sparse array, where the operating class is the key. + * @hide + */ + @VisibleForTesting + public static SparseArray<int[]> parseDppChannelList(String channelList) { + SparseArray<int[]> channelListArray = new SparseArray<>(); + + if (TextUtils.isEmpty(channelList)) { + return channelListArray; + } + StringTokenizer str = new StringTokenizer(channelList, ","); + String classStr = null; + List<Integer> channelsInClass = new ArrayList<>(); + + try { + while (str.hasMoreElements()) { + String cur = str.nextToken(); + + /** + * Example for a channel list: + * + * 81/1,2,3,4,5,6,7,8,9,10,11,115/36,40,44,48,118/52,56,60,64,121/100,104,108,112, + * 116,120,124,128,132,136,140,0/144,124/149,153,157,161,125/165 + * + * Detect operating class by the delimiter of '/' and use a string tokenizer with + * ',' as a delimiter. + */ + int classDelim = cur.indexOf('/'); + if (classDelim != -1) { + if (classStr != null) { + // Store the last channel array in the sparse array, where the operating + // class is the key (as an integer). + int[] channelsArray = new int[channelsInClass.size()]; + for (int i = 0; i < channelsInClass.size(); i++) { + channelsArray[i] = channelsInClass.get(i); + } + channelListArray.append(Integer.parseInt(classStr), channelsArray); + channelsInClass = new ArrayList<>(); + } + + // Init a new operating class and store the first channel + classStr = cur.substring(0, classDelim); + String channelStr = cur.substring(classDelim + 1); + channelsInClass.add(Integer.parseInt(channelStr)); + } else { + if (classStr == null) { + // Invalid format + Log.e(TAG, "Cannot parse DPP channel list"); + return new SparseArray<>(); + } + channelsInClass.add(Integer.parseInt(cur)); + } + } + + // Store the last array + if (classStr != null) { + int[] channelsArray = new int[channelsInClass.size()]; + for (int i = 0; i < channelsInClass.size(); i++) { + channelsArray[i] = channelsInClass.get(i); + } + channelListArray.append(Integer.parseInt(classStr), channelsArray); + } + return channelListArray; + } catch (NumberFormatException e) { + Log.e(TAG, "Cannot parse DPP channel list"); + return new SparseArray<>(); + } + } } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 8fedda48aa0d..8badcc0c2552 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -724,21 +724,6 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java new file mode 100644 index 000000000000..b10141434b0b --- /dev/null +++ b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java @@ -0,0 +1,84 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.util.SparseArray; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.EasyConnectStatusCallbackTest}. + */ +@SmallTest +public class EasyConnectStatusCallbackTest { + private EasyConnectStatusCallback mEasyConnectStatusCallback = new EasyConnectStatusCallback() { + @Override + public void onEnrolleeSuccess(int newNetworkId) { + + } + + @Override + public void onConfiguratorSuccess(int code) { + + } + + @Override + public void onProgress(int code) { + + } + + @Override + public void onFailure(int code) { + mOnFailureR1EventReceived = true; + mLastCode = code; + } + }; + private boolean mOnFailureR1EventReceived; + private int mLastCode; + + @Before + public void setUp() { + mOnFailureR1EventReceived = false; + mLastCode = 0; + } + + /** + * Test that the legacy R1 onFailure is called by default if the R2 onFailure is not overridden + * by the app. + */ + @Test + public void testR1OnFailureCalled() { + + SparseArray<int[]> channelList = new SparseArray<>(); + int[] channelArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + + channelList.append(81, channelArray); + mEasyConnectStatusCallback.onFailure( + EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK, + "SomeSSID", channelList, new int[] {81}); + + assertTrue(mOnFailureR1EventReceived); + assertEquals(mLastCode, + EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK); + } +} diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 4a46744a2d32..821661196f4f 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -89,6 +89,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.connectivity.WifiActivityEnergyInfo; import android.os.test.TestLooper; +import android.util.SparseArray; import androidx.test.filters.SmallTest; @@ -2089,4 +2090,63 @@ public class WifiManagerTest { .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WAPI)); assertFalse(mWifiManager.isWapiSupported()); } + + /* + * Test that DPP channel list is parsed correctly + */ + @Test + public void testparseDppChannelList() throws Exception { + String channelList = "81/1,2,3,4,5,6,7,8,9,10,11,115/36,40,44,48"; + SparseArray<int[]> expectedResult = new SparseArray<>(); + expectedResult.append(81, new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}); + expectedResult.append(115, new int[]{36, 40, 44, 48}); + + SparseArray<int[]> result = WifiManager.parseDppChannelList(channelList); + assertEquals(expectedResult.size(), result.size()); + + int index = 0; + int key; + + // Compare the two primitive int arrays + do { + try { + key = result.keyAt(index); + } catch (java.lang.ArrayIndexOutOfBoundsException e) { + break; + } + int[] expected = expectedResult.get(key); + int[] output = result.get(key); + assertEquals(expected.length, output.length); + for (int i = 0; i < output.length; i++) { + assertEquals(expected[i], output[i]); + } + index++; + } while (true); + } + + /* + * Test that DPP channel list parser gracefully fails for invalid input + */ + @Test + public void testparseDppChannelListWithInvalidFormats() throws Exception { + String channelList = "1,2,3,4,5,6,7,8,9,10,11,36,40,44,48"; + SparseArray<int[]> result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "ajgalskgjalskjg3-09683dh"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "13/abc,46////"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "11/4,5,13/"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + + channelList = "/24,6"; + result = WifiManager.parseDppChannelList(channelList); + assertEquals(result.size(), 0); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java index fa4f711056db..1af0bcbf3f30 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -80,12 +80,6 @@ public class WifiScannerTest { 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 @@ public class WifiScannerTest { 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 @@ public class WifiScannerTest { 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); |