diff options
817 files changed, 19453 insertions, 8946 deletions
diff --git a/Android.bp b/Android.bp index bba6185739fd..554ceae3adad 100644 --- a/Android.bp +++ b/Android.bp @@ -262,12 +262,13 @@ filegroup { name: "framework-updatable-sources", srcs: [ ":framework-appsearch-sources", - ":framework-sdkext-sources", + ":framework-sdkextensions-sources", ":framework-statsd-sources", ":framework-tethering-srcs", ":updatable-media-srcs", ":framework-mediaprovider-sources", ":framework-wifi-updatable-sources", + ":ike-srcs", ] } @@ -432,6 +433,7 @@ java_library { // TODO(b/146167933): Use framework-statsd-stubs "framework-statsd", "framework-wifi-stubs", + "ike-stubs", ], installable: true, javac_shard_size: 150, @@ -440,8 +442,6 @@ java_library { "libcore-platform-compat-config", "services-platform-compat-config", "media-provider-platform-compat-config", - "services-devicepolicy-platform-compat-config", - "services-core-platform-compat-config", ], static_libs: [ // If MimeMap ever becomes its own APEX, then this dependency would need to be removed @@ -481,12 +481,14 @@ java_library { "updatable_media_stubs", "framework_mediaprovider_stubs", "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs - "framework-sdkext-stubs-systemapi", + "framework-sdkextensions-stubs-systemapi", // TODO(b/146167933): Use framework-statsd-stubs instead. "framework-statsd", // TODO(b/140299412): should be framework-wifi-stubs "framework-wifi", - // TODO(jiyong): add more stubs for APEXes here + "ike-stubs", + // TODO(b/147200698): should be the stub of framework-tethering + "framework-tethering", ], sdk_version: "core_platform", apex_available: ["//apex_available:platform"], @@ -616,6 +618,15 @@ java_library { } filegroup { + name: "framework-ike-shared-srcs", + visibility: ["//frameworks/opt/net/ike"], + srcs: [ + "core/java/android/net/annotations/PolicyDirection.java", + "telephony/java/android/telephony/Annotation.java", + ], +} + +filegroup { name: "framework-networkstack-shared-srcs", srcs: [ // TODO: remove these annotations as soon as we can use andoid.support.annotations.* @@ -648,6 +659,7 @@ filegroup { "core/java/com/android/internal/util/Preconditions.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", + "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/android/net/shared/Inet4AddressUtils.java", ], } @@ -1134,6 +1146,7 @@ filegroup { srcs: [ ":framework-annotations", "core/java/android/net/InterfaceConfiguration.java", + "core/java/android/os/BasicShellCommandHandler.java", "core/java/android/os/HandlerExecutor.java", "core/java/android/util/BackupUtils.java", "core/java/android/util/LocalLog.java", diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp index 3dc5a2c10b6b..1f30dda21ef7 100644 --- a/apex/appsearch/framework/Android.bp +++ b/apex/appsearch/framework/Android.bp @@ -26,9 +26,16 @@ java_library { installable: true, sdk_version: "core_platform", // TODO(b/146218515) should be core_current srcs: [":framework-appsearch-sources"], + hostdex: true, // for hiddenapi check libs: [ "framework-minus-apex", // TODO(b/146218515) should be framework-system-stubs ], + visibility: [ + "//frameworks/base/apex/appsearch:__subpackages__", + // TODO(b/146218515) remove this when framework is built with the stub of appsearch + "//frameworks/base", + ], + apex_available: ["com.android.appsearch"], } metalava_appsearch_docs_args = diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS new file mode 100644 index 000000000000..8e04399196e2 --- /dev/null +++ b/apex/blobstore/OWNERS @@ -0,0 +1,4 @@ +set noparent + +sudheersai@google.com +yamasani@google.com diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING new file mode 100644 index 000000000000..4dc0c49380c8 --- /dev/null +++ b/apex/blobstore/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsBlobStoreTestCases" + } + ] +}
\ No newline at end of file diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp index 98bbe8243183..ec074262fb13 100644 --- a/apex/jobscheduler/framework/Android.bp +++ b/apex/jobscheduler/framework/Android.bp @@ -25,5 +25,6 @@ java_library { }, libs: [ "framework-minus-apex", + "unsupportedappusage", ], } diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 8b3b3a28f2bc..0bb07caf0b00 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -29,7 +29,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.content.ComponentName; import android.net.NetworkRequest; diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index 42cf17b1264e..ef1351e6d597 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -18,8 +18,7 @@ package android.app.job; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; -import android.app.job.IJobCallback; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.net.Network; import android.net.Uri; diff --git a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java index c6631fa76494..0c45cbf6dc11 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java @@ -19,7 +19,7 @@ package android.app.job; import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN; import android.annotation.BytesLong; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.os.Build; import android.os.Parcel; diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 041825c235d0..6109b713de24 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -1,5 +1,6 @@ package com.android.server.usage; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; @@ -99,8 +100,18 @@ public interface AppStandbyInternal { List<AppStandbyInfo> getAppStandbyBuckets(int userId); - void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, - int reason, long elapsedRealtime, boolean resetTimeout); + /** + * Changes an app's standby bucket to the provided value. The caller can only set the standby + * bucket for a different app than itself. + */ + void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid, + int callingPid); + + /** + * Changes the app standby bucket for multiple apps at once. + */ + void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, int callingUid, + int callingPid); void addActiveDeviceAdmin(String adminPkg, int userId); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 2f8b5130edb0..58eb58961ac4 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -47,6 +47,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppGlobals; @@ -101,7 +102,6 @@ import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.usage.AppIdleHistory.AppUsageHistory; -import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import java.io.File; import java.io.PrintWriter; @@ -109,6 +109,7 @@ import java.time.Duration; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -1014,14 +1015,57 @@ public class AppStandbyController implements AppStandbyInternal { } } + @Override + public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, + int callingUid, int callingPid) { + setAppStandbyBuckets( + Collections.singletonList(new AppStandbyInfo(packageName, bucket)), + userId, callingUid, callingPid); + } + + @Override + public void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, + int callingUid, int callingPid) { + userId = ActivityManager.handleIncomingUser( + callingPid, callingUid, userId, false, true, "setAppStandbyBucket", null); + final boolean shellCaller = callingUid == Process.ROOT_UID + || callingUid == Process.SHELL_UID; + final boolean systemCaller = UserHandle.isCore(callingUid); + final int reason = systemCaller ? REASON_MAIN_FORCED : REASON_MAIN_PREDICTED; + final int packageFlags = PackageManager.MATCH_ANY_USER + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE; + final int numApps = appBuckets.size(); + final long elapsedRealtime = mInjector.elapsedRealtime(); + for (int i = 0; i < numApps; ++i) { + final AppStandbyInfo bucketInfo = appBuckets.get(i); + final String packageName = bucketInfo.mPackageName; + final int bucket = bucketInfo.mStandbyBucket; + if (bucket < STANDBY_BUCKET_ACTIVE || bucket > STANDBY_BUCKET_NEVER) { + throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket); + } + final int packageUid = mInjector.getPackageManagerInternal() + .getPackageUid(packageName, packageFlags, userId); + // Caller cannot set their own standby state + if (packageUid == callingUid) { + throw new IllegalArgumentException("Cannot set your own standby bucket"); + } + if (packageUid < 0) { + throw new IllegalArgumentException( + "Cannot set standby bucket for non existent package (" + packageName + ")"); + } + setAppStandbyBucket(packageName, userId, bucket, reason, elapsedRealtime, shellCaller); + } + } + @VisibleForTesting void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, - int reason, long elapsedRealtime) { - setAppStandbyBucket(packageName, userId, newBucket, reason, elapsedRealtime, false); + int reason) { + setAppStandbyBucket( + packageName, userId, newBucket, reason, mInjector.elapsedRealtime(), false); } - @Override - public void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, + private void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, int reason, long elapsedRealtime, boolean resetTimeout) { synchronized (mAppIdleLock) { // If the package is not installed, don't allow the bucket to be set. @@ -1444,6 +1488,10 @@ public class AppStandbyController implements AppStandbyInternal { mBatteryStats.noteEvent(event, packageName, uid); } + PackageManagerInternal getPackageManagerInternal() { + return mPackageManagerInternal; + } + boolean isPackageEphemeral(int userId, String packageName) { return mPackageManagerInternal.isPackageEphemeral(userId, packageName); } diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 6bd0086681a7..18382a488428 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -55,6 +55,13 @@ java_library { jarjar_rules: "jarjar_rules.txt", plugins: ["java_api_finder"], + + hostdex: true, // for hiddenapi check + visibility: ["//frameworks/av/apex:__subpackages__"], + apex_available: [ + "com.android.media", + "test_com.android.media", + ], } filegroup { diff --git a/apex/media/framework/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java index c3dd3fe4451c..d059c670ccb6 100644 --- a/apex/media/framework/java/android/media/MediaController2.java +++ b/apex/media/framework/java/android/media/MediaController2.java @@ -141,6 +141,9 @@ public class MediaController2 implements AutoCloseable { // Note: unbindService() throws IllegalArgumentException when it's called twice. return; } + if (DEBUG) { + Log.d(TAG, "closing " + this); + } mClosed = true; if (mServiceConnection != null) { // Note: This should be called even when the bindService() has returned false. diff --git a/apex/sdkext/TEST_MAPPING b/apex/sdkext/TEST_MAPPING deleted file mode 100644 index 91947f39980a..000000000000 --- a/apex/sdkext/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit": [ - { - "name": "CtsSdkExtTestCases" - } - ] -} diff --git a/apex/sdkext/Android.bp b/apex/sdkextensions/Android.bp index f62f167cdcfa..4c5c2b2cfd4f 100644 --- a/apex/sdkext/Android.bp +++ b/apex/sdkextensions/Android.bp @@ -18,21 +18,26 @@ package { apex { name: "com.android.sdkext", - manifest: "manifest.json", + defaults: [ "com.android.sdkext-defaults" ], binaries: [ "derive_sdk" ], - java_libs: [ "framework-sdkext" ], + prebuilts: [ "cur_sdkinfo" ], + manifest: "manifest.json", +} + +apex_defaults { + name: "com.android.sdkext-defaults", + java_libs: [ "framework-sdkextensions" ], prebuilts: [ - "com.android.sdkext.ldconfig", - "cur_sdkinfo", - "derive_sdk.rc", + "com.android.sdkext.ldconfig", + "derive_sdk.rc", ], key: "com.android.sdkext.key", certificate: ":com.android.sdkext.certificate", } sdk { - name: "sdkext-sdk", - java_header_libs: [ "framework-sdkext-stubs-systemapi" ], + name: "sdkextensions-sdk", + java_header_libs: [ "framework-sdkextensions-stubs-systemapi" ], } apex_key { diff --git a/apex/sdkext/OWNERS b/apex/sdkextensions/OWNERS index feb274262bef..feb274262bef 100644 --- a/apex/sdkext/OWNERS +++ b/apex/sdkextensions/OWNERS diff --git a/apex/sdkextensions/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING new file mode 100644 index 000000000000..4e1883382e2c --- /dev/null +++ b/apex/sdkextensions/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "CtsSdkExtensionsTestCases" + }, + { + "name": "apiextensions_e2e_tests" + } + ] +} diff --git a/apex/sdkext/com.android.sdkext.avbpubkey b/apex/sdkextensions/com.android.sdkext.avbpubkey Binary files differindex 8f47741ed3b8..8f47741ed3b8 100644 --- a/apex/sdkext/com.android.sdkext.avbpubkey +++ b/apex/sdkextensions/com.android.sdkext.avbpubkey diff --git a/apex/sdkext/com.android.sdkext.pem b/apex/sdkextensions/com.android.sdkext.pem index 816460183aa3..816460183aa3 100644 --- a/apex/sdkext/com.android.sdkext.pem +++ b/apex/sdkextensions/com.android.sdkext.pem diff --git a/apex/sdkext/com.android.sdkext.pk8 b/apex/sdkextensions/com.android.sdkext.pk8 Binary files differindex ccc0bf438cd1..ccc0bf438cd1 100644 --- a/apex/sdkext/com.android.sdkext.pk8 +++ b/apex/sdkextensions/com.android.sdkext.pk8 diff --git a/apex/sdkext/com.android.sdkext.x509.pem b/apex/sdkextensions/com.android.sdkext.x509.pem index 45d2ade354d4..45d2ade354d4 100644 --- a/apex/sdkext/com.android.sdkext.x509.pem +++ b/apex/sdkextensions/com.android.sdkext.x509.pem diff --git a/apex/sdkext/derive_sdk/Android.bp b/apex/sdkextensions/derive_sdk/Android.bp index c4e3c296f210..cf49902d9978 100644 --- a/apex/sdkext/derive_sdk/Android.bp +++ b/apex/sdkextensions/derive_sdk/Android.bp @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_binary { - name: "derive_sdk", +cc_defaults { + name: "derive_sdk-defaults", srcs: [ "derive_sdk.cpp", "sdk.proto", @@ -30,6 +30,24 @@ cc_binary { ], } +cc_binary { + name: "derive_sdk", + defaults: [ "derive_sdk-defaults" ], + apex_available: [ "com.android.sdkext" ], + visibility: [ "//frameworks/base/apex/sdkextensions" ] +} + +// Work around testing using a 64-bit test suite on 32-bit test device by +// using a prefer32 version of derive_sdk in testing. +cc_binary { + name: "derive_sdk_prefer32", + defaults: [ "derive_sdk-defaults" ], + compile_multilib: "prefer32", + stem: "derive_sdk", + apex_available: [ "test_com.android.sdkext" ], + visibility: [ "//frameworks/base/apex/sdkextensions/testing" ] +} + prebuilt_etc { name: "derive_sdk.rc", src: "derive_sdk.rc", diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp index 0a9711677015..6fb7ef43416e 100644 --- a/apex/sdkext/derive_sdk/derive_sdk.cpp +++ b/apex/sdkextensions/derive_sdk/derive_sdk.cpp @@ -26,7 +26,7 @@ #include <android-base/logging.h> #include <android-base/properties.h> -#include "frameworks/base/apex/sdkext/derive_sdk/sdk.pb.h" +#include "frameworks/base/apex/sdkextensions/derive_sdk/sdk.pb.h" using com::android::sdkext::proto::SdkVersion; diff --git a/apex/sdkext/derive_sdk/derive_sdk.rc b/apex/sdkextensions/derive_sdk/derive_sdk.rc index 1b667949eeaa..1b667949eeaa 100644 --- a/apex/sdkext/derive_sdk/derive_sdk.rc +++ b/apex/sdkextensions/derive_sdk/derive_sdk.rc diff --git a/apex/sdkext/derive_sdk/sdk.proto b/apex/sdkextensions/derive_sdk/sdk.proto index d15b93552ff4..d15b93552ff4 100644 --- a/apex/sdkext/derive_sdk/sdk.proto +++ b/apex/sdkextensions/derive_sdk/sdk.proto diff --git a/apex/sdkext/framework/Android.bp b/apex/sdkextensions/framework/Android.bp index a50dc3d4f349..dd174734df6d 100644 --- a/apex/sdkext/framework/Android.bp +++ b/apex/sdkextensions/framework/Android.bp @@ -17,55 +17,63 @@ package { } filegroup { - name: "framework-sdkext-sources", + name: "framework-sdkextensions-sources", srcs: [ "java/**/*.java", ], path: "java", - visibility: [ "//frameworks/base:__pkg__" ] // For the "global" stubs. + visibility: [ "//frameworks/base" ] // For the "global" stubs. } java_library { - name: "framework-sdkext", - srcs: [ ":framework-sdkext-sources" ], + name: "framework-sdkextensions", + srcs: [ ":framework-sdkextensions-sources" ], sdk_version: "system_current", libs: [ "framework-annotations-lib" ], permitted_packages: [ "android.os.ext" ], installable: true, - visibility: [ "//frameworks/base/apex/sdkext:__pkg__" ], + visibility: [ + "//frameworks/base/apex/sdkextensions", + "//frameworks/base/apex/sdkextensions/testing", + ], + hostdex: true, // for hiddenapi check + apex_available: [ + "com.android.sdkext", + "test_com.android.sdkext", + ], } droidstubs { - name: "framework-sdkext-droidstubs-publicapi", + name: "framework-sdkextensions-droidstubs-publicapi", defaults: [ - "framework-sdkext-stubs-defaults", + "framework-sdkextensions-stubs-defaults", "framework-module-stubs-defaults-publicapi", ] } droidstubs { - name: "framework-sdkext-droidstubs-systemapi", + name: "framework-sdkextensions-droidstubs-systemapi", defaults: [ - "framework-sdkext-stubs-defaults", + "framework-sdkextensions-stubs-defaults", "framework-module-stubs-defaults-systemapi", ] } stubs_defaults { - name: "framework-sdkext-stubs-defaults", + name: "framework-sdkextensions-stubs-defaults", srcs: [ - ":framework-sdkext-sources", + ":framework-sdkextensions-sources", ":framework-annotations", ], sdk_version: "system_current", } java_library { - name: "framework-sdkext-stubs-systemapi", - srcs: [":framework-sdkext-droidstubs-systemapi"], + name: "framework-sdkextensions-stubs-systemapi", + srcs: [":framework-sdkextensions-droidstubs-systemapi"], sdk_version: "system_current", visibility: [ - "//frameworks/base:__pkg__", // Framework - "//frameworks/base/apex/sdkext:__pkg__", // sdkext SDK + "//frameworks/base", // Framework + "//frameworks/base/apex/sdkextensions", // sdkextensions SDK ] } diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java index a8a7effa9b6c..a8a7effa9b6c 100644 --- a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java +++ b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java diff --git a/apex/sdkext/framework/java/android/os/ext/package.html b/apex/sdkextensions/framework/java/android/os/ext/package.html index 34c1697c01fd..34c1697c01fd 100644 --- a/apex/sdkext/framework/java/android/os/ext/package.html +++ b/apex/sdkextensions/framework/java/android/os/ext/package.html diff --git a/apex/sdkext/gen_sdkinfo.py b/apex/sdkextensions/gen_sdkinfo.py index 5af478ba7fe6..5af478ba7fe6 100644 --- a/apex/sdkext/gen_sdkinfo.py +++ b/apex/sdkextensions/gen_sdkinfo.py diff --git a/apex/sdkext/ld.config.txt b/apex/sdkextensions/ld.config.txt index b4470685f4fc..dcc69b892760 100644 --- a/apex/sdkext/ld.config.txt +++ b/apex/sdkextensions/ld.config.txt @@ -1,10 +1,10 @@ # Copyright (C) 2019 The Android Open Source Project # -# Bionic loader config file for the sdkext apex. +# Bionic loader config file for the sdkextensions apex. -dir.sdkext = /apex/com.android.sdkext/bin/ +dir.sdkextensions = /apex/com.android.sdkext/bin/ -[sdkext] +[sdkextensions] additional.namespaces = platform namespace.default.isolated = true diff --git a/apex/sdkext/manifest.json b/apex/sdkextensions/manifest.json index 048f5c4f177b..048f5c4f177b 100644 --- a/apex/sdkext/manifest.json +++ b/apex/sdkextensions/manifest.json diff --git a/apex/sdkext/sdk.proto b/apex/sdkextensions/sdk.proto index d15b93552ff4..d15b93552ff4 100644 --- a/apex/sdkext/sdk.proto +++ b/apex/sdkextensions/sdk.proto diff --git a/apex/sdkextensions/testing/Android.bp b/apex/sdkextensions/testing/Android.bp new file mode 100644 index 000000000000..e6451cc29bc2 --- /dev/null +++ b/apex/sdkextensions/testing/Android.bp @@ -0,0 +1,46 @@ +// 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. + +apex { + name: "test_com.android.sdkext", + visibility: [ "//system/apex/tests" ], + defaults: ["com.android.sdkext-defaults"], + manifest: "test_manifest.json", + prebuilts: [ "sdkinfo_45" ], + file_contexts: ":com.android.sdkext-file_contexts", + installable: false, // Should never be installed on the systemimage + multilib: { + prefer32: { + binaries: ["derive_sdk_prefer32"], + }, + }, + // The automated test infra ends up building this apex for 64+32-bit and + // then installs it on a 32-bit-only device. Work around this weirdness + // by preferring 32-bit. + compile_multilib: "prefer32", +} + +genrule { + name: "sdkinfo_45_src", + out: [ "sdkinfo.binarypb" ], + tools: [ "gen_sdkinfo" ], + cmd: "$(location) -v 45 -o $(out)", +} + +prebuilt_etc { + name: "sdkinfo_45", + src: ":sdkinfo_45_src", + filename: "sdkinfo.binarypb", + installable: false, +} diff --git a/apex/sdkextensions/testing/test_manifest.json b/apex/sdkextensions/testing/test_manifest.json new file mode 100644 index 000000000000..1b4a2b0c6e60 --- /dev/null +++ b/apex/sdkextensions/testing/test_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.sdkext", + "version": 2147483647 +} diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl index 055836777678..dec56345ec2f 100644 --- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl +++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl @@ -85,4 +85,44 @@ interface IStatsManagerService { * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. */ void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName); + + /** + * Returns the most recently registered experiment IDs. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + long[] getRegisteredExperimentIds(); + + /** + * Fetches metadata across statsd. Returns byte array representing wire-encoded proto. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + byte[] getMetadata(in String packageName); + + /** + * Fetches data for the specified configuration key. Returns a byte array representing proto + * wire-encoded of ConfigMetricsReportList. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + byte[] getData(in long key, in String packageName); + + /** + * Sets a configuration with the specified config id and subscribes to updates for this + * configuration id. Broadcasts will be sent if this configuration needs to be collected. + * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is + * registered in a separate function. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + void addConfiguration(in long configId, in byte[] config, in String packageName); + + /** + * Removes the configuration with the matching config id. No-op if this config id does not + * exist. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + void removeConfiguration(in long configId, in String packageName); }
\ No newline at end of file diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index 935843921feb..c409f516de1b 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -89,24 +89,24 @@ interface IStatsd { * * Requires Manifest.permission.DUMP. */ - byte[] getData(in long key, in String packageName); + byte[] getData(in long key, int callingUid); /** * Fetches metadata across statsd. Returns byte array representing wire-encoded proto. * * Requires Manifest.permission.DUMP. */ - byte[] getMetadata(in String packageName); + byte[] getMetadata(); /** - * Sets a configuration with the specified config key and subscribes to updates for this + * Sets a configuration with the specified config id and subscribes to updates for this * configuration key. Broadcasts will be sent if this configuration needs to be collected. * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is * registered in a separate function. * * Requires Manifest.permission.DUMP. */ - void addConfiguration(in long configKey, in byte[] config, in String packageName); + void addConfiguration(in long configId, in byte[] config, in int callingUid); /** * Registers the given pending intent for this config key. This intent is invoked when the @@ -143,12 +143,12 @@ interface IStatsd { void removeActiveConfigsChangedOperation(int callingUid); /** - * Removes the configuration with the matching config key. No-op if this config key does not + * Removes the configuration with the matching config id. No-op if this config id does not * exist. * * Requires Manifest.permission.DUMP. */ - void removeConfiguration(in long configKey, in String packageName); + void removeConfiguration(in long configId, in int callingUid); /** * Set the PendingIntentRef to be used when broadcasting subscriber diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index a2b0577fe001..0b46645ad06f 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -37,7 +37,16 @@ java_library { // TODO(b/146230220): Use framework-system-stubs instead. "android_system_stubs_current", ], - // TODO:(b/146210774): Add apex_available field. + hostdex: true, // for hiddenapi check + visibility: [ + "//frameworks/base/apex/statsd:__subpackages__", + //TODO(b/146167933) remove this when framework is built with framework-statsd-stubs + "//frameworks/base", + ], + apex_available: [ + "com.android.os.statsd", + "test_com.android.os.statsd", + ], } droidstubs { diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java index 1d03e3b702c4..b27d0f7699fc 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java @@ -19,6 +19,7 @@ package com.android.server.stats; import static com.android.server.stats.StatsCompanion.PendingIntentRef; import android.Manifest; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.Context; @@ -245,20 +246,127 @@ public class StatsManagerService extends IStatsManagerService.Stub { } } + @Override + public long[] getRegisteredExperimentIds() throws IllegalStateException { + enforceDumpAndUsageStatsPermission(null); + final long token = Binder.clearCallingIdentity(); + try { + IStatsd statsd = waitForStatsd(); + if (statsd != null) { + return statsd.getRegisteredExperimentIds(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to getRegisteredExperimentIds with statsd"); + throw new IllegalStateException(e.getMessage(), e); + } finally { + Binder.restoreCallingIdentity(token); + } + throw new IllegalStateException("Failed to connect to statsd to registerExperimentIds"); + } + + @Override + public byte[] getMetadata(String packageName) throws IllegalStateException { + enforceDumpAndUsageStatsPermission(packageName); + final long token = Binder.clearCallingIdentity(); + try { + IStatsd statsd = waitForStatsd(); + if (statsd != null) { + return statsd.getMetadata(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to getMetadata with statsd"); + throw new IllegalStateException(e.getMessage(), e); + } finally { + Binder.restoreCallingIdentity(token); + } + throw new IllegalStateException("Failed to connect to statsd to getMetadata"); + } + + @Override + public byte[] getData(long key, String packageName) throws IllegalStateException { + enforceDumpAndUsageStatsPermission(packageName); + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + IStatsd statsd = waitForStatsd(); + if (statsd != null) { + return statsd.getData(key, callingUid); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to getData with statsd"); + throw new IllegalStateException(e.getMessage(), e); + } finally { + Binder.restoreCallingIdentity(token); + } + throw new IllegalStateException("Failed to connect to statsd to getData"); + } + + @Override + public void addConfiguration(long configId, byte[] config, String packageName) + throws IllegalStateException { + enforceDumpAndUsageStatsPermission(packageName); + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + IStatsd statsd = waitForStatsd(); + if (statsd != null) { + statsd.addConfiguration(configId, config, callingUid); + return; + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to addConfiguration with statsd"); + throw new IllegalStateException(e.getMessage(), e); + } finally { + Binder.restoreCallingIdentity(token); + } + throw new IllegalStateException("Failed to connect to statsd to addConfig"); + } + + @Override + public void removeConfiguration(long configId, String packageName) + throws IllegalStateException { + enforceDumpAndUsageStatsPermission(packageName); + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + IStatsd statsd = waitForStatsd(); + if (statsd != null) { + statsd.removeConfiguration(configId, callingUid); + return; + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to removeConfiguration with statsd"); + throw new IllegalStateException(e.getMessage(), e); + } finally { + Binder.restoreCallingIdentity(token); + } + throw new IllegalStateException("Failed to connect to statsd to removeConfig"); + } + void setStatsCompanionService(StatsCompanionService statsCompanionService) { mStatsCompanionService = statsCompanionService; } - private void enforceDumpAndUsageStatsPermission(String packageName) { + /** + * Checks that the caller has both DUMP and PACKAGE_USAGE_STATS permissions. Also checks that + * the caller has USAGE_STATS_PERMISSION_OPS for the specified packageName if it is not null. + * + * @param packageName The packageName to check USAGE_STATS_PERMISSION_OPS. + */ + private void enforceDumpAndUsageStatsPermission(@Nullable String packageName) { int callingUid = Binder.getCallingUid(); int callingPid = Binder.getCallingPid(); if (callingPid == Process.myPid()) { return; } + mContext.enforceCallingPermission(Manifest.permission.DUMP, null); mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, null); + if (packageName == null) { + return; + } AppOpsManager appOpsManager = (AppOpsManager) mContext .getSystemService(Context.APP_OPS_SERVICE); switch (appOpsManager.noteOp(USAGE_STATS_PERMISSION_OPS, diff --git a/api/current.txt b/api/current.txt index c31610fe5fe7..b5448f906e17 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9504,6 +9504,7 @@ package android.content { method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]); method @Nullable public final String getCallingFeatureId(); method @Nullable public final String getCallingPackage(); + method @Nullable public final String getCallingPackageUnchecked(); method @Nullable public final android.content.Context getContext(); method @Nullable public final android.content.pm.PathPermission[] getPathPermissions(); method @Nullable public final String getReadPermission(); @@ -9513,6 +9514,7 @@ package android.content { method @Nullable public abstract android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues); method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable android.os.Bundle); method protected boolean isTemporary(); + method public void onCallingPackageChanged(); method public void onConfigurationChanged(android.content.res.Configuration); method public abstract boolean onCreate(); method public void onLowMemory(); @@ -9627,13 +9629,13 @@ package android.content { ctor public ContentProviderResult(@NonNull android.net.Uri); ctor public ContentProviderResult(int); ctor public ContentProviderResult(@NonNull android.os.Bundle); - ctor public ContentProviderResult(@NonNull Exception); + ctor public ContentProviderResult(@NonNull Throwable); ctor public ContentProviderResult(android.os.Parcel); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.ContentProviderResult> CREATOR; field @Nullable public final Integer count; - field @Nullable public final Exception exception; + field @Nullable public final Throwable exception; field @Nullable public final android.os.Bundle extras; field @Nullable public final android.net.Uri uri; } @@ -9998,6 +10000,7 @@ package android.content { field public static final String MEDIA_ROUTER_SERVICE = "media_router"; field public static final String MEDIA_SESSION_SERVICE = "media_session"; field public static final String MIDI_SERVICE = "midi"; + field public static final String MMS_SERVICE = "mms"; field public static final int MODE_APPEND = 32768; // 0x8000 field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8 field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4 @@ -11353,6 +11356,8 @@ package android.content.pm { } public class CrossProfileApps { + method public boolean canInteractAcrossProfiles(); + method public boolean canRequestInteractAcrossProfiles(); method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle); method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles(); @@ -11925,6 +11930,7 @@ package android.content.pm { field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore"; field public static final String FEATURE_TELEPHONY = "android.hardware.telephony"; field public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma"; + field public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data"; field public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc"; field public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm"; field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims"; @@ -13323,6 +13329,7 @@ package android.database.sqlite { method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String); method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]); method @Nullable public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory(); + method @Nullable public java.util.Collection<java.util.regex.Pattern> getProjectionGreylist(); method @Nullable public java.util.Map<java.lang.String,java.lang.String> getProjectionMap(); method @Nullable public String getTables(); method public long insert(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues); @@ -13335,6 +13342,7 @@ package android.database.sqlite { method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String, android.os.CancellationSignal); method public void setCursorFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory); method public void setDistinct(boolean); + method public void setProjectionGreylist(@Nullable java.util.Collection<java.util.regex.Pattern>); method public void setProjectionMap(@Nullable java.util.Map<java.lang.String,java.lang.String>); method public void setStrict(boolean); method public void setStrictColumns(boolean); @@ -25781,8 +25789,8 @@ package android.media { method @Nullable public android.graphics.Bitmap getImageAtIndex(int); method @Nullable public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams); method @Nullable public android.graphics.Bitmap getPrimaryImage(); - method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int); - method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams); + method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, @IntRange(from=1) int, @IntRange(from=1) int); + method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, @IntRange(from=1) int, @IntRange(from=1) int, @NonNull android.media.MediaMetadataRetriever.BitmapParams); method public void release(); method public void setDataSource(String) throws java.lang.IllegalArgumentException; method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException; @@ -28683,6 +28691,8 @@ package android.media.tv { method public int getVideoWidth(); method public boolean isAudioDescription(); method public boolean isEncrypted(); + method public boolean isHardOfHearing(); + method public boolean isSpokenSubtitle(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR; field public static final int TYPE_AUDIO = 0; // 0x0 @@ -28699,7 +28709,9 @@ package android.media.tv { method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence); method @NonNull public android.media.tv.TvTrackInfo.Builder setEncrypted(boolean); method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle); + method @NonNull public android.media.tv.TvTrackInfo.Builder setHardOfHearing(boolean); method public android.media.tv.TvTrackInfo.Builder setLanguage(String); + method @NonNull public android.media.tv.TvTrackInfo.Builder setSpokenSubtitle(boolean); method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte); method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float); method public android.media.tv.TvTrackInfo.Builder setVideoHeight(int); @@ -29011,6 +29023,37 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR; } + public class ConnectivityDiagnosticsManager { + method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1 + field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2 + } + + public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback { + ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback(); + method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport); + method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport); + method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean); + } + + public static class ConnectivityDiagnosticsManager.ConnectivityReport { + ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); + field @NonNull public final android.os.PersistableBundle additionalInfo; + field @NonNull public final android.net.LinkProperties linkProperties; + field @NonNull public final android.net.Network network; + field @NonNull public final android.net.NetworkCapabilities networkCapabilities; + field public final long reportTimestamp; + } + + public static class ConnectivityDiagnosticsManager.DataStallReport { + ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle); + field public final int detectionMethod; + field @NonNull public final android.net.Network network; + field public final long reportTimestamp; + field @NonNull public final android.os.PersistableBundle stallDetails; + } + public class ConnectivityManager { method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); method public boolean bindProcessToNetwork(@Nullable android.net.Network); @@ -29115,6 +29158,7 @@ package android.net { ctor public DhcpInfo(); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR; field public int dns1; field public int dns2; field public int gateway; @@ -29481,6 +29525,7 @@ package android.net { method public android.net.NetworkRequest.Builder addCapability(int); method public android.net.NetworkRequest.Builder addTransportType(int); method public android.net.NetworkRequest build(); + method @NonNull public android.net.NetworkRequest.Builder clearCapabilities(); method public android.net.NetworkRequest.Builder removeCapability(int); method public android.net.NetworkRequest.Builder removeTransportType(int); method public android.net.NetworkRequest.Builder setNetworkSpecifier(String); @@ -30279,6 +30324,7 @@ package android.net.wifi { @Deprecated public static class WifiConfiguration.AuthAlgorithm { field @Deprecated public static final int LEAP = 2; // 0x2 field @Deprecated public static final int OPEN = 0; // 0x0 + field @Deprecated public static final int SAE = 3; // 0x3 field @Deprecated public static final int SHARED = 1; // 0x1 field @Deprecated public static final String[] strings; field @Deprecated public static final String varName = "auth_alg"; @@ -35681,7 +35727,9 @@ package android.os { method public int describeContents(); method @Nullable public android.os.PersistableBundle getPersistableBundle(@Nullable String); method public void putPersistableBundle(@Nullable String, @Nullable android.os.PersistableBundle); + method @NonNull public static android.os.PersistableBundle readFromStream(@NonNull java.io.InputStream) throws java.io.IOException; method public void writeToParcel(android.os.Parcel, int); + method public void writeToStream(@NonNull java.io.OutputStream) throws java.io.IOException; field @NonNull public static final android.os.Parcelable.Creator<android.os.PersistableBundle> CREATOR; field public static final android.os.PersistableBundle EMPTY; } @@ -35783,6 +35831,7 @@ package android.os { field public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; // 0xffffffed field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8 field public static final int THREAD_PRIORITY_VIDEO = -10; // 0xfffffff6 + field public static final int WIFI_UID = 1010; // 0x3f2 } public abstract class ProxyFileDescriptorCallback { @@ -36349,15 +36398,22 @@ package android.os.storage { method public boolean isObbMounted(String); method public boolean mountObb(String, String, android.os.storage.OnObbStateChangeListener); method @NonNull public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException; + method public void registerStorageVolumeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.os.storage.StorageManager.StorageVolumeCallback); method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException; method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(String, boolean, android.os.storage.OnObbStateChangeListener); + method public void unregisterStorageVolumeCallback(@NonNull android.os.storage.StorageManager.StorageVolumeCallback); field public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; field public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES"; field public static final String EXTRA_UUID = "android.os.storage.extra.UUID"; field public static final java.util.UUID UUID_DEFAULT; } + public static class StorageManager.StorageVolumeCallback { + ctor public StorageManager.StorageVolumeCallback(); + method public void onStateChanged(@NonNull android.os.storage.StorageVolume); + } + public final class StorageVolume implements android.os.Parcelable { method @Deprecated @Nullable public android.content.Intent createAccessIntent(String); method @NonNull public android.content.Intent createOpenDocumentTreeIntent(); @@ -39473,7 +39529,9 @@ package android.provider { field public static final String ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS"; field public static final String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS"; field public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; + field public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"; field public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; + field public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION"; field public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"; field public static final String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION"; field public static final String ACTION_MANAGE_UNKNOWN_APP_SOURCES = "android.settings.MANAGE_UNKNOWN_APP_SOURCES"; @@ -44968,6 +45026,7 @@ package android.telephony { field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe field public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1; // 0xffffffff + field public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX"; field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array"; @@ -45165,6 +45224,7 @@ package android.telephony { public static final class CarrierConfigManager.Ims { field public static final String KEY_PREFIX = "ims."; + field public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT = "ims.wifi_off_deferring_time_int"; } public abstract class CellIdentity implements android.os.Parcelable { @@ -45473,6 +45533,11 @@ package android.telephony { method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback); } + public final class MmsManager { + method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent); + method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent); + } + @Deprecated public class NeighboringCellInfo implements android.os.Parcelable { ctor @Deprecated public NeighboringCellInfo(); ctor @Deprecated public NeighboringCellInfo(int, int); @@ -45669,6 +45734,7 @@ package android.telephony { method public String getOperatorNumeric(); method public boolean getRoaming(); method public int getState(); + method public boolean isSearching(); method public void setIsManualSelection(boolean); method public void setOperatorName(String, String, String); method public void setRoaming(boolean); @@ -45709,8 +45775,8 @@ package android.telephony { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); method public java.util.ArrayList<java.lang.String> divideMessage(String); - method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); - method @Nullable public android.os.Bundle getCarrierConfigValues(); + method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); + method @NonNull public android.os.Bundle getCarrierConfigValues(); method public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int); @@ -45719,7 +45785,7 @@ package android.telephony { method public int getSubscriptionId(); method public void injectSmsPdu(byte[], String, android.app.PendingIntent); method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); - method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); + method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent); @@ -45927,6 +45993,7 @@ package android.telephony { public class SubscriptionManager { method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); + method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid); method public boolean canManageSubscription(android.telephony.SubscriptionInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); @@ -46085,12 +46152,12 @@ package android.telephony { method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle); method public boolean hasCarrierPrivileges(); method public boolean hasIccCard(); - method public boolean iccCloseLogicalChannel(int); - method public byte[] iccExchangeSimIO(int, int, int, int, int, String); + method @Deprecated public boolean iccCloseLogicalChannel(int); + method @Deprecated public byte[] iccExchangeSimIO(int, int, int, int, int, String); method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String); - method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int); - method public String iccTransmitApduBasicChannel(int, int, int, int, int, String); - method public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String); + method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int); + method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String); + method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String); method public boolean isConcurrentVoiceAndDataSupported(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled(); @@ -46108,7 +46175,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); - method public String sendEnvelopeWithStatus(String); + method @Deprecated public String sendEnvelopeWithStatus(String); method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean); @@ -51550,9 +51617,10 @@ package android.view { method public boolean dispatchUnhandledMove(android.view.View, int); method protected void dispatchVisibilityChanged(@NonNull android.view.View, int); method public void dispatchWindowFocusChanged(boolean); - method public void dispatchWindowInsetsAnimationFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); + method public void dispatchWindowInsetsAnimationFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); + method public void dispatchWindowInsetsAnimationPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets); - method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); + method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowVisibilityChanged(int); method @CallSuper public void draw(android.graphics.Canvas); @@ -53232,9 +53300,10 @@ package android.view { } public interface WindowInsetsAnimationCallback { - method public default void onFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); + method public default void onFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); + method public default void onPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation); method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets); - method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); + method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds); } public static final class WindowInsetsAnimationCallback.AnimationBounds { diff --git a/api/system-current.txt b/api/system-current.txt index 33a86214e5a1..63a8b4d6ca91 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -152,6 +152,7 @@ package android { field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"; field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT"; field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES"; + field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION"; field public static final String READ_ACTIVE_EMERGENCY_SESSION = "android.permission.READ_ACTIVE_EMERGENCY_SESSION"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; @@ -567,6 +568,7 @@ package android.app { } public class DownloadManager { + method @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public void onMediaStoreDownloadsDeleted(@NonNull android.util.LongSparseArray<java.lang.String>); field public static final String ACTION_DOWNLOAD_COMPLETED = "android.intent.action.DOWNLOAD_COMPLETED"; } @@ -677,6 +679,7 @@ package android.app { public class StatusBarManager { method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean); } public static final class StatusBarManager.DisableInfo { @@ -799,6 +802,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk(); + method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isOrganizationOwnedDeviceWithManagedProfile(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk(); method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long); method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean); @@ -1652,11 +1656,17 @@ package android.content { method @NonNull public final android.os.UserHandle getSendingUser(); } + public abstract class ContentProvider implements android.content.ComponentCallbacks2 { + method public int checkUriPermission(@NonNull android.net.Uri, int, int); + } + public class ContentProviderClient implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long); } public abstract class ContentResolver { + method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File); + method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri); method @Nullable @RequiresPermission("android.permission.CACHE_CONTENT") public android.os.Bundle getCache(@NonNull android.net.Uri); method @RequiresPermission("android.permission.CACHE_CONTENT") public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle); } @@ -1697,6 +1707,7 @@ package android.content { field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry"; field public static final String TETHERING_SERVICE = "tethering"; field public static final String VR_SERVICE = "vrmanager"; + field public static final String WIFI_COND_SERVICE = "wificond"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; } @@ -3577,9 +3588,17 @@ package android.hardware.usb { } public class UsbManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public long getCurrentFunctions(); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts(); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String); + method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long); field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED"; + field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE"; + field public static final long FUNCTION_NONE = 0L; // 0x0L + field public static final long FUNCTION_RNDIS = 32L; // 0x20L + field public static final String USB_CONFIGURED = "configured"; + field public static final String USB_CONNECTED = "connected"; + field public static final String USB_FUNCTION_RNDIS = "rndis"; } public final class UsbPort { @@ -4292,7 +4311,7 @@ package android.media.session { } public static interface MediaSessionManager.OnMediaKeyEventDispatchedListener { - method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @NonNull android.media.session.MediaSession.Token); + method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @Nullable android.media.session.MediaSession.Token); } public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener { @@ -4676,6 +4695,7 @@ package android.net { method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl(); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); @@ -4683,6 +4703,7 @@ package android.net { method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; @@ -4822,6 +4843,7 @@ package android.net { public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public MatchAllNetworkSpecifier(); method public int describeContents(); + method public boolean satisfiedBy(android.net.NetworkSpecifier); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR; } @@ -4833,6 +4855,7 @@ package android.net { } public final class NetworkCapabilities implements android.os.Parcelable { + method public boolean deduceRestrictedCapability(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String); @@ -4852,6 +4875,17 @@ package android.net { field public final android.net.WifiKey wifiKey; } + public class NetworkProvider { + ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String); + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest); + method @Nullable public android.os.Messenger getMessenger(); + method @NonNull public String getName(); + method public int getProviderId(); + method public void onNetworkRequested(@NonNull android.net.NetworkRequest, int, int); + method public void onRequestWithdrawn(@NonNull android.net.NetworkRequest); + field public static final int ID_NONE = -1; // 0xffffffff + } + public abstract class NetworkRecommendationProvider { ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor); method public final android.os.IBinder getBinder(); @@ -4885,10 +4919,43 @@ package android.net { method public void updateScores(@NonNull java.util.List<android.net.ScoredNetwork>); } + public abstract class NetworkSpecifier { + method public void assertValidFromUid(int); + method @Nullable public android.net.NetworkSpecifier redact(); + method public abstract boolean satisfiedBy(@Nullable android.net.NetworkSpecifier); + } + public class NetworkStack { field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; } + public final class NetworkStats implements android.os.Parcelable { + ctor public NetworkStats(long, int); + method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats); + method @NonNull public android.net.NetworkStats addValues(@NonNull android.net.NetworkStats.Entry); + method public int describeContents(); + method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR; + field public static final int DEFAULT_NETWORK_NO = 0; // 0x0 + field public static final int DEFAULT_NETWORK_YES = 1; // 0x1 + field @Nullable public static final String IFACE_ALL; + field public static final String IFACE_VT = "vt_data0"; + field public static final int METERED_NO = 0; // 0x0 + field public static final int METERED_YES = 1; // 0x1 + field public static final int ROAMING_NO = 0; // 0x0 + field public static final int ROAMING_YES = 1; // 0x1 + field public static final int SET_DEFAULT = 0; // 0x0 + field public static final int SET_FOREGROUND = 1; // 0x1 + field public static final int TAG_NONE = 0; // 0x0 + field public static final int UID_ALL = -1; // 0xffffffff + field public static final int UID_TETHERING = -5; // 0xfffffffb + } + + public static class NetworkStats.Entry { + ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long); + } + public final class RouteInfo implements android.os.Parcelable { ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); method public int getType(); @@ -4955,6 +5022,7 @@ package android.net { public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public StringNetworkSpecifier(@NonNull String); method public int describeContents(); + method public boolean satisfiedBy(android.net.NetworkSpecifier); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR; field @NonNull public final String specifier; @@ -5479,6 +5547,25 @@ package android.net.metrics { } +package android.net.netstats.provider { + + public abstract class AbstractNetworkStatsProvider { + ctor public AbstractNetworkStatsProvider(); + method public abstract void requestStatsUpdate(int); + method public abstract void setAlert(long); + method public abstract void setLimit(@NonNull String, long); + field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff + } + + public class NetworkStatsProviderCallback { + method public void onAlertReached(); + method public void onLimitReached(); + method public void onStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats); + method public void unregister(); + } + +} + package android.net.util { public final class SocketUtils { @@ -5738,6 +5825,7 @@ package android.net.wifi { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR; field public static final int SOFTAP_FEATURE_ACS_OFFLOAD = 1; // 0x1 field public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2; // 0x2 + field public static final int SOFTAP_FEATURE_WPA3_SAE = 4; // 0x4 } public final class SoftApConfiguration implements android.os.Parcelable { @@ -5746,9 +5834,10 @@ package android.net.wifi { method @Nullable public android.net.MacAddress getBssid(); method public int getChannel(); method public int getMaxNumberOfClients(); + method @Nullable public String getPassphrase(); method public int getSecurityType(); + method public int getShutdownTimeoutMillis(); method @Nullable public String getSsid(); - method @Nullable public String getWpa2Passphrase(); method public boolean isHiddenSsid(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int BAND_2GHZ = 1; // 0x1 @@ -5758,6 +5847,8 @@ package android.net.wifi { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR; field public static final int SECURITY_TYPE_OPEN = 0; // 0x0 field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1 + field public static final int SECURITY_TYPE_WPA3_SAE = 3; // 0x3 + field public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; // 0x2 } public static final class SoftApConfiguration.Builder { @@ -5769,8 +5860,9 @@ package android.net.wifi { method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(int); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String); - method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String); } public final class SoftApInfo implements android.os.Parcelable { @@ -5913,6 +6005,7 @@ package android.net.wifi { public class WifiManager { method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener); @@ -5932,6 +6025,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>); method public boolean isApMacRandomizationSupported(); method public boolean isConnectedMacRandomizationSupported(); method @Deprecated public boolean isDeviceToDeviceRttSupported(); @@ -5989,6 +6083,7 @@ package android.net.wifi { field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK"; field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; field public static final String EXTRA_URL = "android.net.wifi.extra.URL"; + field public static final String EXTRA_WIFI_AP_FAILURE_REASON = "android.net.wifi.extra.WIFI_AP_FAILURE_REASON"; field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME"; field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE"; field public static final String EXTRA_WIFI_AP_STATE = "wifi_state"; @@ -6067,6 +6162,10 @@ package android.net.wifi { field public int numUsage; } + public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { + method public boolean satisfiedBy(android.net.NetworkSpecifier); + } + public static final class WifiNetworkSuggestion.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int); } @@ -6253,6 +6352,10 @@ package android.net.wifi.aware { method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]); } + public final class WifiAwareNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { + method public boolean satisfiedBy(android.net.NetworkSpecifier); + } + public class WifiAwareSession implements java.lang.AutoCloseable { method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]); } @@ -6269,6 +6372,10 @@ package android.net.wifi.hotspot2 { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.OsuProvider> CREATOR; } + public final class PasspointConfiguration implements android.os.Parcelable { + method public boolean isAutoJoinEnabled(); + } + public abstract class ProvisioningCallback { ctor public ProvisioningCallback(); method public abstract void onProvisioningComplete(); @@ -6935,6 +7042,10 @@ package android.os { method public boolean hasSingleFileDescriptor(); } + public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable { + method @NonNull public static android.os.ParcelFileDescriptor wrap(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.Handler, @NonNull android.os.ParcelFileDescriptor.OnCloseListener) throws java.io.IOException; + } + public final class PowerManager { method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend(); @@ -7043,7 +7154,22 @@ package android.os { } public class TelephonyServiceManager { + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getCarrierConfigServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccCardControllerServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccControllerService(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getIccPhoneBookServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getNetworkPolicyServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getOpportunisticNetworkServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPackageManagerServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPermissionManagerServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPhoneSubServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSmsServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSubscriptionServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyImsServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRcsMessageServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRegistryServiceRegisterer(); method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyServiceRegisterer(); + method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getWindowServiceRegisterer(); } public static class TelephonyServiceManager.ServiceNotFoundException extends java.lang.Exception { @@ -7059,11 +7185,13 @@ package android.os { public class UpdateEngine { ctor public UpdateEngine(); + method @NonNull public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]); method public void applyPayload(String, long, long, String[]); - method public void applyPayload(@NonNull android.os.ParcelFileDescriptor, long, long, @NonNull String[]); + method public void applyPayload(@NonNull android.content.res.AssetFileDescriptor, @NonNull String[]); method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler); method public boolean bind(android.os.UpdateEngineCallback); method public void cancel(); + method public int cleanupAppliedPayload(); method public void resetStatus(); method public void resume(); method public void suspend(); @@ -7071,14 +7199,21 @@ package android.os { method public boolean verifyPayloadMetadata(String); } + public static final class UpdateEngine.AllocateSpaceResult { + method public int errorCode(); + method public long freeSpaceRequired(); + } + public static final class UpdateEngine.ErrorCodeConstants { ctor public UpdateEngine.ErrorCodeConstants(); + field public static final int DEVICE_CORRUPTED = 61; // 0x3d field public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; // 0xc field public static final int DOWNLOAD_TRANSFER_ERROR = 9; // 0x9 field public static final int ERROR = 1; // 0x1 field public static final int FILESYSTEM_COPIER_ERROR = 4; // 0x4 field public static final int INSTALL_DEVICE_OPEN_ERROR = 7; // 0x7 field public static final int KERNEL_DEVICE_OPEN_ERROR = 8; // 0x8 + field public static final int NOT_ENOUGH_SPACE = 60; // 0x3c field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6 field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb @@ -7314,6 +7449,10 @@ package android.os.storage { field @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1 } + public final class StorageVolume implements android.os.Parcelable { + method @NonNull public String getId(); + } + } package android.permission { @@ -7723,6 +7862,7 @@ package android.provider { } public final class Settings { + method public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, boolean); field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS"; field public static final String ACTION_BUGREPORT_HANDLER_SETTINGS = "android.settings.BUGREPORT_HANDLER_SETTINGS"; field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; @@ -7733,6 +7873,7 @@ package android.provider { field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS"; field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; + field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; } public static final class Settings.Global extends android.provider.Settings.NameValueTable { @@ -7752,6 +7893,7 @@ package android.provider { field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis"; field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt"; + field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled"; field public static final String TETHER_SUPPORTED = "tether_supported"; field public static final String THEATER_MODE_ON = "theater_mode_on"; field public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; @@ -7891,7 +8033,71 @@ package android.provider { } public static final class Telephony.SimInfo { + field public static final String ACCESS_RULES = "access_rules"; + field public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = "access_rules_from_carrier_configs"; + field public static final String CARD_ID = "card_id"; + field public static final String CARRIER_ID = "carrier_id"; + field public static final String CARRIER_NAME = "carrier_name"; + field public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; + field public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration"; + field public static final String CB_ALERT_SPEECH = "enable_alert_speech"; + field public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate"; + field public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts"; + field public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts"; + field public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts"; + field public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts"; + field public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts"; + field public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts"; + field public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; + field public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts"; + field public static final String COLOR = "color"; field @NonNull public static final android.net.Uri CONTENT_URI; + field public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules"; + field public static final String DATA_ROAMING = "data_roaming"; + field public static final int DATA_ROAMING_DEFAULT = 0; // 0x0 + field public static final int DATA_ROAMING_DISABLE = 0; // 0x0 + field public static final int DATA_ROAMING_ENABLE = 1; // 0x1 + field public static final String DISPLAY_NAME = "display_name"; + field public static final String EHPLMNS = "ehplmns"; + field public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled"; + field public static final String GROUP_OWNER = "group_owner"; + field public static final String GROUP_UUID = "group_uuid"; + field public static final String HPLMNS = "hplmns"; + field public static final String ICC_ID = "icc_id"; + field public static final String IMSI = "imsi"; + field public static final String ISO_COUNTRY_CODE = "iso_country_code"; + field public static final String IS_EMBEDDED = "is_embedded"; + field public static final String IS_OPPORTUNISTIC = "is_opportunistic"; + field public static final String IS_REMOVABLE = "is_removable"; + field public static final String MCC = "mcc"; + field public static final String MCC_STRING = "mcc_string"; + field public static final String MNC = "mnc"; + field public static final String MNC_STRING = "mnc_string"; + field public static final String NAME_SOURCE = "name_source"; + field public static final int NAME_SOURCE_CARRIER = 3; // 0x3 + field public static final int NAME_SOURCE_DEFAULT = 0; // 0x0 + field public static final int NAME_SOURCE_SIM_PNN = 4; // 0x4 + field public static final int NAME_SOURCE_SIM_SPN = 1; // 0x1 + field public static final int NAME_SOURCE_USER_INPUT = 2; // 0x2 + field public static final String NUMBER = "number"; + field public static final String PROFILE_CLASS = "profile_class"; + field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff + field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2 + field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1 + field public static final int PROFILE_CLASS_TESTING = 0; // 0x0 + field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff + field public static final int SIM_NOT_INSERTED = -1; // 0xffffffff + field public static final String SIM_SLOT_INDEX = "sim_id"; + field public static final String SUBSCRIPTION_TYPE = "subscription_type"; + field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0 + field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1 + field public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled"; + field public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id"; + field public static final String VT_IMS_ENABLED = "vt_ims_enabled"; + field public static final String WFC_IMS_ENABLED = "wfc_ims_enabled"; + field public static final String WFC_IMS_MODE = "wfc_ims_mode"; + field public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled"; + field public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode"; } public static final class Telephony.Sms.Intents { @@ -9158,30 +9364,37 @@ package android.telephony { public abstract class CellIdentity implements android.os.Parcelable { method @NonNull public abstract android.telephony.CellLocation asCellLocation(); + method @NonNull public abstract android.telephony.CellIdentity sanitizeLocationInfo(); } public final class CellIdentityCdma extends android.telephony.CellIdentity { method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation(); + method @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo(); } public final class CellIdentityGsm extends android.telephony.CellIdentity { method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + method @NonNull public android.telephony.CellIdentityGsm sanitizeLocationInfo(); } public final class CellIdentityLte extends android.telephony.CellIdentity { method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + method @NonNull public android.telephony.CellIdentityLte sanitizeLocationInfo(); } public final class CellIdentityNr extends android.telephony.CellIdentity { method @NonNull public android.telephony.CellLocation asCellLocation(); + method @NonNull public android.telephony.CellIdentityNr sanitizeLocationInfo(); } public final class CellIdentityTdscdma extends android.telephony.CellIdentity { method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + method @NonNull public android.telephony.CellIdentityTdscdma sanitizeLocationInfo(); } public final class CellIdentityWcdma extends android.telephony.CellIdentity { method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation(); + method @NonNull public android.telephony.CellIdentityWcdma sanitizeLocationInfo(); } public final class DataFailCause { @@ -10189,10 +10402,10 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String); - method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int); @@ -10359,6 +10572,20 @@ package android.telephony { method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings); } + public final class WapPushManagerConnector { + ctor public WapPushManagerConnector(@NonNull android.content.Context); + method public boolean bindToWapPushManagerService(); + method @Nullable public String getConnectedWapPushManagerServicePackage(); + method public int processMessage(@NonNull String, @NonNull String, @NonNull android.content.Intent); + method public void unbindWapPushManagerService(); + field public static final int RESULT_APP_QUERY_FAILED = 2; // 0x2 + field public static final int RESULT_EXCEPTION_CAUGHT = 16; // 0x10 + field public static final int RESULT_FURTHER_PROCESSING = 32768; // 0x8000 + field public static final int RESULT_INVALID_RECEIVER_NAME = 8; // 0x8 + field public static final int RESULT_MESSAGE_HANDLED = 1; // 0x1 + field public static final int RESULT_SIGNATURE_NO_MATCH = 4; // 0x4 + } + } package android.telephony.cdma { @@ -11609,6 +11836,8 @@ package android.view.accessibility { public final class AccessibilityManager { method public int getAccessibilityWindowId(@Nullable android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut(); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int); } } diff --git a/api/test-current.txt b/api/test-current.txt index 99679428cefb..d017dd63bdd3 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -437,6 +437,7 @@ package android.app { public class StatusBarManager { method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean); } public static final class StatusBarManager.DisableInfo { @@ -2551,6 +2552,7 @@ package android.provider { field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION"; field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; + field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1 } @@ -2569,6 +2571,7 @@ package android.provider { field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; field public static final String NOTIFICATION_BUBBLES = "notification_bubbles"; field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices"; + field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled"; field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 7b96ce92e307..1c6867c39790 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -78,7 +78,6 @@ cc_defaults { "src/external/StatsPuller.cpp", "src/external/StatsPullerManager.cpp", "src/external/SubsystemSleepStatePuller.cpp", - "src/external/SurfaceflingerStatsPuller.cpp", "src/external/TrainInfoPuller.cpp", "src/FieldValue.cpp", "src/guardrail/StatsdStats.cpp", @@ -126,30 +125,28 @@ cc_defaults { ], static_libs: [ - "libhealthhalutils", - "libplatformprotos", - ], - - shared_libs: [ "android.frameworks.stats@1.0", - "android.hardware.health@2.0", "android.hardware.power.stats@1.0", "android.hardware.power@1.0", "android.hardware.power@1.1", "libbase", - "libbinder", "libcutils", - "libgraphicsenv", - "libhidlbase", - "libincident", + "libhealthhalutils", "liblog", + "libplatformprotos", "libprotoutil", - "libservices", "libstatslog", - "libstatsmetadata", "libstatssocket", "libsysutils", - "libtimestats_proto", + ], + shared_libs: [ + "android.hardware.health@2.0", + "libbinder", + "libgraphicsenv", + "libhidlbase", + "libincident", + "libservices", + "libstatsmetadata", "libutils", ], } @@ -298,7 +295,6 @@ cc_test { "tests/external/puller_util_test.cpp", "tests/external/StatsCallbackPuller_test.cpp", "tests/external/StatsPuller_test.cpp", - "tests/external/SurfaceflingerStatsPuller_test.cpp", "tests/FieldValue_test.cpp", "tests/guardrail/StatsdStats_test.cpp", "tests/indexed_priority_queue_test.cpp", diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 5ff0f97865b8..1ca19c3417c2 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1135,12 +1135,11 @@ void StatsService::OnLogEvent(LogEvent* event) { } } -Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); +Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) { + ENFORCE_UID(AID_SYSTEM); - IPCThreadState* ipc = IPCThreadState::self(); - VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); - ConfigKey configKey(ipc->getCallingUid(), key); + VLOG("StatsService::getData with Uid %i", callingUid); + ConfigKey configKey(callingUid, key); // The dump latency does not matter here since we do not include the current bucket, we do not // need to pull any new data anyhow. mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/, @@ -1148,22 +1147,18 @@ Status StatsService::getData(int64_t key, const String16& packageName, vector<ui return Status::ok(); } -Status StatsService::getMetadata(const String16& packageName, vector<uint8_t>* output) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); +Status StatsService::getMetadata(vector<uint8_t>* output) { + ENFORCE_UID(AID_SYSTEM); - IPCThreadState* ipc = IPCThreadState::self(); - VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(), - ipc->getCallingUid()); StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters. return Status::ok(); } Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config, - const String16& packageName) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); + const int32_t callingUid) { + ENFORCE_UID(AID_SYSTEM); - IPCThreadState* ipc = IPCThreadState::self(); - if (addConfigurationChecked(ipc->getCallingUid(), key, config)) { + if (addConfigurationChecked(callingUid, key, config)) { return Status::ok(); } else { ALOGE("Could not parse malformatted StatsdConfig"); @@ -1228,13 +1223,11 @@ Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUi return Status::ok(); } -Status StatsService::removeConfiguration(int64_t key, const String16& packageName) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); +Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) { + ENFORCE_UID(AID_SYSTEM); - IPCThreadState* ipc = IPCThreadState::self(); - ConfigKey configKey(ipc->getCallingUid(), key); + ConfigKey configKey(callingUid, key); mConfigManager->RemoveConfig(configKey); - SubscriberReporter::getInstance().removeConfig(configKey); return Status::ok(); } @@ -1477,17 +1470,7 @@ Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackType Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) { - uid_t uid = IPCThreadState::self()->getCallingUid(); - - // Caller must be granted these permissions - if (!checkCallingPermission(String16(kPermissionDump))) { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d lacks permission %s", uid, kPermissionDump)); - } - if (!checkCallingPermission(String16(kPermissionUsage))) { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage)); - } + ENFORCE_UID(AID_SYSTEM); // TODO: add verifier permission // Read the latest train info diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 0565f3ce2484..c9a9072ecb92 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -99,15 +99,14 @@ public: * Binder call for clients to request data for this configuration key. */ virtual Status getData(int64_t key, - const String16& packageName, + const int32_t callingUid, vector<uint8_t>* output) override; /** * Binder call for clients to get metadata across all configs in statsd. */ - virtual Status getMetadata(const String16& packageName, - vector<uint8_t>* output) override; + virtual Status getMetadata(vector<uint8_t>* output) override; /** @@ -116,7 +115,7 @@ public: */ virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config, - const String16& packageName) override; + const int32_t callingUid) override; /** * Binder call to let clients register the data fetch operation for a configuration. @@ -146,7 +145,7 @@ public: * Binder call to allow clients to remove the specified configuration. */ virtual Status removeConfiguration(int64_t key, - const String16& packageName) override; + const int32_t callingUid) override; /** * Binder call to associate the given config's subscriberId with the given pendingIntentRef. diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 55d73c1ae342..972adf7d4d05 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -189,25 +189,11 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { // Remove from map uidIt->second.erase(key); - // No more configs for this uid, lets remove the active configs callback. - if (uidIt->second.empty()) { - auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid); - if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) { - mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver); - } - } - for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } } - auto itReceiver = mConfigReceivers.find(key); - if (itReceiver != mConfigReceivers.end()) { - // Remove from map - mConfigReceivers.erase(itReceiver); - } - // Remove from disk. There can still be a lingering file on disk so we check // whether or not the config was on memory. remove_saved_configs(key); @@ -238,12 +224,6 @@ void ConfigManager::RemoveConfigs(int uid) { // Remove from map remove_saved_configs(*it); removed.push_back(*it); - mConfigReceivers.erase(*it); - } - - auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid); - if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) { - mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver); } mConfigs.erase(uidIt); @@ -277,8 +257,6 @@ void ConfigManager::RemoveAllConfigs() { uidIt = mConfigs.erase(uidIt); } - mConfigReceivers.clear(); - mActiveConfigsChangedReceivers.clear(); for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 50896f84da43..1d31873209b2 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -41,7 +41,6 @@ #include "StatsCallbackPullerDeprecated.h" #include "StatsCompanionServicePuller.h" #include "SubsystemSleepStatePuller.h" -#include "SurfaceflingerStatsPuller.h" #include "TrainInfoPuller.h" #include "statslog.h" @@ -273,10 +272,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // App ops {{.atomTag = android::util::APP_OPS}, {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}}, - // SurfaceflingerStatsGlobalInfo - {{.atomTag = android::util::SURFACEFLINGER_STATS_GLOBAL_INFO}, - {.puller = - new SurfaceflingerStatsPuller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO)}}, // VmsClientStats {{.atomTag = android::util::VMS_CLIENT_STATS}, {.additiveFields = {5, 6, 7, 8, 9, 10}, diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp deleted file mode 100644 index 23b2236f35f2..000000000000 --- a/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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. - */ - -#include "SurfaceflingerStatsPuller.h" - -#include <cutils/compiler.h> - -#include <numeric> - -#include "logd/LogEvent.h" -#include "stats_log_util.h" -#include "statslog.h" - -namespace android { -namespace os { -namespace statsd { - -SurfaceflingerStatsPuller::SurfaceflingerStatsPuller(const int tagId) : StatsPuller(tagId) { -} - -bool SurfaceflingerStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) { - switch (mTagId) { - case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO: - return pullGlobalInfo(data); - default: - break; - } - - return false; -} - -static int64_t getTotalTime( - const google::protobuf::RepeatedPtrField<surfaceflinger::SFTimeStatsHistogramBucketProto>& - buckets) { - int64_t total = 0; - for (const auto& bucket : buckets) { - if (bucket.time_millis() == 1000) { - continue; - } - - total += bucket.time_millis() * bucket.frame_count(); - } - - return total; -} - -bool SurfaceflingerStatsPuller::pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data) { - std::string protoBytes; - if (CC_UNLIKELY(mStatsProvider)) { - protoBytes = mStatsProvider(); - } else { - std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("dumpsys SurfaceFlinger --timestats -dump --proto", "r"), pclose); - if (!pipe.get()) { - return false; - } - char buf[1024]; - size_t bytesRead = 0; - do { - bytesRead = fread(buf, 1, sizeof(buf), pipe.get()); - protoBytes.append(buf, bytesRead); - } while (bytesRead > 0); - } - surfaceflinger::SFTimeStatsGlobalProto proto; - proto.ParseFromString(protoBytes); - - int64_t totalTime = getTotalTime(proto.present_to_present()); - - data->clear(); - data->reserve(1); - std::shared_ptr<LogEvent> event = - make_shared<LogEvent>(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, getWallClockNs(), - getElapsedRealtimeNs()); - if (!event->write(proto.total_frames())) return false; - if (!event->write(proto.missed_frames())) return false; - if (!event->write(proto.client_composition_frames())) return false; - if (!event->write(proto.display_on_time())) return false; - if (!event->write(totalTime)) return false; - event->init(); - data->emplace_back(event); - - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.h b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h deleted file mode 100644 index ed7153edf797..000000000000 --- a/cmds/statsd/src/external/SurfaceflingerStatsPuller.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <timestatsproto/TimeStatsProtoHeader.h> - -#include "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Pull metrics from Surfaceflinger - */ -class SurfaceflingerStatsPuller : public StatsPuller { -public: - explicit SurfaceflingerStatsPuller(const int tagId); - - // StatsPuller interface - bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override; - -protected: - // Test-only, for injecting fake data - using StatsProvider = std::function<std::string()>; - StatsProvider mStatsProvider; - -private: - bool pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 35c6d373418e..e85b97514242 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -207,10 +207,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio &linkedConditionDimensionKey); if (trueConditionDimensions.find(linkedConditionDimensionKey) != trueConditionDimensions.end()) { - for (auto& condIt : whatIt.second) { - condIt.second->onConditionChanged( - currentUnSlicedPartCondition, eventTime); - } + whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime); } } } else { @@ -222,15 +219,11 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio &linkedConditionDimensionKey); if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) != dimensionsChangedToTrue->end()) { - for (auto& condIt : whatIt.second) { - condIt.second->onConditionChanged(true, eventTime); - } + whatIt.second->onConditionChanged(true, eventTime); } if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) != dimensionsChangedToFalse->end()) { - for (auto& condIt : whatIt.second) { - condIt.second->onConditionChanged(false, eventTime); - } + whatIt.second->onConditionChanged(false, eventTime); } } } @@ -247,9 +240,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overa // Now for each of the on-going event, check if the condition has changed for them. for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - for (auto& pair : whatIt.second) { - pair.second->onSlicedConditionMayChange(overallCondition, eventTimeNs); - } + whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs); } } @@ -283,18 +274,14 @@ void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTime } for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - for (auto& pair : whatIt.second) { - pair.second->onConditionChanged(mIsActive, eventTimeNs); - } + whatIt.second->onConditionChanged(mIsActive, eventTimeNs); } } else if (mIsActive) { flushIfNeededLocked(eventTimeNs); onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs); } else { // mConditionSliced == true && !mIsActive for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - for (auto& pair : whatIt.second) { - pair.second->onConditionChanged(mIsActive, eventTimeNs); - } + whatIt.second->onConditionChanged(mIsActive, eventTimeNs); } } } @@ -310,9 +297,7 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, flushIfNeededLocked(eventTime); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - for (auto& pair : whatIt.second) { - pair.second->onConditionChanged(conditionMet, eventTime); - } + whatIt.second->onConditionChanged(conditionMet, eventTime); } } @@ -425,19 +410,11 @@ void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs const int64_t& nextBucketStartTimeNs) { for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin(); whatIt != mCurrentSlicedDurationTrackerMap.end();) { - for (auto it = whatIt->second.begin(); it != whatIt->second.end();) { - if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) { - VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(), - it->first.toString().c_str()); - it = whatIt->second.erase(it); - } else { - ++it; - } - } - if (whatIt->second.empty()) { + if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) { + VLOG("erase bucket for key %s", whatIt->first.toString().c_str()); whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt); } else { - whatIt++; + ++whatIt; } } StatsdStats::getInstance().noteBucketCount(mMetricId); @@ -453,35 +430,15 @@ void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { (unsigned long)mCurrentSlicedDurationTrackerMap.size()); if (verbose) { for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) { - for (const auto& slice : whatIt.second) { - fprintf(out, "\t(what)%s\t(states)%s\n", whatIt.first.toString().c_str(), - slice.first.toString().c_str()); - slice.second->dumpStates(out, verbose); - } + fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str()); + whatIt.second->dumpStates(out, verbose); } } } bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat()); - if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - auto stateIt = whatIt->second.find(newKey.getStateValuesKey()); - if (stateIt != whatIt->second.end()) { - return false; - } - if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = whatIt->second.size() + 1; - StatsdStats::getInstance().noteMetricDimensionInConditionSize( - mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("DurationMetric %lld dropping data for state values key %s", - (long long)mMetricId, newKey.getStateValuesKey().toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - } else { + if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1; @@ -503,24 +460,16 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey const ConditionKey& conditionKeys, bool condition, const LogEvent& event) { const auto& whatKey = eventKey.getDimensionKeyInWhat(); - const auto& stateKey = eventKey.getStateValuesKey(); auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { if (hitGuardRailLocked(eventKey)) { return; } - mCurrentSlicedDurationTrackerMap[whatKey][stateKey] = createDurationTracker(eventKey); - } else { - if (whatIt->second.find(stateKey) == whatIt->second.end()) { - if (hitGuardRailLocked(eventKey)) { - return; - } - mCurrentSlicedDurationTrackerMap[whatKey][stateKey] = createDurationTracker(eventKey); - } + mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey); } - auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(stateKey); + auto it = mCurrentSlicedDurationTrackerMap.find(whatKey); if (mUseWhatDimensionAsInternalDimension) { it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys); @@ -560,18 +509,14 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, // Handles Stopall events. if (matcherIndex == mStopAllIndex) { for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - for (auto& pair : whatIt.second) { - pair.second->noteStopAll(event.GetElapsedTimestampNs()); - } + whatIt.second->noteStopAll(event.GetElapsedTimestampNs()); } return; } - HashableDimensionKey dimensionInWhat; + HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY; if (!mDimensionsInWhat.empty()) { filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); - } else { - dimensionInWhat = DEFAULT_DIMENSION_KEY; } // Handles Stop events. @@ -579,9 +524,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, if (mUseWhatDimensionAsInternalDimension) { auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - for (const auto& stateIt : whatIt->second) { - stateIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); - } + whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); } return; } @@ -593,10 +536,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - for (const auto& stateIt : whatIt->second) { - stateIt.second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), - false); - } + whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false); } return; } @@ -619,8 +559,8 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, condition = condition && mIsActive; - handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY), - conditionKey, condition, event); + handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY), conditionKey, + condition, event); } size_t DurationMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 45908fb48f75..06da0f64aedb 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -132,8 +132,7 @@ private: std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets; // The duration trackers in the current bucket. - std::unordered_map<HashableDimensionKey, - std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>> + std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>> mCurrentSlicedDurationTrackerMap; // Helper function to create a duration tracker given the metric aggregation type. diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 6b5c2994a0c8..afe93d445e1d 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -82,8 +82,6 @@ public: virtual ~DurationTracker(){}; - virtual unique_ptr<DurationTracker> clone(const int64_t eventTime) = 0; - virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, const ConditionKey& conditionKey) = 0; virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime, diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index df66cb0c53e5..2be5855e90e6 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -37,24 +37,6 @@ MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, conditionSliced, fullLink, anomalyTrackers) { } -unique_ptr<DurationTracker> MaxDurationTracker::clone(const int64_t eventTime) { - auto clonedTracker = make_unique<MaxDurationTracker>(*this); - for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end();) { - if (it->second.state != kStopped) { - it->second.lastStartTime = eventTime; - it->second.lastDuration = 0; - it++; - } else { - it = clonedTracker->mInfos.erase(it); - } - } - if (clonedTracker->mInfos.empty()) { - return nullptr; - } else { - return clonedTracker; - } -} - bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // ===========GuardRail============== if (mInfos.find(newKey) != mInfos.end()) { diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index d0371da096ea..efb8dc70afd1 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -38,8 +38,6 @@ public: MaxDurationTracker(const MaxDurationTracker& tracker) = default; - unique_ptr<DurationTracker> clone(const int64_t eventTime) override; - void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, const ConditionKey& conditionKey) override; void noteStop(const HashableDimensionKey& key, const int64_t eventTime, diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index b0fd975b2328..57f39656fdfe 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -38,13 +38,6 @@ OringDurationTracker::OringDurationTracker( mLastStartTime = 0; } -unique_ptr<DurationTracker> OringDurationTracker::clone(const int64_t eventTime) { - auto clonedTracker = make_unique<OringDurationTracker>(*this); - clonedTracker->mLastStartTime = eventTime; - clonedTracker->mDuration = 0; - return clonedTracker; -} - bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // ===========GuardRail============== // 1. Report the tuple count if the tuple count > soft limit diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index 43c48d5f536b..c3aad668aa78 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -37,8 +37,6 @@ public: OringDurationTracker(const OringDurationTracker& tracker) = default; - unique_ptr<DurationTracker> clone(const int64_t eventTime) override; - void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, const ConditionKey& conditionKey) override; void noteStop(const HashableDimensionKey& key, const int64_t eventTime, diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp index a9a105f0fda7..a37cad14fcbc 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp +++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp @@ -69,12 +69,6 @@ void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey, } } -void SubscriberReporter::removeConfig(const ConfigKey& configKey) { - VLOG("SubscriberReporter::removeConfig called."); - lock_guard<std::mutex> lock(mLock); - mIntentMap.erase(configKey); -} - void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey, const Subscription& subscription, const MetricDimensionKey& dimKey) const { diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h index 8ccc8ee626d4..087a1b84b91f 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ b/cmds/statsd/src/subscriber/SubscriberReporter.h @@ -59,9 +59,6 @@ public: */ void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId); - /** Remove all information stored by SubscriberReporter about the given config. */ - void removeConfig(const ConfigKey& configKey); - /** * Sends a broadcast via the intentSender previously stored for the * given (configKey, subscriberId) pair by setBroadcastSubscriber. diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 0bc3ebb81ce6..16b51d99535b 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -28,16 +28,16 @@ namespace statsd { #ifdef __ANDROID__ -const string kAndroid = "android"; const string kApp1 = "app1.sharing.1"; const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs. +const int kCallingUid = 0; // Randomly chosen void SendConfig(StatsService& service, const StatsdConfig& config) { string str; config.SerializeToString(&str); std::vector<uint8_t> configAsVec(str.begin(), str.end()); bool success; - service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str())); + service.addConfiguration(kConfigKey, configAsVec, kCallingUid); } ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp, @@ -50,7 +50,7 @@ ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestam ConfigMetricsReportList reports; reports.ParseFromArray(output.data(), output.size()); EXPECT_EQ(1, reports.reports_size()); - return reports.reports(0); + return reports.reports(kCallingUid); } StatsdConfig MakeConfig() { diff --git a/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp deleted file mode 100644 index 5b7a30d4a5aa..000000000000 --- a/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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. - */ - -#undef LOG_TAG -#define LOG_TAG "SurfaceflingerStatsPuller_test" - -#include "src/external/SurfaceflingerStatsPuller.h" -#include "statslog.h" - -#include <gtest/gtest.h> -#include <log/log.h> - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -class TestableSurfaceflingerStatsPuller : public SurfaceflingerStatsPuller { -public: - TestableSurfaceflingerStatsPuller(const int tagId) : SurfaceflingerStatsPuller(tagId){}; - - void injectStats(const StatsProvider& statsProvider) { - mStatsProvider = statsProvider; - } -}; - -class SurfaceflingerStatsPullerTest : public ::testing::Test { -public: - SurfaceflingerStatsPullerTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - } - - ~SurfaceflingerStatsPullerTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); - } -}; - -TEST_F(SurfaceflingerStatsPullerTest, pullGlobalStats) { - surfaceflinger::SFTimeStatsGlobalProto proto; - proto.set_total_frames(1); - proto.set_missed_frames(2); - proto.set_client_composition_frames(2); - proto.set_display_on_time(4); - - auto bucketOne = proto.add_present_to_present(); - bucketOne->set_time_millis(2); - bucketOne->set_frame_count(4); - auto bucketTwo = proto.add_present_to_present(); - bucketTwo->set_time_millis(4); - bucketTwo->set_frame_count(1); - auto bucketThree = proto.add_present_to_present(); - bucketThree->set_time_millis(1000); - bucketThree->set_frame_count(1); - static constexpr int64_t expectedAnimationMillis = 12; - TestableSurfaceflingerStatsPuller puller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); - - puller.injectStats([&] { - return proto.SerializeAsString(); - }); - puller.ForceClearCache(); - vector<std::shared_ptr<LogEvent>> outData; - puller.Pull(&outData); - - ASSERT_EQ(1, outData.size()); - EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, outData[0]->GetTagId()); - EXPECT_EQ(proto.total_frames(), outData[0]->getValues()[0].mValue.long_value); - EXPECT_EQ(proto.missed_frames(), outData[0]->getValues()[1].mValue.long_value); - EXPECT_EQ(proto.client_composition_frames(), outData[0]->getValues()[2].mValue.long_value); - EXPECT_EQ(proto.display_on_time(), outData[0]->getValues()[3].mValue.long_value); - EXPECT_EQ(expectedAnimationMillis, outData[0]->getValues()[4].mValue.long_value); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a4f6f57c097e..b82a67556fc0 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3349,8 +3349,8 @@ public final class ActivityThread extends ClientTransactionHandler { } @Override - public void handleStartActivity(ActivityClientRecord r, - PendingTransactionActions pendingActions) { + public void handleStartActivity(IBinder token, PendingTransactionActions pendingActions) { + final ActivityClientRecord r = mActivities.get(token); final Activity activity = r.activity; if (r.activity == null) { // TODO(lifecycler): What do we do in this case? @@ -3364,6 +3364,8 @@ public final class ActivityThread extends ClientTransactionHandler { return; } + unscheduleGcIdler(); + // Start activity.performStart("handleStartActivity"); r.setState(ON_START); @@ -3400,6 +3402,9 @@ public final class ActivityThread extends ClientTransactionHandler { + " did not call through to super.onPostCreate()"); } } + + updateVisibility(r, true /* show */); + mSomeActivitiesChanged = true; } /** @@ -4660,8 +4665,8 @@ public final class ActivityThread extends ClientTransactionHandler { @UnsupportedAppUsage final void performStopActivity(IBinder token, boolean saveState, String reason) { ActivityClientRecord r = mActivities.get(token); - performStopActivityInner(r, null /* stopInfo */, false /* keepShown */, saveState, - false /* finalStateRequest */, reason); + performStopActivityInner(r, null /* stopInfo */, saveState, false /* finalStateRequest */, + reason); } private static final class ProviderRefCount { @@ -4687,25 +4692,19 @@ public final class ActivityThread extends ClientTransactionHandler { } /** - * Core implementation of stopping an activity. Note this is a little - * tricky because the server's meaning of stop is slightly different - * than our client -- for the server, stop means to save state and give - * it the result when it is done, but the window may still be visible. - * For the client, we want to call onStop()/onStart() to indicate when - * the activity's UI visibility changes. + * Core implementation of stopping an activity. * @param r Target activity client record. * @param info Action that will report activity stop to server. - * @param keepShown Flag indicating whether the activity is still shown. * @param saveState Flag indicating whether the activity state should be saved. * @param finalStateRequest Flag indicating if this call is handling final lifecycle state * request for a transaction. * @param reason Reason for performing this operation. */ - private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown, + private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean saveState, boolean finalStateRequest, String reason) { if (localLOGV) Slog.v(TAG, "Performing stop of " + r); if (r != null) { - if (!keepShown && r.stopped) { + if (r.stopped) { if (r.activity.mFinished) { // If we are finishing, we won't call onResume() in certain // cases. So here we likewise don't want to call onStop() @@ -4740,9 +4739,7 @@ public final class ActivityThread extends ClientTransactionHandler { } } - if (!keepShown) { - callActivityOnStop(r, saveState, reason); - } + callActivityOnStop(r, saveState, reason); } } @@ -4810,20 +4807,19 @@ public final class ActivityThread extends ClientTransactionHandler { } @Override - public void handleStopActivity(IBinder token, boolean show, int configChanges, + public void handleStopActivity(IBinder token, int configChanges, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { final ActivityClientRecord r = mActivities.get(token); r.activity.mConfigChangeFlags |= configChanges; final StopInfo stopInfo = new StopInfo(); - performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest, + performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest, reason); if (localLOGV) Slog.v( - TAG, "Finishing stop of " + r + ": show=" + show - + " win=" + r.window); + TAG, "Finishing stop of " + r + ": win=" + r.window); - updateVisibility(r, show); + updateVisibility(r, false); // Make sure any pending writes are now committed. if (!r.isPreHoneycomb()) { @@ -4859,34 +4855,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - @Override - public void handleWindowVisibility(IBinder token, boolean show) { - ActivityClientRecord r = mActivities.get(token); - - if (r == null) { - Log.w(TAG, "handleWindowVisibility: no activity for token " + token); - return; - } - - if (!show && !r.stopped) { - performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */, - false /* finalStateRequest */, "handleWindowVisibility"); - } else if (show && r.getLifecycleState() == ON_STOP) { - // If we are getting ready to gc after going to the background, well - // we are back active so skip it. - unscheduleGcIdler(); - - r.activity.performRestart(true /* start */, "handleWindowVisibility"); - r.setState(ON_START); - } - if (r.activity.mDecor != null) { - if (false) Slog.v( - TAG, "Handle window " + r + " visibility: " + show); - updateVisibility(r, show); - } - mSomeActivitiesChanged = true; - } - // TODO: This method should be changed to use {@link #performStopActivityInner} to perform to // stop operation on the activity to reduce code duplication and the chance of fixing a bug in // one place and missing the other. diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index f9a689a7e1de..d2235f10da99 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -119,7 +119,6 @@ public abstract class ClientTransactionHandler { /** * Stop the activity. * @param token Target activity token. - * @param show Flag indicating whether activity is still shown. * @param configChanges Activity configuration changes. * @param pendingActions Pending actions to be used on this or later stages of activity * transaction. @@ -127,7 +126,7 @@ public abstract class ClientTransactionHandler { * request for a transaction. * @param reason Reason for performing this operation. */ - public abstract void handleStopActivity(IBinder token, boolean show, int configChanges, + public abstract void handleStopActivity(IBinder token, int configChanges, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason); /** Report that activity was stopped to server. */ @@ -161,15 +160,12 @@ public abstract class ClientTransactionHandler { /** Request that an activity enter picture-in-picture. */ public abstract void handlePictureInPictureRequested(IBinder token); - /** Update window visibility. */ - public abstract void handleWindowVisibility(IBinder token, boolean show); - /** Perform activity launch. */ public abstract Activity handleLaunchActivity(ActivityThread.ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent); /** Perform activity start. */ - public abstract void handleStartActivity(ActivityThread.ActivityClientRecord r, + public abstract void handleStartActivity(IBinder token, PendingTransactionActions pendingActions); /** Get package info. */ diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 49c389a0c4a7..1278ff6817fd 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -16,7 +16,9 @@ package android.app; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -40,11 +42,13 @@ import android.os.Environment; import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.provider.BaseColumns; import android.provider.Downloads; import android.provider.MediaStore; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; +import android.util.LongSparseArray; import android.util.Pair; import java.io.File; @@ -1069,6 +1073,37 @@ public class DownloadManager { } /** + * Notify {@link DownloadManager} that the given {@link MediaStore} items + * were just deleted so that {@link DownloadManager} internal data + * structures can be cleaned up. + * + * @param idToMime map from {@link BaseColumns#_ID} to + * {@link ContentResolver#getType(Uri)}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) + public void onMediaStoreDownloadsDeleted(@NonNull LongSparseArray<String> idToMime) { + try (ContentProviderClient client = mResolver + .acquireUnstableContentProviderClient(mBaseUri)) { + final Bundle callExtras = new Bundle(); + final long[] ids = new long[idToMime.size()]; + final String[] mimeTypes = new String[idToMime.size()]; + for (int i = idToMime.size() - 1; i >= 0; --i) { + ids[i] = idToMime.keyAt(i); + mimeTypes[i] = idToMime.valueAt(i); + } + callExtras.putLongArray(android.provider.Downloads.EXTRA_IDS, ids); + callExtras.putStringArray(android.provider.Downloads.EXTRA_MIME_TYPES, + mimeTypes); + client.call(android.provider.Downloads.CALL_MEDIASTORE_DOWNLOADS_DELETED, + null, callExtras); + } catch (RemoteException e) { + // Should not happen + } + } + + /** * Enqueue a new download. The download will start automatically once the download manager is * ready to execute it and connectivity is available. * diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java index 4033aea32b55..7cdf85e0a6b8 100644 --- a/core/java/android/app/LocalActivityManager.java +++ b/core/java/android/app/LocalActivityManager.java @@ -177,7 +177,7 @@ public class LocalActivityManager { pendingActions = null; } - mActivityThread.handleStartActivity(clientRecord, pendingActions); + mActivityThread.handleStartActivity(r, pendingActions); r.curState = STARTED; if (desiredState == RESUMED) { diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 83d1de60cac7..84263749232d 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -157,11 +157,11 @@ public final class StatsManager { public void addConfig(long configKey, byte[] config) throws StatsUnavailableException { synchronized (sLock) { try { - IStatsd service = getIStatsdLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); // can throw IllegalArgumentException service.addConfiguration(configKey, config, mContext.getOpPackageName()); } catch (RemoteException e) { - Slog.e(TAG, "Failed to connect to statsd when adding configuration"); + Slog.e(TAG, "Failed to connect to statsmanager when adding configuration"); throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); @@ -194,10 +194,10 @@ public final class StatsManager { public void removeConfig(long configKey) throws StatsUnavailableException { synchronized (sLock) { try { - IStatsd service = getIStatsdLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); service.removeConfiguration(configKey, mContext.getOpPackageName()); } catch (RemoteException e) { - Slog.e(TAG, "Failed to connect to statsd when removing configuration"); + Slog.e(TAG, "Failed to connect to statsmanager when removing configuration"); throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); @@ -390,10 +390,10 @@ public final class StatsManager { public byte[] getReports(long configKey) throws StatsUnavailableException { synchronized (sLock) { try { - IStatsd service = getIStatsdLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); return service.getData(configKey, mContext.getOpPackageName()); } catch (RemoteException e) { - Slog.e(TAG, "Failed to connect to statsd when getting data"); + Slog.e(TAG, "Failed to connect to statsmanager when getting data"); throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); @@ -427,10 +427,10 @@ public final class StatsManager { public byte[] getStatsMetadata() throws StatsUnavailableException { synchronized (sLock) { try { - IStatsd service = getIStatsdLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); return service.getMetadata(mContext.getOpPackageName()); } catch (RemoteException e) { - Slog.e(TAG, "Failed to connect to statsd when getting metadata"); + Slog.e(TAG, "Failed to connect to statsmanager when getting metadata"); throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); @@ -462,21 +462,19 @@ public final class StatsManager { throws StatsUnavailableException { synchronized (sLock) { try { - IStatsd service = getIStatsdLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); if (service == null) { - if (DEBUG) { - Slog.d(TAG, "Failed to find statsd when getting experiment IDs"); - } - return new long[0]; + throw new StatsUnavailableException("Failed to find statsmanager when " + + "getting experiment IDs"); } return service.getRegisteredExperimentIds(); } catch (RemoteException e) { if (DEBUG) { Slog.d(TAG, - "Failed to connect to StatsCompanionService when getting " + "Failed to connect to StatsManagerService when getting " + "registered experiment IDs"); } - return new long[0]; + throw new StatsUnavailableException("could not connect", e); } } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index a1765c85d57b..078e4538c66b 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -153,6 +153,11 @@ public class StatusBarManager { */ public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_ROTATE_SUGGESTIONS; + /** + * disable flags to be applied when the device is sim-locked. + */ + private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND; + /** @hide */ public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0; /** @hide */ @@ -385,6 +390,30 @@ public class StatusBarManager { } /** + * Enable or disable expansion of the status bar. When the device is SIM-locked, the status + * bar should not be expandable. + * + * @param disabled If {@code true}, the status bar will be set to non-expandable. If + * {@code false}, re-enables expansion of the status bar. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR) + public void setDisabledForSimNetworkLock(boolean disabled) { + try { + final int userId = Binder.getCallingUserHandle().getIdentifier(); + final IStatusBarService svc = getService(); + if (svc != null) { + svc.disableForUser(disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE, + mToken, mContext.getPackageName(), userId); + } + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Get this app's currently requested disabled components * * @return a new DisableInfo diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index f3028a10bb10..2aac94c6f5da 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -88,7 +88,6 @@ import android.telephony.data.ApnSetting; import android.util.ArraySet; import android.util.Log; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; @@ -6757,6 +6756,34 @@ public class DevicePolicyManager { } /** + * @hide + * Privileged apps can use this method to find out if the device was provisioned as + * organization-owend device with a managed profile. + * + * This, together with checking whether the device has a device owner (by calling + * {@link #isDeviceManaged()}), could be used to learn whether the device is owned by an + * organization or an individual: + * If this method returns true OR {@link #isDeviceManaged()} returns true, then + * the device is owned by an organization. Otherwise, it's owned by an individual. + * + * @return {@code true} if the device was provisioned as organization-owned device, + * {@code false} otherwise. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public boolean isOrganizationOwnedDeviceWithManagedProfile() { + throwIfParentInstance("isOrganizationOwnedDeviceWithManagedProfile"); + if (mService != null) { + try { + return mService.isOrganizationOwnedDeviceWithManagedProfile(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Returns whether the specified package can read the device identifiers. * * @param packageName The package name of the app to check for device identifier access. @@ -11215,12 +11242,12 @@ public class DevicePolicyManager { * #setCrossProfilePackages(ComponentName, Set)}.</li> * <li>The default package names set by the OEM that are allowed to request user consent for * cross-profile communication without being explicitly enabled by the admin, via - * {@link R.array#cross_profile_apps}</li> + * {@link com.android.internal.R.array#cross_profile_apps}</li> * </ul> * * @return the combined set of whitelisted package names set via * {@link #setCrossProfilePackages(ComponentName, Set)} and - * {@link R.array#cross_profile_apps} + * {@link com.android.internal.R.array#cross_profile_apps} * * @hide */ diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index f299d456a18f..e6c89d9071e6 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -17,9 +17,12 @@ package android.app.admin; import android.annotation.UserIdInt; +import android.content.ComponentName; import android.content.Intent; +import android.os.UserHandle; import java.util.List; +import java.util.Set; /** * Device policy manager local system service interface. @@ -165,4 +168,23 @@ public abstract class DevicePolicyManagerInternal { * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead. */ protected abstract DeviceStateCache getDeviceStateCache(); + + /** + * Returns the combined set of the following: + * <ul> + * <li>The package names that the admin has previously set as allowed to request user consent + * for cross-profile communication, via {@link + * DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.</li> + * <li>The default package names that are allowed to request user consent for cross-profile + * communication without being explicitly enabled by the admin , via {@link + * DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}.</li> + * </ul> + * + * @return the combined set of whitelisted package names set via + * {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and + * {@link DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)} + * + * @hide + */ + public abstract List<String> getAllCrossProfilePackages(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 55dfe2fec4c9..3eec46bd010e 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -156,6 +156,7 @@ interface IDevicePolicyManager { void setProfileName(in ComponentName who, String profileName); void clearProfileOwner(in ComponentName who); boolean hasUserSetupCompleted(); + boolean isOrganizationOwnedDeviceWithManagedProfile(); boolean checkDeviceIdentifierAccess(in String packageName, int pid, int uid); diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java index 115d1ececc0e..4fbe02b9cf76 100644 --- a/core/java/android/app/servertransaction/WindowVisibilityItem.java +++ b/core/java/android/app/servertransaction/StartActivityItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 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. @@ -24,41 +24,44 @@ import android.os.Parcel; import android.os.Trace; /** - * Window visibility change message. + * Request to move an activity to started and visible state. * @hide */ -public class WindowVisibilityItem extends ClientTransactionItem { +public class StartActivityItem extends ActivityLifecycleItem { - private boolean mShowWindow; + private static final String TAG = "StartActivityItem"; @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, - mShowWindow ? "activityShowWindow" : "activityHideWindow"); - client.handleWindowVisibility(token, mShowWindow); + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "startActivityItem"); + client.handleStartActivity(token, pendingActions); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } + @Override + public int getTargetState() { + return ON_START; + } + // ObjectPoolItem implementation - private WindowVisibilityItem() {} + private StartActivityItem() {} /** Obtain an instance initialized with provided params. */ - public static WindowVisibilityItem obtain(boolean showWindow) { - WindowVisibilityItem instance = ObjectPool.obtain(WindowVisibilityItem.class); + public static StartActivityItem obtain() { + StartActivityItem instance = ObjectPool.obtain(StartActivityItem.class); if (instance == null) { - instance = new WindowVisibilityItem(); + instance = new StartActivityItem(); } - instance.mShowWindow = showWindow; return instance; } @Override public void recycle() { - mShowWindow = false; + super.recycle(); ObjectPool.recycle(this); } @@ -68,24 +71,24 @@ public class WindowVisibilityItem extends ClientTransactionItem { /** Write to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeBoolean(mShowWindow); + // Empty } /** Read from Parcel. */ - private WindowVisibilityItem(Parcel in) { - mShowWindow = in.readBoolean(); + private StartActivityItem(Parcel in) { + // Empty } - public static final @android.annotation.NonNull Creator<WindowVisibilityItem> CREATOR = - new Creator<WindowVisibilityItem>() { - public WindowVisibilityItem createFromParcel(Parcel in) { - return new WindowVisibilityItem(in); - } + public static final @android.annotation.NonNull Creator<StartActivityItem> CREATOR = + new Creator<StartActivityItem>() { + public StartActivityItem createFromParcel(Parcel in) { + return new StartActivityItem(in); + } - public WindowVisibilityItem[] newArray(int size) { - return new WindowVisibilityItem[size]; - } - }; + public StartActivityItem[] newArray(int size) { + return new StartActivityItem[size]; + } + }; @Override public boolean equals(Object o) { @@ -95,17 +98,17 @@ public class WindowVisibilityItem extends ClientTransactionItem { if (o == null || getClass() != o.getClass()) { return false; } - final WindowVisibilityItem other = (WindowVisibilityItem) o; - return mShowWindow == other.mShowWindow; + return true; } @Override public int hashCode() { - return 17 + 31 * (mShowWindow ? 1 : 0); + return 17; } @Override public String toString() { - return "WindowVisibilityItem{showWindow=" + mShowWindow + "}"; + return "StartActivityItem{}"; } } + diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index 63efa6fe7c17..8668bd49c8f5 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -31,14 +31,13 @@ public class StopActivityItem extends ActivityLifecycleItem { private static final String TAG = "StopActivityItem"; - private boolean mShowWindow; private int mConfigChanges; @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop"); - client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions, + client.handleStopActivity(token, mConfigChanges, pendingActions, true /* finalStateRequest */, "STOP_ACTIVITY_ITEM"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -59,13 +58,15 @@ public class StopActivityItem extends ActivityLifecycleItem { private StopActivityItem() {} - /** Obtain an instance initialized with provided params. */ - public static StopActivityItem obtain(boolean showWindow, int configChanges) { + /** + * Obtain an instance initialized with provided params. + * @param configChanges Configuration pieces that changed. + */ + public static StopActivityItem obtain(int configChanges) { StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class); if (instance == null) { instance = new StopActivityItem(); } - instance.mShowWindow = showWindow; instance.mConfigChanges = configChanges; return instance; @@ -74,7 +75,6 @@ public class StopActivityItem extends ActivityLifecycleItem { @Override public void recycle() { super.recycle(); - mShowWindow = false; mConfigChanges = 0; ObjectPool.recycle(this); } @@ -85,13 +85,11 @@ public class StopActivityItem extends ActivityLifecycleItem { /** Write to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeBoolean(mShowWindow); dest.writeInt(mConfigChanges); } /** Read from Parcel. */ private StopActivityItem(Parcel in) { - mShowWindow = in.readBoolean(); mConfigChanges = in.readInt(); } @@ -115,20 +113,18 @@ public class StopActivityItem extends ActivityLifecycleItem { return false; } final StopActivityItem other = (StopActivityItem) o; - return mShowWindow == other.mShowWindow && mConfigChanges == other.mConfigChanges; + return mConfigChanges == other.mConfigChanges; } @Override public int hashCode() { int result = 17; - result = 31 * result + (mShowWindow ? 1 : 0); result = 31 * result + mConfigChanges; return result; } @Override public String toString() { - return "StopActivityItem{showWindow=" + mShowWindow + ",configChanges=" + mConfigChanges - + "}"; + return "StopActivityItem{configChanges=" + mConfigChanges + "}"; } } diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index 20e0da3ead8a..17fcda587322 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -218,7 +218,7 @@ public class TransactionExecutor { null /* customIntent */); break; case ON_START: - mTransactionHandler.handleStartActivity(r, mPendingActions); + mTransactionHandler.handleStartActivity(r.token, mPendingActions); break; case ON_RESUME: mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */, @@ -230,8 +230,8 @@ public class TransactionExecutor { "LIFECYCLER_PAUSE_ACTIVITY"); break; case ON_STOP: - mTransactionHandler.handleStopActivity(r.token, false /* show */, - 0 /* configChanges */, mPendingActions, false /* finalStateRequest */, + mTransactionHandler.handleStopActivity(r.token, 0 /* configChanges */, + mPendingActions, false /* finalStateRequest */, "LIFECYCLER_STOP_ACTIVITY"); break; case ON_DESTROY: diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 0ea8c3c159fa..6df92a78cc9f 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -183,8 +183,7 @@ public class TransactionExecutorHelper { lifecycleItem = PauseActivityItem.obtain(); break; case ON_STOP: - lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(), - 0 /* configChanges */); + lifecycleItem = StopActivityItem.obtain(0 /* configChanges */); break; default: lifecycleItem = ResumeActivityItem.obtain(false /* isForward */); diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java index 55f92be14cd0..50de73855511 100644 --- a/core/java/android/app/timedetector/ManualTimeSuggestion.java +++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import java.util.ArrayList; import java.util.Arrays; diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java index 4c55ba12d881..17e9c5a79fa5 100644 --- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java +++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import java.util.ArrayList; import java.util.Arrays; diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java index 4a89a1245473..479e4b4efb4c 100644 --- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java +++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import java.util.ArrayList; import java.util.Collections; diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index af9ece00f491..54dd1bed5361 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -24,8 +24,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.SystemClock; +import android.os.TimestampedValue; import android.util.Log; -import android.util.TimestampedValue; /** * The interface through which system components can send signals to the TimeDetectorService. diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index b1b6f0d61f9f..cb1f05573d93 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2670,6 +2670,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); return true; + } else if (profile == BluetoothProfile.PBAP) { + BluetoothPbap pbap = new BluetoothPbap(context, listener); + return true; } else if (profile == BluetoothProfile.HEALTH) { Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); return false; @@ -2742,6 +2745,10 @@ public final class BluetoothAdapter { BluetoothPan pan = (BluetoothPan) proxy; pan.close(); break; + case BluetoothProfile.PBAP: + BluetoothPbap pbap = (BluetoothPbap) proxy; + pbap.close(); + break; case BluetoothProfile.GATT: BluetoothGatt gatt = (BluetoothGatt) proxy; gatt.close(); diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 393d48891d64..85826fd4e669 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -27,6 +27,7 @@ import static android.os.Trace.TRACE_TAG_DATABASE; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.AppOpsManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.PackageManager; @@ -942,7 +943,18 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return null; } - /** {@hide} */ + /** + * Return the package name of the caller that initiated the request being + * processed on the current thread. The returned package will have + * <em>not</em> been verified to belong to the calling UID. Returns + * {@code null} if not currently processing a request. + * <p> + * This will always return {@code null} when processing + * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests. + * + * @see Binder#getCallingUid() + * @see Context#grantUriPermission(String, Uri, int) + */ public final @Nullable String getCallingPackageUnchecked() { final Pair<String, String> pkg = mCallingPackage.get(); if (pkg != null) { @@ -952,7 +964,14 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return null; } - /** {@hide} */ + /** + * Called whenever the value of {@link #getCallingPackage()} changes, giving + * the provider an opportunity to invalidate any security related caching it + * may be performing. + * <p> + * This typically happens when a {@link ContentProvider} makes a nested call + * back into itself when already processing a call from a remote process. + */ public void onCallingPackageChanged() { } @@ -1390,8 +1409,11 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * @param uri The URI to query. This will be the full URI sent by the client. * @param projection The list of columns to put into the cursor. * If {@code null} provide a default set of columns. - * @param queryArgs A Bundle containing all additional information necessary for the query. - * Values in the Bundle may include SQL style arguments. + * @param queryArgs A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. * @param cancellationSignal A signal to cancel the operation in progress, * or {@code null}. * @return a Cursor or {@code null}. @@ -1525,8 +1547,24 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return false; } - /** {@hide} */ + /** + * Perform a detailed internal check on a {@link Uri} to determine if a UID + * is able to access it with specific mode flags. + * <p> + * This method is typically used when the provider implements more dynamic + * access controls that cannot be expressed with {@code <path-permission>} + * style static rules. + * + * @param uri the {@link Uri} to perform an access check on. + * @param uid the UID to check the permission for. + * @param modeFlags the access flags to use for the access check, such as + * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}. + * @return {@link PackageManager#PERMISSION_GRANTED} if access is allowed, + * otherwise {@link PackageManager#PERMISSION_DENIED}. + * @hide + */ @Override + @SystemApi public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) { return PackageManager.PERMISSION_DENIED; } @@ -1574,9 +1612,14 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * * @param uri The content:// URI of the insertion request. * @param values A set of column_name/value pairs to add to the database. - * @param extras A Bundle containing all additional information necessary - * for the insert. + * @param extras A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. * @return The URI for the newly inserted item. + * @throws IllegalArgumentException if the provider doesn't support one of + * the requested Bundle arguments. */ @Override public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values, @@ -1653,10 +1696,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * * @param uri The full URI to query, including a row ID (if a specific * record is requested). - * @param extras A Bundle containing all additional information necessary - * for the delete. Values in the Bundle may include SQL style - * arguments. - * @return The number of rows affected. + * @param extras A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. + * @throws IllegalArgumentException if the provider doesn't support one of + * the requested Bundle arguments. * @throws SQLException */ @Override @@ -1699,10 +1745,14 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * @param uri The URI to query. This can potentially have a record ID if * this is an update request for a specific record. * @param values A set of column_name/value pairs to update in the database. - * @param extras A Bundle containing all additional information necessary - * for the update. Values in the Bundle may include SQL style - * arguments. + * @param extras A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. * @return the number of rows affected. + * @throws IllegalArgumentException if the provider doesn't support one of + * the requested Bundle arguments. */ @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index 93f42879d946..494d2aeaf42a 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -18,7 +18,7 @@ package android.content; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java index 11dda83a6e6c..4fb1ddb958d5 100644 --- a/core/java/android/content/ContentProviderResult.java +++ b/core/java/android/content/ContentProviderResult.java @@ -36,7 +36,7 @@ public class ContentProviderResult implements Parcelable { public final @Nullable Uri uri; public final @Nullable Integer count; public final @Nullable Bundle extras; - public final @Nullable Exception exception; + public final @Nullable Throwable exception; public ContentProviderResult(@NonNull Uri uri) { this(Objects.requireNonNull(uri), null, null, null); @@ -50,12 +50,12 @@ public class ContentProviderResult implements Parcelable { this(null, null, Objects.requireNonNull(extras), null); } - public ContentProviderResult(@NonNull Exception exception) { + public ContentProviderResult(@NonNull Throwable exception) { this(null, null, null, exception); } /** {@hide} */ - public ContentProviderResult(Uri uri, Integer count, Bundle extras, Exception exception) { + public ContentProviderResult(Uri uri, Integer count, Bundle extras, Throwable exception) { this.uri = uri; this.count = count; this.extras = extras; @@ -79,7 +79,7 @@ public class ContentProviderResult implements Parcelable { extras = null; } if (source.readInt() != 0) { - exception = (Exception) ParcelableException.readFromParcel(source); + exception = ParcelableException.readFromParcel(source); } else { exception = null; } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 1d3c6505f677..6cd1cd3354c2 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -984,7 +984,11 @@ public abstract class ContentResolver implements ContentInterface { * retrieve. * @param projection A list of which columns to return. Passing null will * return all columns, which is inefficient. - * @param queryArgs A Bundle containing any arguments to the query. + * @param queryArgs A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. @@ -1925,9 +1929,15 @@ public abstract class ContentResolver implements ContentInterface { * @param url The URL of the table to insert into. * @param values The initial values for the newly inserted row. The key is the column name for * the field. Passing an empty ContentValues will create an empty row. - * @param extras A Bundle containing all additional information necessary for the insert. + * @param extras A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. * @return the URL of the newly created row. May return <code>null</code> if the underlying * content provider returns <code>null</code>, or if it crashes. + * @throws IllegalArgumentException if the provider doesn't support one of + * the requested Bundle arguments. */ @Override public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url, @@ -2061,9 +2071,14 @@ public abstract class ContentResolver implements ContentInterface { * If the content provider supports transactions, the deletion will be atomic. * * @param url The URL of the row to delete. - * @param extras A Bundle containing all additional information necessary for the delete. - * Values in the Bundle may include SQL style arguments. + * @param extras A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. * @return The number of rows deleted. + * @throws IllegalArgumentException if the provider doesn't support one of + * the requested Bundle arguments. */ @Override public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable Bundle extras) { @@ -2121,10 +2136,15 @@ public abstract class ContentResolver implements ContentInterface { * @param uri The URI to modify. * @param values The new field values. The key is the column name for the field. A null value will remove an existing field value. - * @param extras A Bundle containing all additional information necessary for the update. - * Values in the Bundle may include SQL style arguments. + * @param extras A Bundle containing additional information necessary for + * the operation. Arguments may include SQL style arguments, such + * as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that + * the documentation for each individual provider will indicate + * which arguments they support. * @return the number of rows updated. * @throws NullPointerException if uri or values are null + * @throws IllegalArgumentException if the provider doesn't support one of + * the requested Bundle arguments. */ @Override public final int update(@RequiresPermission.Write @NonNull Uri uri, @@ -3851,15 +3871,47 @@ public abstract class ContentResolver implements ContentInterface { } } + /** + * Decode a path generated by {@link #encodeToFile(Uri)} back into + * the original {@link Uri}. + * <p> + * This is used to offer a way to intercept filesystem calls in + * {@link ContentProvider} unaware code and redirect them to a + * {@link ContentProvider} when they attempt to use {@code _DATA} columns + * that are otherwise deprecated. + * + * @hide + */ + @SystemApi + public static @NonNull Uri decodeFromFile(@NonNull File file) { + return translateDeprecatedDataPath(file.getAbsolutePath()); + } + + /** + * Encode a {@link Uri} into an opaque filesystem path which can then be + * resurrected by {@link #decodeFromFile(File)}. + * <p> + * This is used to offer a way to intercept filesystem calls in + * {@link ContentProvider} unaware code and redirect them to a + * {@link ContentProvider} when they attempt to use {@code _DATA} columns + * that are otherwise deprecated. + * + * @hide + */ + @SystemApi + public static @NonNull File encodeToFile(@NonNull Uri uri) { + return new File(translateDeprecatedDataPath(uri)); + } + /** {@hide} */ - public static Uri translateDeprecatedDataPath(String path) { + public static @NonNull Uri translateDeprecatedDataPath(@NonNull String path) { final String ssp = "//" + path.substring(DEPRECATE_DATA_PREFIX.length()); return Uri.parse(new Uri.Builder().scheme(SCHEME_CONTENT) .encodedOpaquePart(ssp).build().toString()); } /** {@hide} */ - public static String translateDeprecatedDataPath(Uri uri) { + public static @NonNull String translateDeprecatedDataPath(@NonNull Uri uri) { return DEPRECATE_DATA_PREFIX + uri.getEncodedSchemeSpecificPart().substring(2); } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a28868ef38f8..5cb2907dfba4 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3391,6 +3391,7 @@ public abstract class Context { TELEPHONY_SUBSCRIPTION_SERVICE, CARRIER_CONFIG_SERVICE, EUICC_SERVICE, + MMS_SERVICE, TELECOM_SERVICE, CLIPBOARD_SERVICE, INPUT_METHOD_SERVICE, @@ -3587,6 +3588,8 @@ public abstract class Context { * @see android.telephony.CarrierConfigManager * @see #EUICC_SERVICE * @see android.telephony.euicc.EuiccManager + * @see #MMS_SERVICE + * @see android.telephony.MmsManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager * @see #UI_MODE_SERVICE @@ -4016,6 +4019,7 @@ public abstract class Context { * @see android.net.wifi.WifiCondManager * @hide */ + @SystemApi public static final String WIFI_COND_SERVICE = "wificond"; /** @@ -4262,6 +4266,15 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.MmsManager} to send/receive MMS messages. + * + * @see #getSystemService(String) + * @see android.telephony.MmsManager + */ + public static final String MMS_SERVICE = "mms"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.content.ClipboardManager} for accessing and modifying * the contents of the global clipboard. * diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index e897b917fcc2..abf32c5e0840 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -30,6 +30,7 @@ import com.android.internal.R; import com.android.internal.util.UserIcons; import java.util.List; +import java.util.Set; /** * Class for handling cross profile operations. Apps can use this class to interact with its @@ -169,6 +170,62 @@ public class CrossProfileApps { } } + /** + * Returns whether the calling package can request to interact across profiles. + * + * <p>The package's current ability to interact across profiles can be checked with + * {@link #canInteractAcrossProfiles()}. + * + * <p>Specifically, returns whether the following are all true: + * <ul> + * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> + * <li>The calling app has requested</li> + * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. + * <li>The calling package has either been whitelisted by default by the OEM or has been + * explicitly whitelisted by the admin via + * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. + * </li> + * </ul> + * + * @return true if the calling package can request to interact across profiles. + */ + public boolean canRequestInteractAcrossProfiles() { + try { + return mService.canRequestInteractAcrossProfiles(mContext.getPackageName()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Returns whether the calling package can interact across profiles. + * + * <p>The package's current ability to request to interact across profiles can be checked with + * {@link #canRequestInteractAcrossProfiles()}. + * + * <p>Specifically, returns whether the following are all true: + * <ul> + * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> + * <li>The user has previously consented to cross-profile communication for the calling + * package.</li> + * <li>The calling package has either been whitelisted by default by the OEM or has been + * explicitly whitelisted by the admin via + * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. + * </li> + * </ul> + * + * @return true if the calling package can interact across profiles. + * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the + * calling UID. + */ + public boolean canInteractAcrossProfiles() { + try { + return mService.canInteractAcrossProfiles(mContext.getPackageName()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index d2d66cbda610..c5db0ccebf52 100644 --- a/core/java/android/content/pm/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -30,4 +30,6 @@ interface ICrossProfileApps { void startActivityAsUser(in IApplicationThread caller, in String callingPackage, in ComponentName component, int userId, boolean launchMainActivity); List<UserHandle> getTargetUserProfiles(in String callingPackage); + boolean canInteractAcrossProfiles(in String callingPackage); + boolean canRequestInteractAcrossProfiles(in String callingPackage); }
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b85c58ad0e2f..4bfc40e698b9 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2323,6 +2323,13 @@ public abstract class PackageManager { public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * has a telephony radio that support data. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data"; + + /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device supports telephony carrier restriction mechanism. * diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index ac2e373f000d..6639b3d5c0b5 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -18,7 +18,7 @@ package android.content.pm.parsing; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java index 595685729a0c..f04a30ce4239 100644 --- a/core/java/android/content/pm/parsing/ComponentParseUtils.java +++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java @@ -27,8 +27,8 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityTaskManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index bba14c39de72..36ec67ee1471 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -35,8 +35,8 @@ import com.android.internal.util.ArrayUtils; import libcore.util.EmptyArray; import java.util.Arrays; +import java.util.Collection; import java.util.Iterator; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -56,7 +56,7 @@ public class SQLiteQueryBuilder { "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL|GROUP_CONCAT)\\((.+)\\)"); private Map<String, String> mProjectionMap = null; - private List<Pattern> mProjectionGreylist = null; + private Collection<Pattern> mProjectionGreylist = null; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private String mTables = ""; @@ -196,20 +196,16 @@ public class SQLiteQueryBuilder { * Sets a projection greylist of columns that will be allowed through, even * when {@link #setStrict(boolean)} is enabled. This provides a way for * abusive custom columns like {@code COUNT(*)} to continue working. - * - * @hide */ - public void setProjectionGreylist(@Nullable List<Pattern> projectionGreylist) { + public void setProjectionGreylist(@Nullable Collection<Pattern> projectionGreylist) { mProjectionGreylist = projectionGreylist; } /** * Gets the projection greylist for the query, as last configured by - * {@link #setProjectionGreylist(List)}. - * - * @hide + * {@link #setProjectionGreylist}. */ - public @Nullable List<Pattern> getProjectionGreylist() { + public @Nullable Collection<Pattern> getProjectionGreylist() { return mProjectionGreylist; } @@ -244,25 +240,27 @@ public class SQLiteQueryBuilder { } /** - * When set, the selection is verified against malicious arguments. - * When using this class to create a statement using + * When set, the selection is verified against malicious arguments. When + * using this class to create a statement using * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)}, - * non-numeric limits will raise an exception. If a projection map is specified, fields - * not in that map will be ignored. - * If this class is used to execute the statement directly using + * non-numeric limits will raise an exception. If a projection map is + * specified, fields not in that map will be ignored. If this class is used + * to execute the statement directly using * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)} * or * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)}, - * additionally also parenthesis escaping selection are caught. - * - * To summarize: To get maximum protection against malicious third party apps (for example - * content provider consumers), make sure to do the following: + * additionally also parenthesis escaping selection are caught. To + * summarize: To get maximum protection against malicious third party apps + * (for example content provider consumers), make sure to do the following: * <ul> * <li>Set this value to true</li> * <li>Use a projection map</li> - * <li>Use one of the query overloads instead of getting the statement as a sql string</li> + * <li>Use one of the query overloads instead of getting the statement as a + * sql string</li> * </ul> - * By default, this value is false. + * <p> + * This feature is disabled by default on each newly constructed + * {@link SQLiteQueryBuilder} and needs to be manually enabled. */ public void setStrict(boolean strict) { if (strict) { @@ -287,6 +285,9 @@ public class SQLiteQueryBuilder { * This enforcement applies to {@link #insert}, {@link #query}, and * {@link #update} operations. Any enforcement failures will throw an * {@link IllegalArgumentException}. + * <p> + * This feature is disabled by default on each newly constructed + * {@link SQLiteQueryBuilder} and needs to be manually enabled. */ public void setStrictColumns(boolean strictColumns) { if (strictColumns) { @@ -323,6 +324,9 @@ public class SQLiteQueryBuilder { * {@link #delete} operations. This enforcement does not apply to trusted * inputs, such as those provided by {@link #appendWhere}. Any enforcement * failures will throw an {@link IllegalArgumentException}. + * <p> + * This feature is disabled by default on each newly constructed + * {@link SQLiteQueryBuilder} and needs to be manually enabled. */ public void setStrictGrammar(boolean strictGrammar) { if (strictGrammar) { diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 638d81b2f635..6e1987c47d20 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -90,4 +90,11 @@ interface IInputManager { /** Create an input monitor for gestures. */ InputMonitor monitorGestureInput(String name, int displayId); + + // Add a runtime association between the input port and the display port. This overrides any + // static associations. + void addPortAssociation(in String inputPort, int displayPort); + // Remove the runtime association between the input port and the display port. Any existing + // static association for the cleared input port will be restored. + void removePortAssociation(in String inputPort); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 8d32db013fb3..83f01a5dca35 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -17,6 +17,7 @@ package android.hardware.input; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; @@ -963,6 +964,41 @@ public final class InputManager { } } + /** + * Add a runtime association between the input port and the display port. This overrides any + * static associations. + * @param inputPort The port of the input device. + * @param displayPort The physical port of the associated display. + * <p> + * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. + * </p> + * @hide + */ + public void addPortAssociation(@NonNull String inputPort, int displayPort) { + try { + mIm.addPortAssociation(inputPort, displayPort); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Remove the runtime association between the input port and the display port. Any existing + * static association for the cleared input port will be restored. + * @param inputPort The port of the input device to be cleared. + * <p> + * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. + * </p> + * @hide + */ + public void removePortAssociation(@NonNull String inputPort) { + try { + mIm.removePortAssociation(inputPort); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 73b9d1739061..67fdda37ed3b 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -91,7 +91,7 @@ public class UsbManager { * * {@hide} */ - @UnsupportedAppUsage + @SystemApi public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE"; @@ -164,7 +164,7 @@ public class UsbManager { * * {@hide} */ - @UnsupportedAppUsage + @SystemApi public static final String USB_CONNECTED = "connected"; /** @@ -181,6 +181,7 @@ public class UsbManager { * * {@hide} */ + @SystemApi public static final String USB_CONFIGURED = "configured"; /** @@ -217,6 +218,7 @@ public class UsbManager { * * {@hide} */ + @SystemApi public static final String USB_FUNCTION_RNDIS = "rndis"; /** @@ -319,6 +321,7 @@ public class UsbManager { * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)} * {@hide} */ + @SystemApi public static final long FUNCTION_NONE = 0; /** @@ -337,6 +340,7 @@ public class UsbManager { * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)} * {@hide} */ + @SystemApi public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS; /** @@ -698,6 +702,8 @@ public class UsbManager { * * {@hide} */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long functions) { try { mService.setCurrentFunctions(functions); @@ -737,6 +743,8 @@ public class UsbManager { * * {@hide} */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_USB) public long getCurrentFunctions() { try { return mService.getCurrentFunctions(); diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java new file mode 100644 index 000000000000..6afdb5ef1b16 --- /dev/null +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -0,0 +1,242 @@ +/* + * 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; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.PersistableBundle; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * Class that provides utilities for collecting network connectivity diagnostics information. + * Connectivity information is made available through triggerable diagnostics tools and by listening + * to System validations. Some diagnostics information may be permissions-restricted. + * + * <p>ConnectivityDiagnosticsManager is intended for use by applications offering network + * connectivity on a user device. These tools will provide several mechanisms for these applications + * to be alerted to network conditions as well as diagnose potential network issues themselves. + * + * <p>The primary responsibilities of this class are to: + * + * <ul> + * <li>Allow permissioned applications to register and unregister callbacks for network event + * notifications + * <li>Invoke callbacks for network event notifications, including: + * <ul> + * <li>Network validations + * <li>Data stalls + * <li>Connectivity reports from applications + * </ul> + * </ul> + */ +public class ConnectivityDiagnosticsManager { + public static final int DETECTION_METHOD_DNS_EVENTS = 1; + public static final int DETECTION_METHOD_TCP_METRICS = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"DETECTION_METHOD_"}, + value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS}) + public @interface DetectionMethod {} + + /** @hide */ + public ConnectivityDiagnosticsManager() {} + + /** Class that includes connectivity information for a specific Network at a specific time. */ + public static class ConnectivityReport { + /** The Network for which this ConnectivityReport applied */ + @NonNull public final Network network; + + /** + * The timestamp for the report. The timestamp is taken from {@link + * System#currentTimeMillis}. + */ + public final long reportTimestamp; + + /** LinkProperties available on the Network at the reported timestamp */ + @NonNull public final LinkProperties linkProperties; + + /** NetworkCapabilities available on the Network at the reported timestamp */ + @NonNull public final NetworkCapabilities networkCapabilities; + + /** PersistableBundle that may contain additional info about the report */ + @NonNull public final PersistableBundle additionalInfo; + + /** + * Constructor for ConnectivityReport. + * + * <p>Apps should obtain instances through {@link + * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own + * instances (unless for testing purposes). + * + * @param network The Network for which this ConnectivityReport applies + * @param reportTimestamp The timestamp for the report + * @param linkProperties The LinkProperties available on network at reportTimestamp + * @param networkCapabilities The NetworkCapabilities available on network at + * reportTimestamp + * @param additionalInfo A PersistableBundle that may contain additional info about the + * report + */ + public ConnectivityReport( + @NonNull Network network, + long reportTimestamp, + @NonNull LinkProperties linkProperties, + @NonNull NetworkCapabilities networkCapabilities, + @NonNull PersistableBundle additionalInfo) { + this.network = network; + this.reportTimestamp = reportTimestamp; + this.linkProperties = linkProperties; + this.networkCapabilities = networkCapabilities; + this.additionalInfo = additionalInfo; + } + } + + /** Class that includes information for a suspected data stall on a specific Network */ + public static class DataStallReport { + /** The Network for which this DataStallReport applied */ + @NonNull public final Network network; + + /** + * The timestamp for the report. The timestamp is taken from {@link + * System#currentTimeMillis}. + */ + public final long reportTimestamp; + + /** The detection method used to identify the suspected data stall */ + @DetectionMethod public final int detectionMethod; + + /** PersistableBundle that may contain additional information on the suspected data stall */ + @NonNull public final PersistableBundle stallDetails; + + /** + * Constructor for DataStallReport. + * + * <p>Apps should obtain instances through {@link + * ConnectivityDiagnosticsCallback#onDataStallSuspected} instead of instantiating their own + * instances (unless for testing purposes). + * + * @param network The Network for which this DataStallReport applies + * @param reportTimestamp The timestamp for the report + * @param detectionMethod The detection method used to identify this data stall + * @param stallDetails A PersistableBundle that may contain additional info about the report + */ + public DataStallReport( + @NonNull Network network, + long reportTimestamp, + @DetectionMethod int detectionMethod, + @NonNull PersistableBundle stallDetails) { + this.network = network; + this.reportTimestamp = reportTimestamp; + this.detectionMethod = detectionMethod; + this.stallDetails = stallDetails; + } + } + + /** + * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about + * network connectivity events. Must be extended by applications wanting notifications. + */ + public abstract static class ConnectivityDiagnosticsCallback { + /** + * Called when the platform completes a data connectivity check. This will also be invoked + * upon registration with the latest report. + * + * <p>The Network specified in the ConnectivityReport may not be active any more when this + * method is invoked. + * + * @param report The ConnectivityReport containing information about a connectivity check + */ + public void onConnectivityReport(@NonNull ConnectivityReport report) {} + + /** + * Called when the platform suspects a data stall on some Network. + * + * <p>The Network specified in the DataStallReport may not be active any more when this + * method is invoked. + * + * @param report The DataStallReport containing information about the suspected data stall + */ + public void onDataStallSuspected(@NonNull DataStallReport report) {} + + /** + * Called when any app reports connectivity to the System. + * + * @param network The Network for which connectivity has been reported + * @param hasConnectivity The connectivity reported to the System + */ + public void onNetworkConnectivityReported( + @NonNull Network network, boolean hasConnectivity) {} + } + + /** + * Registers a ConnectivityDiagnosticsCallback with the System. + * + * <p>Only apps that offer network connectivity to the user are allowed to register callbacks. + * This includes: + * + * <ul> + * <li>Carrier apps with active subscriptions + * <li>Active VPNs + * <li>WiFi Suggesters + * </ul> + * + * <p>Callbacks will be limited to receiving notifications for networks over which apps provide + * connectivity. + * + * <p>If a registering app loses its relevant permissions, any callbacks it registered will + * silently stop receiving callbacks. + * + * <p>Each register() call <b>MUST</b> use a unique ConnectivityDiagnosticsCallback instance. If + * a single instance is registered with multiple NetworkRequests, an IllegalArgumentException + * will be thrown. + * + * @param request The NetworkRequest that will be used to match with Networks for which + * callbacks will be fired + * @param e The Executor to be used for running the callback method invocations + * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the + * System + * @throws IllegalArgumentException if the same callback instance is registered with multiple + * NetworkRequests + * @throws SecurityException if the caller does not have appropriate permissions to register a + * callback + */ + public void registerConnectivityDiagnosticsCallback( + @NonNull NetworkRequest request, + @NonNull Executor e, + @NonNull ConnectivityDiagnosticsCallback callback) { + // TODO(b/143187964): implement ConnectivityDiagnostics functionality + throw new UnsupportedOperationException("registerCallback() not supported yet"); + } + + /** + * Unregisters a ConnectivityDiagnosticsCallback with the System. + * + * <p>If the given callback is not currently registered with the System, this operation will be + * a no-op. + * + * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System. + */ + public void unregisterConnectivityDiagnosticsCallback( + @NonNull ConnectivityDiagnosticsCallback callback) { + // TODO(b/143187964): implement ConnectivityDiagnostics functionality + throw new UnsupportedOperationException("registerCallback() not supported yet"); + } +} diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 6da16a82c822..03d4200bd90f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -363,7 +363,7 @@ public class ConnectivityManager { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage public static final String ACTION_TETHER_STATE_CHANGED = - "android.net.conn.TETHER_STATE_CHANGED"; + TetheringManager.ACTION_TETHER_STATE_CHANGED; /** * @hide @@ -371,14 +371,14 @@ public class ConnectivityManager { * tethering and currently available for tethering. */ @UnsupportedAppUsage - public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + public static final String EXTRA_AVAILABLE_TETHER = TetheringManager.EXTRA_AVAILABLE_TETHER; /** * @hide * gives a String[] listing all the interfaces currently in local-only * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding) */ - public static final String EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray"; + public static final String EXTRA_ACTIVE_LOCAL_ONLY = TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; /** * @hide @@ -386,7 +386,7 @@ public class ConnectivityManager { * (ie, has DHCPv4 support and packets potentially forwarded/NATed) */ @UnsupportedAppUsage - public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + public static final String EXTRA_ACTIVE_TETHER = TetheringManager.EXTRA_ACTIVE_TETHER; /** * @hide @@ -395,7 +395,7 @@ public class ConnectivityManager { * for any interfaces listed here. */ @UnsupportedAppUsage - public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + public static final String EXTRA_ERRORED_TETHER = TetheringManager.EXTRA_ERRORED_TETHER; /** * Broadcast Action: The captive portal tracker has finished its test. @@ -445,7 +445,7 @@ public class ConnectivityManager { * @see #startTethering(int, boolean, OnStartTetheringCallback) * @hide */ - public static final int TETHERING_INVALID = -1; + public static final int TETHERING_INVALID = TetheringManager.TETHERING_INVALID; /** * Wifi tethering type. @@ -453,7 +453,7 @@ public class ConnectivityManager { * @hide */ @SystemApi - public static final int TETHERING_WIFI = 0; + public static final int TETHERING_WIFI = TetheringManager.TETHERING_WIFI; /** * USB tethering type. @@ -461,7 +461,7 @@ public class ConnectivityManager { * @hide */ @SystemApi - public static final int TETHERING_USB = 1; + public static final int TETHERING_USB = TetheringManager.TETHERING_USB; /** * Bluetooth tethering type. @@ -469,7 +469,7 @@ public class ConnectivityManager { * @hide */ @SystemApi - public static final int TETHERING_BLUETOOTH = 2; + public static final int TETHERING_BLUETOOTH = TetheringManager.TETHERING_BLUETOOTH; /** * Wifi P2p tethering type. @@ -477,41 +477,41 @@ public class ConnectivityManager { * need to start from #startTethering(int, boolean, OnStartTetheringCallback). * @hide */ - public static final int TETHERING_WIFI_P2P = 3; + public static final int TETHERING_WIFI_P2P = TetheringManager.TETHERING_WIFI_P2P; /** * Extra used for communicating with the TetherService. Includes the type of tethering to * enable if any. * @hide */ - public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; + public static final String EXTRA_ADD_TETHER_TYPE = TetheringManager.EXTRA_ADD_TETHER_TYPE; /** * Extra used for communicating with the TetherService. Includes the type of tethering for * which to cancel provisioning. * @hide */ - public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; + public static final String EXTRA_REM_TETHER_TYPE = TetheringManager.EXTRA_REM_TETHER_TYPE; /** * Extra used for communicating with the TetherService. True to schedule a recheck of tether * provisioning. * @hide */ - public static final String EXTRA_SET_ALARM = "extraSetAlarm"; + public static final String EXTRA_SET_ALARM = TetheringManager.EXTRA_SET_ALARM; /** * Tells the TetherService to run a provision check now. * @hide */ - public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; + public static final String EXTRA_RUN_PROVISION = TetheringManager.EXTRA_RUN_PROVISION; /** * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} * which will receive provisioning results. Can be left empty. * @hide */ - public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + public static final String EXTRA_PROVISION_CALLBACK = TetheringManager.EXTRA_PROVISION_CALLBACK; /** * The absence of a connection type. @@ -3107,6 +3107,63 @@ public class ConnectivityManager { } } + /** + * Registers the specified {@link NetworkProvider}. + * Each listener must only be registered once. The listener can be unregistered with + * {@link #unregisterNetworkProvider}. + * + * @param provider the provider to register + * @return the ID of the provider. This ID must be used by the provider when registering + * {@link android.net.NetworkAgent}s. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public int registerNetworkProvider(@NonNull NetworkProvider provider) { + if (provider.getProviderId() != NetworkProvider.ID_NONE) { + // TODO: Provide a better method for checking this by moving NetworkFactory.SerialNumber + // out of NetworkFactory. + throw new IllegalStateException("NetworkProviders can only be registered once"); + } + + try { + int providerId = mService.registerNetworkProvider(provider.getMessenger(), + provider.getName()); + provider.setProviderId(providerId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return provider.getProviderId(); + } + + /** + * Unregisters the specified NetworkProvider. + * + * @param provider the provider to unregister + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void unregisterNetworkProvider(@NonNull NetworkProvider provider) { + try { + mService.unregisterNetworkProvider(provider.getMessenger()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + provider.setProviderId(NetworkProvider.ID_NONE); + } + + + /** @hide exposed via the NetworkProvider class. */ + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) { + try { + mService.declareNetworkRequestUnfulfillable(request); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + // TODO : remove this method. It is a stopgap measure to help sheperding a number // of dependent changes that would conflict throughout the automerger graph. Having this // temporarily helps with the process of going through with all these dependent changes across diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java index 98bab44e19fb..912df67a0b45 100644 --- a/core/java/android/net/DhcpInfo.java +++ b/core/java/android/net/DhcpInfo.java @@ -16,8 +16,8 @@ package android.net; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; /** * A simple object for retrieving the results of a DHCP request. @@ -67,12 +67,12 @@ public class DhcpInfo implements Parcelable { buf.append(NetworkUtils.intToInetAddress(addr).getHostAddress()); } - /** Implement the Parcelable interface {@hide} */ + /** Implement the Parcelable interface */ public int describeContents() { return 0; } - /** Implement the Parcelable interface {@hide} */ + /** Implement the Parcelable interface */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(ipAddress); dest.writeInt(gateway); @@ -83,7 +83,7 @@ public class DhcpInfo implements Parcelable { dest.writeInt(leaseDuration); } - /** Implement the Parcelable interface {@hide} */ + /** Implement the Parcelable interface */ public static final @android.annotation.NonNull Creator<DhcpInfo> CREATOR = new Creator<DhcpInfo>() { public DhcpInfo createFromParcel(Parcel in) { diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 09c02efbcfc4..e6a0379ff629 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -142,12 +142,16 @@ interface IConnectivityManager void setAirplaneMode(boolean enable); - int registerNetworkFactory(in Messenger messenger, in String name); - boolean requestBandwidthUpdate(in Network network); + int registerNetworkFactory(in Messenger messenger, in String name); void unregisterNetworkFactory(in Messenger messenger); + int registerNetworkProvider(in Messenger messenger, in String name); + void unregisterNetworkProvider(in Messenger messenger); + + void declareNetworkRequestUnfulfillable(in NetworkRequest request); + int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 45d0c7313fca..09ec6c35fcb9 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -17,7 +17,6 @@ package android.net; import static com.android.internal.util.Preconditions.checkNotNull; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; @@ -26,6 +25,7 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; +import android.net.annotations.PolicyDirection; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -41,8 +41,6 @@ import dalvik.system.CloseGuard; import java.io.FileDescriptor; import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.Socket; @@ -78,11 +76,6 @@ public final class IpSecManager { */ public static final int DIRECTION_OUT = 1; - /** @hide */ - @IntDef(value = {DIRECTION_IN, DIRECTION_OUT}) - @Retention(RetentionPolicy.SOURCE) - public @interface PolicyDirection {} - /** * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index. * diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 33c39d489835..739e8178e68e 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -587,15 +587,14 @@ public final class NetworkCapabilities implements Parcelable { } /** - * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if all the capabilities it provides are - * typically provided by restricted networks. + * Deduces that all the capabilities it provides are typically provided by restricted networks + * or not. * - * TODO: consider: - * - Renaming it to guessRestrictedCapability and make it set the - * restricted capability bit in addition to clearing it. + * @return {@code true} if the network should be restricted. * @hide */ - public void maybeMarkCapabilitiesRestricted() { + @SystemApi + public boolean deduceRestrictedCapability() { // Check if we have any capability that forces the network to be restricted. final boolean forceRestrictedCapability = (mNetworkCapabilities & FORCE_RESTRICTED_CAPABILITIES) != 0; @@ -609,8 +608,17 @@ public final class NetworkCapabilities implements Parcelable { final boolean hasRestrictedCapabilities = (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0; - if (forceRestrictedCapability - || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities)) { + return forceRestrictedCapability + || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities); + } + + /** + * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if deducing the network is restricted. + * + * @hide + */ + public void maybeMarkCapabilitiesRestricted() { + if (deduceRestrictedCapability()) { removeCapability(NET_CAPABILITY_NOT_RESTRICTED); } } diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java new file mode 100644 index 000000000000..2c0e4aa700b1 --- /dev/null +++ b/core/java/android/net/NetworkProvider.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.util.Log; + +/** + * Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device + * to networks and makes them available to to the core network stack by creating + * {@link NetworkAgent}s. The networks can then provide connectivity to apps and can be interacted + * with via networking APIs such as {@link ConnectivityManager}. + * + * Subclasses should implement {@link #onNetworkRequested} and {@link #onRequestWithdrawn} to + * receive {@link NetworkRequest}s sent by the system and by apps. A network that is not the + * best (highest-scoring) network for any request is generally not used by the system, and torn + * down. + * + * @hide + */ +@SystemApi +public class NetworkProvider { + /** + * {@code providerId} value that indicates the absence of a provider. It is the providerId of + * any NetworkProvider that is not currently registered, and of any NetworkRequest that is not + * currently being satisfied by a network. + */ + public static final int ID_NONE = -1; + + /** + * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any + * provider, so they use this constant for clarity instead of NONE. + * @hide only used by ConnectivityService. + */ + public static final int ID_VPN = -2; + + /** + * The first providerId value that will be allocated. + * @hide only used by ConnectivityService. + */ + public static final int FIRST_PROVIDER_ID = 1; + + /** @hide only used by ConnectivityService */ + public static final int CMD_REQUEST_NETWORK = 1; + /** @hide only used by ConnectivityService */ + public static final int CMD_CANCEL_REQUEST = 2; + + private final Messenger mMessenger; + private final String mName; + private final ConnectivityManager mCm; + + private int mProviderId = ID_NONE; + + /** + * Constructs a new NetworkProvider. + * + * @param looper the Looper on which to run {@link #onNetworkRequested} and + * {@link #onRequestWithdrawn}. + * @param name the name of the listener, used only for debugging. + * + * @hide + */ + @SystemApi + public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) { + mCm = ConnectivityManager.from(context); + + Handler handler = new Handler(looper) { + @Override + public void handleMessage(Message m) { + switch (m.what) { + case CMD_REQUEST_NETWORK: + onNetworkRequested((NetworkRequest) m.obj, m.arg1, m.arg2); + break; + case CMD_CANCEL_REQUEST: + onRequestWithdrawn((NetworkRequest) m.obj); + break; + default: + Log.e(mName, "Unhandled message: " + m.what); + } + } + }; + mMessenger = new Messenger(handler); + mName = name; + } + + // TODO: consider adding a register() method so ConnectivityManager does not need to call this. + public @Nullable Messenger getMessenger() { + return mMessenger; + } + + public @NonNull String getName() { + return mName; + } + + /** + * Returns the ID of this provider. This is known only once the provider is registered via + * {@link ConnectivityManager#registerNetworkProvider()}, otherwise the ID is {@link #ID_NONE}. + * This ID must be used when registering any {@link NetworkAgent}s. + */ + public int getProviderId() { + return mProviderId; + } + + /** @hide */ + public void setProviderId(int providerId) { + mProviderId = providerId; + } + + /** + * Called when a NetworkRequest is received. The request may be a new request or an existing + * request with a different score. + * + * @param request the NetworkRequest being received + * @param score the score of the network currently satisfying the request, or 0 if none. + * @param providerId the ID of the provider that created the network currently satisfying this + * request, or {@link #ID_NONE} if none. + * + * @hide + */ + @SystemApi + public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {} + + /** + * Called when a NetworkRequest is withdrawn. + * @hide + */ + @SystemApi + public void onRequestWithdrawn(@NonNull NetworkRequest request) {} + + /** + * Asserts that no provider will ever be able to satisfy the specified request. The provider + * must only call this method if it knows that it is the only provider on the system capable of + * satisfying this request, and that the request cannot be satisfied. The application filing the + * request will receive an {@link NetworkCallback#onUnavailable()} callback. + * + * @param request the request that cannot be fulfilled + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) { + mCm.declareNetworkRequestUnfulfillable(request); + } +} diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 431773dc2622..9731f3ca186d 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -247,9 +247,8 @@ public class NetworkRequest implements Parcelable { * removing even the capabilities that are set by default when the object is constructed. * * @return The builder to facilitate chaining. - * @hide */ - @UnsupportedAppUsage + @NonNull public Builder clearCapabilities() { mNetworkCapabilities.clearAll(); return this; diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java index 2bc3eb56ec2d..cf31d217c967 100644 --- a/core/java/android/net/NetworkSpecifier.java +++ b/core/java/android/net/NetworkSpecifier.java @@ -16,6 +16,9 @@ package android.net; +import android.annotation.Nullable; +import android.annotation.SystemApi; + /** * Describes specific properties of a requested network for use in a {@link NetworkRequest}. * @@ -31,7 +34,8 @@ public abstract class NetworkSpecifier { * * @hide */ - public abstract boolean satisfiedBy(NetworkSpecifier other); + @SystemApi + public abstract boolean satisfiedBy(@Nullable NetworkSpecifier other); /** * Optional method which can be overridden by concrete implementations of NetworkSpecifier to @@ -45,6 +49,7 @@ public abstract class NetworkSpecifier { * * @hide */ + @SystemApi public void assertValidFromUid(int requestorUid) { // empty } @@ -68,6 +73,8 @@ public abstract class NetworkSpecifier { * * @hide */ + @SystemApi + @Nullable public NetworkSpecifier redact() { // TODO (b/122160111): convert default to null once all platform NetworkSpecifiers // implement this method. diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 45bf4d2e1358..96d7a80886a5 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -19,6 +19,9 @@ package android.net; import static android.os.Process.CLAT_UID; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -48,59 +51,104 @@ import java.util.function.Predicate; * @hide */ // @NotThreadSafe -public class NetworkStats implements Parcelable { +@SystemApi +public final class NetworkStats implements Parcelable { private static final String TAG = "NetworkStats"; + /** {@link #iface} value when interface details unavailable. */ - public static final String IFACE_ALL = null; + @SuppressLint("CompileTimeConstant") + @Nullable public static final String IFACE_ALL = null; + /** + * Virtual network interface for video telephony. This is for VT data usage counting + * purpose. + */ + public static final String IFACE_VT = "vt_data0"; + /** {@link #uid} value when UID details unavailable. */ public static final int UID_ALL = -1; - /** {@link #tag} value matching any tag. */ + /** Special UID value for data usage by tethering. */ + public static final int UID_TETHERING = -5; + + /** + * {@link #tag} value matching any tag. + * @hide + */ // TODO: Rename TAG_ALL to TAG_ANY. public static final int TAG_ALL = -1; - /** {@link #set} value for all sets combined, not including debug sets. */ + /** + * {@link #set} value for all sets combined, not including debug sets. + * @hide + */ public static final int SET_ALL = -1; /** {@link #set} value where background data is accounted. */ public static final int SET_DEFAULT = 0; /** {@link #set} value where foreground data is accounted. */ public static final int SET_FOREGROUND = 1; - /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */ + /** + * All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. + * @hide + */ public static final int SET_DEBUG_START = 1000; - /** Debug {@link #set} value when the VPN stats are moved in. */ + /** + * Debug {@link #set} value when the VPN stats are moved in. + * @hide + */ public static final int SET_DBG_VPN_IN = 1001; - /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */ + /** + * Debug {@link #set} value when the VPN stats are moved out of a vpn UID. + * @hide + */ public static final int SET_DBG_VPN_OUT = 1002; - /** Include all interfaces when filtering */ - public static final String[] INTERFACES_ALL = null; + /** + * Include all interfaces when filtering + * @hide + */ + public @Nullable static final String[] INTERFACES_ALL = null; /** {@link #tag} value for total data across all tags. */ // TODO: Rename TAG_NONE to TAG_ALL. public static final int TAG_NONE = 0; - /** {@link #metered} value to account for all metered states. */ + /** + * {@link #metered} value to account for all metered states. + * @hide + */ public static final int METERED_ALL = -1; /** {@link #metered} value where native, unmetered data is accounted. */ public static final int METERED_NO = 0; /** {@link #metered} value where metered data is accounted. */ public static final int METERED_YES = 1; - /** {@link #roaming} value to account for all roaming states. */ + /** + * {@link #roaming} value to account for all roaming states. + * @hide + */ public static final int ROAMING_ALL = -1; /** {@link #roaming} value where native, non-roaming data is accounted. */ public static final int ROAMING_NO = 0; /** {@link #roaming} value where roaming data is accounted. */ public static final int ROAMING_YES = 1; - /** {@link #onDefaultNetwork} value to account for all default network states. */ + /** + * {@link #onDefaultNetwork} value to account for all default network states. + * @hide + */ public static final int DEFAULT_NETWORK_ALL = -1; /** {@link #onDefaultNetwork} value to account for usage while not the default network. */ public static final int DEFAULT_NETWORK_NO = 0; /** {@link #onDefaultNetwork} value to account for usage while the default network. */ public static final int DEFAULT_NETWORK_YES = 1; - /** Denotes a request for stats at the interface level. */ + /** + * Denotes a request for stats at the interface level. + * @hide + */ public static final int STATS_PER_IFACE = 0; - /** Denotes a request for stats at the interface and UID level. */ + /** + * Denotes a request for stats at the interface and UID level. + * @hide + */ public static final int STATS_PER_UID = 1; private static final String CLATD_INTERFACE_PREFIX = "v4-"; @@ -144,60 +192,78 @@ public class NetworkStats implements Parcelable { @UnsupportedAppUsage private long[] operations; + /** @hide */ + @SystemApi public static class Entry { + /** @hide */ @UnsupportedAppUsage public String iface; + /** @hide */ @UnsupportedAppUsage public int uid; + /** @hide */ @UnsupportedAppUsage public int set; + /** @hide */ @UnsupportedAppUsage public int tag; /** * Note that this is only populated w/ the default value when read from /proc or written * to disk. We merge in the correct value when reporting this value to clients of * getSummary(). + * @hide */ public int metered; /** * Note that this is only populated w/ the default value when read from /proc or written * to disk. We merge in the correct value when reporting this value to clients of * getSummary(). + * @hide */ public int roaming; /** * Note that this is only populated w/ the default value when read from /proc or written * to disk. We merge in the correct value when reporting this value to clients of * getSummary(). + * @hide */ public int defaultNetwork; + /** @hide */ @UnsupportedAppUsage public long rxBytes; + /** @hide */ @UnsupportedAppUsage public long rxPackets; + /** @hide */ @UnsupportedAppUsage public long txBytes; + /** @hide */ @UnsupportedAppUsage public long txPackets; + /** @hide */ + @UnsupportedAppUsage public long operations; + /** @hide */ @UnsupportedAppUsage public Entry() { this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L); } + /** @hide */ public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, operations); } + /** @hide */ public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, rxPackets, txBytes, txPackets, operations); } - public Entry(String iface, int uid, int set, int tag, int metered, int roaming, + public Entry(@Nullable String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { this.iface = iface; @@ -214,15 +280,18 @@ public class NetworkStats implements Parcelable { this.operations = operations; } + /** @hide */ public boolean isNegative() { return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0; } + /** @hide */ public boolean isEmpty() { return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0 && operations == 0; } + /** @hide */ public void add(Entry another) { this.rxBytes += another.rxBytes; this.rxPackets += another.rxPackets; @@ -249,6 +318,7 @@ public class NetworkStats implements Parcelable { return builder.toString(); } + /** @hide */ @Override public boolean equals(Object o) { if (o instanceof Entry) { @@ -262,13 +332,13 @@ public class NetworkStats implements Parcelable { return false; } + /** @hide */ @Override public int hashCode() { return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface); } } - @UnsupportedAppUsage public NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; this.size = 0; @@ -292,6 +362,7 @@ public class NetworkStats implements Parcelable { } } + /** @hide */ @UnsupportedAppUsage public NetworkStats(Parcel parcel) { elapsedRealtime = parcel.readLong(); @@ -312,7 +383,7 @@ public class NetworkStats implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(elapsedRealtime); dest.writeInt(size); dest.writeInt(capacity); @@ -330,19 +401,23 @@ public class NetworkStats implements Parcelable { dest.writeLongArray(operations); } + /** + * @hide + */ @Override public NetworkStats clone() { final NetworkStats clone = new NetworkStats(elapsedRealtime, size); NetworkStats.Entry entry = null; for (int i = 0; i < size; i++) { entry = getValues(i, entry); - clone.addValues(entry); + clone.addEntry(entry); } return clone; } /** * Clear all data stored in this object. + * @hide */ public void clear() { this.capacity = 0; @@ -360,25 +435,28 @@ public class NetworkStats implements Parcelable { this.operations = EmptyArray.LONG; } + /** @hide */ @VisibleForTesting public NetworkStats addIfaceValues( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { - return addValues( + return addEntry( iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L); } + /** @hide */ @VisibleForTesting - public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes, + public NetworkStats addEntry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { - return addValues(new Entry( + return addEntry(new Entry( iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); } + /** @hide */ @VisibleForTesting - public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming, + public NetworkStats addEntry(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { - return addValues(new Entry( + return addEntry(new Entry( iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations)); } @@ -386,8 +464,9 @@ public class NetworkStats implements Parcelable { /** * Add new stats entry, copying from given {@link Entry}. The {@link Entry} * object can be recycled across multiple calls. + * @hide */ - public NetworkStats addValues(Entry entry) { + public NetworkStats addEntry(Entry entry) { if (size >= capacity) { final int newLength = Math.max(size, 10) * 3 / 2; iface = Arrays.copyOf(iface, newLength); @@ -428,6 +507,7 @@ public class NetworkStats implements Parcelable { /** * Return specific stats entry. + * @hide */ @UnsupportedAppUsage public Entry getValues(int i, Entry recycle) { @@ -467,10 +547,12 @@ public class NetworkStats implements Parcelable { operations[dest] = operations[src]; } + /** @hide */ public long getElapsedRealtime() { return elapsedRealtime; } + /** @hide */ public void setElapsedRealtime(long time) { elapsedRealtime = time; } @@ -478,21 +560,25 @@ public class NetworkStats implements Parcelable { /** * Return age of this {@link NetworkStats} object with respect to * {@link SystemClock#elapsedRealtime()}. + * @hide */ public long getElapsedRealtimeAge() { return SystemClock.elapsedRealtime() - elapsedRealtime; } + /** @hide */ @UnsupportedAppUsage public int size() { return size; } + /** @hide */ @VisibleForTesting public int internalSize() { return capacity; } + /** @hide */ @Deprecated public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { @@ -501,6 +587,7 @@ public class NetworkStats implements Parcelable { txPackets, operations); } + /** @hide */ public NetworkStats combineValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { return combineValues(new Entry( @@ -509,16 +596,20 @@ public class NetworkStats implements Parcelable { /** * Combine given values with an existing row, or create a new row if - * {@link #findIndex(String, int, int, int, int)} is unable to find match. Can - * also be used to subtract values from existing rows. + * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can + * also be used to subtract values from existing rows. This method mutates the referencing + * {@link NetworkStats} object. + * + * @param entry the {@link Entry} to combine. + * @return a reference to this mutated {@link NetworkStats} object. + * @hide */ - @UnsupportedAppUsage - public NetworkStats combineValues(Entry entry) { + public @NonNull NetworkStats combineValues(@NonNull Entry entry) { final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered, entry.roaming, entry.defaultNetwork); if (i == -1) { // only create new entry when positive contribution - addValues(entry); + addEntry(entry); } else { rxBytes[i] += entry.rxBytes; rxPackets[i] += entry.rxPackets; @@ -530,10 +621,33 @@ public class NetworkStats implements Parcelable { } /** + * Add given values with an existing row, or create a new row if + * {@link #findIndex(String, int, int, int, int, int, int)} is unable to find match. Can + * also be used to subtract values from existing rows. + * + * @param entry the {@link Entry} to add. + * @return a new constructed {@link NetworkStats} object that contains the result. + */ + public @NonNull NetworkStats addValues(@NonNull Entry entry) { + return this.clone().combineValues(entry); + } + + /** + * Add the given {@link NetworkStats} objects. + * + * @return the sum of two objects. + */ + public @NonNull NetworkStats add(@NonNull NetworkStats another) { + final NetworkStats ret = this.clone(); + ret.combineAllValues(another); + return ret; + } + + /** * Combine all values from another {@link NetworkStats} into this object. + * @hide */ - @UnsupportedAppUsage - public void combineAllValues(NetworkStats another) { + public void combineAllValues(@NonNull NetworkStats another) { NetworkStats.Entry entry = null; for (int i = 0; i < another.size; i++) { entry = another.getValues(i, entry); @@ -543,6 +657,7 @@ public class NetworkStats implements Parcelable { /** * Find first stats index that matches the requested parameters. + * @hide */ public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming, int defaultNetwork) { @@ -560,6 +675,7 @@ public class NetworkStats implements Parcelable { /** * Find first stats index that matches the requested parameters, starting * search around the hinted index as an optimization. + * @hide */ @VisibleForTesting public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming, @@ -589,6 +705,7 @@ public class NetworkStats implements Parcelable { * Splice in {@link #operations} from the given {@link NetworkStats} based * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface}, * since operation counts are at data layer. + * @hide */ public void spliceOperationsFrom(NetworkStats stats) { for (int i = 0; i < size; i++) { @@ -604,6 +721,7 @@ public class NetworkStats implements Parcelable { /** * Return list of unique interfaces known by this data structure. + * @hide */ public String[] getUniqueIfaces() { final HashSet<String> ifaces = new HashSet<String>(); @@ -617,6 +735,7 @@ public class NetworkStats implements Parcelable { /** * Return list of unique UIDs known by this data structure. + * @hide */ @UnsupportedAppUsage public int[] getUniqueUids() { @@ -636,6 +755,7 @@ public class NetworkStats implements Parcelable { /** * Return total bytes represented by this snapshot object, usually used when * checking if a {@link #subtract(NetworkStats)} delta passes a threshold. + * @hide */ @UnsupportedAppUsage public long getTotalBytes() { @@ -645,6 +765,7 @@ public class NetworkStats implements Parcelable { /** * Return total of all fields represented by this snapshot object. + * @hide */ @UnsupportedAppUsage public Entry getTotal(Entry recycle) { @@ -654,6 +775,7 @@ public class NetworkStats implements Parcelable { /** * Return total of all fields represented by this snapshot object matching * the requested {@link #uid}. + * @hide */ @UnsupportedAppUsage public Entry getTotal(Entry recycle, int limitUid) { @@ -663,11 +785,13 @@ public class NetworkStats implements Parcelable { /** * Return total of all fields represented by this snapshot object matching * the requested {@link #iface}. + * @hide */ public Entry getTotal(Entry recycle, HashSet<String> limitIface) { return getTotal(recycle, limitIface, UID_ALL, false); } + /** @hide */ @UnsupportedAppUsage public Entry getTotalIncludingTags(Entry recycle) { return getTotal(recycle, null, UID_ALL, true); @@ -717,6 +841,7 @@ public class NetworkStats implements Parcelable { /** * Fast path for battery stats. + * @hide */ public long getTotalPackets() { long total = 0; @@ -729,9 +854,12 @@ public class NetworkStats implements Parcelable { /** * Subtract the given {@link NetworkStats}, effectively leaving the delta * between two snapshots in time. Assumes that statistics rows collect over - * time, and that none of them have disappeared. + * time, and that none of them have disappeared. This method does not mutate + * the referencing object. + * + * @return the delta between two objects. */ - public NetworkStats subtract(NetworkStats right) { + public @NonNull NetworkStats subtract(@NonNull NetworkStats right) { return subtract(this, right, null, null); } @@ -742,6 +870,7 @@ public class NetworkStats implements Parcelable { * <p> * If counters have rolled backwards, they are clamped to {@code 0} and * reported to the given {@link NonMonotonicObserver}. + * @hide */ public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie) { @@ -759,6 +888,7 @@ public class NetworkStats implements Parcelable { * If <var>recycle</var> is supplied, this NetworkStats object will be * reused (and returned) as the result if it is large enough to contain * the data. + * @hide */ public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) { @@ -817,7 +947,7 @@ public class NetworkStats implements Parcelable { entry.operations = Math.max(entry.operations, 0); } - result.addValues(entry); + result.addEntry(entry); } return result; @@ -847,6 +977,7 @@ public class NetworkStats implements Parcelable { * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated. * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both. * @param useBpfStats True if eBPF is in use. + * @hide */ public static void apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic, Map<String, String> stackedIfaces, boolean useBpfStats) { @@ -899,6 +1030,7 @@ public class NetworkStats implements Parcelable { * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as * base and stacked traffic. * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both. + * @hide */ public void apply464xlatAdjustments(Map<String, String> stackedIfaces, boolean useBpfStats) { apply464xlatAdjustments(this, this, stackedIfaces, useBpfStats); @@ -907,6 +1039,7 @@ public class NetworkStats implements Parcelable { /** * Return total statistics grouped by {@link #iface}; doesn't mutate the * original structure. + * @hide */ public NetworkStats groupedByIface() { final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); @@ -938,6 +1071,7 @@ public class NetworkStats implements Parcelable { /** * Return total statistics grouped by {@link #uid}; doesn't mutate the * original structure. + * @hide */ public NetworkStats groupedByUid() { final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); @@ -968,6 +1102,7 @@ public class NetworkStats implements Parcelable { /** * Remove all rows that match one of specified UIDs. + * @hide */ public void removeUids(int[] uids) { int nextOutputEntry = 0; @@ -989,6 +1124,7 @@ public class NetworkStats implements Parcelable { * @param limitUid UID to filter for, or {@link #UID_ALL}. * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}. * @param limitTag Tag to filter for, or {@link #TAG_ALL}. + * @hide */ public void filter(int limitUid, String[] limitIfaces, int limitTag) { if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) { @@ -1004,6 +1140,7 @@ public class NetworkStats implements Parcelable { * Only keep entries with {@link #set} value less than {@link #SET_DEBUG_START}. * * <p>This mutates the original structure in place. + * @hide */ public void filterDebugEntries() { filter(e -> e.set < SET_DEBUG_START); @@ -1024,6 +1161,7 @@ public class NetworkStats implements Parcelable { size = nextOutputEntry; } + /** @hide */ public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); @@ -1047,6 +1185,7 @@ public class NetworkStats implements Parcelable { /** * Return text description of {@link #set} value. + * @hide */ public static String setToString(int set) { switch (set) { @@ -1067,6 +1206,7 @@ public class NetworkStats implements Parcelable { /** * Return text description of {@link #set} value. + * @hide */ public static String setToCheckinString(int set) { switch (set) { @@ -1087,6 +1227,7 @@ public class NetworkStats implements Parcelable { /** * @return true if the querySet matches the dataSet. + * @hide */ public static boolean setMatches(int querySet, int dataSet) { if (querySet == dataSet) { @@ -1098,6 +1239,7 @@ public class NetworkStats implements Parcelable { /** * Return text description of {@link #tag} value. + * @hide */ public static String tagToString(int tag) { return "0x" + Integer.toHexString(tag); @@ -1105,6 +1247,7 @@ public class NetworkStats implements Parcelable { /** * Return text description of {@link #metered} value. + * @hide */ public static String meteredToString(int metered) { switch (metered) { @@ -1121,6 +1264,7 @@ public class NetworkStats implements Parcelable { /** * Return text description of {@link #roaming} value. + * @hide */ public static String roamingToString(int roaming) { switch (roaming) { @@ -1137,6 +1281,7 @@ public class NetworkStats implements Parcelable { /** * Return text description of {@link #defaultNetwork} value. + * @hide */ public static String defaultNetworkToString(int defaultNetwork) { switch (defaultNetwork) { @@ -1151,6 +1296,7 @@ public class NetworkStats implements Parcelable { } } + /** @hide */ @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); @@ -1163,8 +1309,7 @@ public class NetworkStats implements Parcelable { return 0; } - @UnsupportedAppUsage - public static final @android.annotation.NonNull Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { + public static final @NonNull Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() { @Override public NetworkStats createFromParcel(Parcel in) { return new NetworkStats(in); @@ -1176,6 +1321,7 @@ public class NetworkStats implements Parcelable { } }; + /** @hide */ public interface NonMonotonicObserver<C> { public void foundNonMonotonic( NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie); @@ -1195,6 +1341,7 @@ public class NetworkStats implements Parcelable { * @param tunUid uid of the VPN application * @param tunIface iface of the vpn tunnel * @param underlyingIfaces underlying network ifaces used by the VPN application + * @hide */ public void migrateTun(int tunUid, @NonNull String tunIface, @NonNull String[] underlyingIfaces) { diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 162d6e27bd42..8108cf08d5c3 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -89,7 +89,7 @@ public class TrafficStats { * * @hide */ - public static final int UID_TETHERING = -5; + public static final int UID_TETHERING = NetworkStats.UID_TETHERING; /** * Tag values in this range are reserved for the network stack. The network stack is diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java new file mode 100644 index 000000000000..febd9b406111 --- /dev/null +++ b/core/java/android/net/annotations/PolicyDirection.java @@ -0,0 +1,35 @@ +/* + * 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.annotations; + +import android.annotation.IntDef; +import android.net.IpSecManager; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * IPsec traffic direction. + * + * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class + * to allow others to statically include it. + * + * @hide + */ +@IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT}) +@Retention(RetentionPolicy.SOURCE) +public @interface PolicyDirection {} diff --git a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java new file mode 100644 index 000000000000..740aa92ad484 --- /dev/null +++ b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.netstats.provider; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.NetworkStats; + +/** + * A base class that allows external modules to implement a custom network statistics provider. + * @hide + */ +@SystemApi +public abstract class AbstractNetworkStatsProvider { + /** + * A value used by {@link #setLimit} and {@link #setAlert} indicates there is no limit. + */ + public static final int QUOTA_UNLIMITED = -1; + + /** + * Called by {@code NetworkStatsService} when global polling is needed. Custom + * implementation of providers MUST respond to it by calling + * {@link NetworkStatsProviderCallback#onStatsUpdated} within one minute. Responding + * later than this may cause the stats to be dropped. + * + * @param token a positive number identifying the new state of the system under which + * {@link NetworkStats} have to be gathered from now on. When this is called, + * custom implementations of providers MUST report the latest stats with the + * previous token, under which stats were being gathered so far. + */ + public abstract void requestStatsUpdate(int token); + + /** + * Called by {@code NetworkStatsService} when setting the interface quota for the specified + * upstream interface. When this is called, the custom implementation should block all egress + * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have + * been reached, and MUST respond to it by calling + * {@link NetworkStatsProviderCallback#onLimitReached()}. + * + * @param iface the interface requiring the operation. + * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting + * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit. + */ + public abstract void setLimit(@NonNull String iface, long quotaBytes); + + /** + * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations + * MUST call {@link NetworkStatsProviderCallback#onAlertReached()} when {@code quotaBytes} bytes + * have been reached. Unlike {@link #setLimit(String, long)}, the custom implementation should + * not block all egress packets. + * + * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting + * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert. + */ + public abstract void setAlert(long quotaBytes); +} diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl new file mode 100644 index 000000000000..55b3d4edb157 --- /dev/null +++ b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.netstats.provider; + +/** + * Interface for NetworkStatsService to query network statistics and set data limits. + * + * @hide + */ +oneway interface INetworkStatsProvider { + void requestStatsUpdate(int token); + void setLimit(String iface, long quotaBytes); + void setAlert(long quotaBytes); +} diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl new file mode 100644 index 000000000000..3ea9318f10d4 --- /dev/null +++ b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.netstats.provider; + +import android.net.NetworkStats; + +/** + * Interface for implementor of {@link INetworkStatsProviderCallback} to push events + * such as network statistics update or notify limit reached. + * @hide + */ +oneway interface INetworkStatsProviderCallback { + void onStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats); + void onAlertReached(); + void onLimitReached(); + void unregister(); +} diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java new file mode 100644 index 000000000000..e17a8eee7ff0 --- /dev/null +++ b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.netstats.provider; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.net.NetworkStats; +import android.os.RemoteException; + +/** + * A callback class that allows callers to report events to the system. + * @hide + */ +@SystemApi +@SuppressLint("CallbackMethodName") +public class NetworkStatsProviderCallback { + @NonNull private final INetworkStatsProviderCallback mBinder; + + /** @hide */ + public NetworkStatsProviderCallback(@NonNull INetworkStatsProviderCallback binder) { + mBinder = binder; + } + + /** + * Notify the system of new network statistics. + * + * Send the network statistics recorded since the last call to {@link #onStatsUpdated}. Must be + * called within one minute of {@link AbstractNetworkStatsProvider#requestStatsUpdate(int)} + * being called. The provider can also call this whenever it wants to reports new stats for any + * reason. Note that the system will not necessarily immediately propagate the statistics to + * reflect the update. + * + * @param token the token under which these stats were gathered. Providers can call this method + * with the current token as often as they want, until the token changes. + * {@see AbstractNetworkStatsProvider#requestStatsUpdate()} + * @param ifaceStats the {@link NetworkStats} per interface to be reported. + * The provider should not include any traffic that is already counted by + * kernel interface counters. + * @param uidStats the same stats as above, but counts {@link NetworkStats} + * per uid. + */ + public void onStatsUpdated(int token, @NonNull NetworkStats ifaceStats, + @NonNull NetworkStats uidStats) { + try { + mBinder.onStatsUpdated(token, ifaceStats, uidStats); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** + * Notify system that the quota set by {@code setAlert} has been reached. + */ + public void onAlertReached() { + try { + mBinder.onAlertReached(); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** + * Notify system that the quota set by {@code setLimit} has been reached. + */ + public void onLimitReached() { + try { + mBinder.onLimitReached(); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** + * Unregister the provider and the referencing callback. + */ + public void unregister() { + try { + mBinder.unregister(); + } catch (RemoteException e) { + // Ignore error. + } + } +} diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java new file mode 100644 index 000000000000..4bf7c9bc086e --- /dev/null +++ b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.netstats.provider; + +import android.annotation.NonNull; + +/** + * A wrapper class of {@link INetworkStatsProvider} that hides the binder interface from exposing + * to outer world. + * + * @hide + */ +public class NetworkStatsProviderWrapper extends INetworkStatsProvider.Stub { + @NonNull final AbstractNetworkStatsProvider mProvider; + + public NetworkStatsProviderWrapper(AbstractNetworkStatsProvider provider) { + mProvider = provider; + } + + @Override + public void requestStatsUpdate(int token) { + mProvider.requestStatsUpdate(token); + } + + @Override + public void setLimit(@NonNull String iface, long quotaBytes) { + mProvider.setLimit(iface, quotaBytes); + } + + @Override + public void setAlert(long quotaBytes) { + mProvider.setAlert(quotaBytes); + } +} diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index c9ebed3bd0a2..1a4dac78855f 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -1886,29 +1886,7 @@ public final class Parcel { public final void writeException(@NonNull Exception e) { AppOpsManager.prefixParcelWithAppOpsIfNeeded(this); - int code = 0; - if (e instanceof Parcelable - && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) { - // We only send Parcelable exceptions that are in the - // BootClassLoader to ensure that the receiver can unpack them - code = EX_PARCELABLE; - } else if (e instanceof SecurityException) { - code = EX_SECURITY; - } else if (e instanceof BadParcelableException) { - code = EX_BAD_PARCELABLE; - } else if (e instanceof IllegalArgumentException) { - code = EX_ILLEGAL_ARGUMENT; - } else if (e instanceof NullPointerException) { - code = EX_NULL_POINTER; - } else if (e instanceof IllegalStateException) { - code = EX_ILLEGAL_STATE; - } else if (e instanceof NetworkOnMainThreadException) { - code = EX_NETWORK_MAIN_THREAD; - } else if (e instanceof UnsupportedOperationException) { - code = EX_UNSUPPORTED_OPERATION; - } else if (e instanceof ServiceSpecificException) { - code = EX_SERVICE_SPECIFIC; - } + int code = getExceptionCode(e); writeInt(code); StrictMode.clearGatheredViolations(); if (code == 0) { @@ -1922,20 +1900,7 @@ public final class Parcel { if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) { sLastWriteExceptionStackTrace = timeNow; - final int sizePosition = dataPosition(); - writeInt(0); // Header size will be filled in later - StackTraceElement[] stackTrace = e.getStackTrace(); - final int truncatedSize = Math.min(stackTrace.length, 5); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < truncatedSize; i++) { - sb.append("\tat ").append(stackTrace[i]).append('\n'); - } - writeString(sb.toString()); - final int payloadPosition = dataPosition(); - setDataPosition(sizePosition); - // Write stack trace header size. Used in native side to skip the header - writeInt(payloadPosition - sizePosition); - setDataPosition(payloadPosition); + writeStackTrace(e); } else { writeInt(0); } @@ -1956,6 +1921,52 @@ public final class Parcel { } } + /** @hide */ + public static int getExceptionCode(@NonNull Throwable e) { + int code = 0; + if (e instanceof Parcelable + && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) { + // We only send Parcelable exceptions that are in the + // BootClassLoader to ensure that the receiver can unpack them + code = EX_PARCELABLE; + } else if (e instanceof SecurityException) { + code = EX_SECURITY; + } else if (e instanceof BadParcelableException) { + code = EX_BAD_PARCELABLE; + } else if (e instanceof IllegalArgumentException) { + code = EX_ILLEGAL_ARGUMENT; + } else if (e instanceof NullPointerException) { + code = EX_NULL_POINTER; + } else if (e instanceof IllegalStateException) { + code = EX_ILLEGAL_STATE; + } else if (e instanceof NetworkOnMainThreadException) { + code = EX_NETWORK_MAIN_THREAD; + } else if (e instanceof UnsupportedOperationException) { + code = EX_UNSUPPORTED_OPERATION; + } else if (e instanceof ServiceSpecificException) { + code = EX_SERVICE_SPECIFIC; + } + return code; + } + + /** @hide */ + public void writeStackTrace(@NonNull Throwable e) { + final int sizePosition = dataPosition(); + writeInt(0); // Header size will be filled in later + StackTraceElement[] stackTrace = e.getStackTrace(); + final int truncatedSize = Math.min(stackTrace.length, 5); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < truncatedSize; i++) { + sb.append("\tat ").append(stackTrace[i]).append('\n'); + } + writeString(sb.toString()); + final int payloadPosition = dataPosition(); + setDataPosition(sizePosition); + // Write stack trace header size. Used in native side to skip the header + writeInt(payloadPosition - sizePosition); + setDataPosition(payloadPosition); + } + /** * Special function for writing information at the front of the Parcel * indicating that no exception occurred. @@ -2069,14 +2080,7 @@ public final class Parcel { if (remoteStackTrace != null) { RemoteException cause = new RemoteException( "Remote stack trace:\n" + remoteStackTrace, null, false, false); - try { - Throwable rootCause = ExceptionUtils.getRootCause(e); - if (rootCause != null) { - rootCause.initCause(cause); - } - } catch (RuntimeException ex) { - Log.e(TAG, "Cannot set cause " + cause + " for " + e, ex); - } + ExceptionUtils.appendCause(e, cause); } SneakyThrow.sneakyThrow(e); } @@ -2088,6 +2092,14 @@ public final class Parcel { * @param msg The exception message. */ private Exception createException(int code, String msg) { + Exception exception = createExceptionOrNull(code, msg); + return exception != null + ? exception + : new RuntimeException("Unknown exception code: " + code + " msg " + msg); + } + + /** @hide */ + public Exception createExceptionOrNull(int code, String msg) { switch (code) { case EX_PARCELABLE: if (readInt() > 0) { @@ -2111,9 +2123,9 @@ public final class Parcel { return new UnsupportedOperationException(msg); case EX_SERVICE_SPECIFIC: return new ServiceSpecificException(readInt(), msg); + default: + return null; } - return new RuntimeException("Unknown exception code: " + code - + " msg " + msg); } /** diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 983053bbe7fd..89ddf8cbd96a 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -31,6 +31,9 @@ import static android.system.OsConstants.S_ISLNK; import static android.system.OsConstants.S_ISREG; import static android.system.OsConstants.S_IWOTH; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; @@ -253,6 +256,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * be opened with the requested mode. * @see #parseMode(String) */ + // We can't accept a generic Executor here, since we need to use + // MessageQueue.addOnFileDescriptorEventListener() + @SuppressLint("ExecutorRegistration") public static ParcelFileDescriptor open(File file, int mode, Handler handler, final OnCloseListener listener) throws IOException { if (handler == null) { @@ -268,9 +274,22 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { return fromFd(fd, handler, listener); } - /** {@hide} */ - public static ParcelFileDescriptor fromPfd(ParcelFileDescriptor pfd, Handler handler, - final OnCloseListener listener) throws IOException { + /** + * Create a new ParcelFileDescriptor wrapping an already-opened file. + * + * @param pfd The already-opened file. + * @param handler to call listener from. + * @param listener to be invoked when the returned descriptor has been + * closed. + * @return a new ParcelFileDescriptor pointing to the given file. + * @hide + */ + @SystemApi + // We can't accept a generic Executor here, since we need to use + // MessageQueue.addOnFileDescriptorEventListener() + @SuppressLint("ExecutorRegistration") + public static @NonNull ParcelFileDescriptor wrap(@NonNull ParcelFileDescriptor pfd, + @NonNull Handler handler, @NonNull OnCloseListener listener) throws IOException { final FileDescriptor original = new FileDescriptor(); original.setInt$(pfd.detachFd()); return fromFd(original, handler, listener); diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index b40283f49408..7a837e167fb0 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -16,17 +16,24 @@ package android.os; +import static java.nio.charset.StandardCharsets.UTF_8; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; /** @@ -339,4 +346,44 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa proto.end(token); } + + /** + * Writes the content of the {@link PersistableBundle} to a {@link OutputStream}. + * + * <p>The content can be read by a {@link #readFromStream}. + * + * @see #readFromStream + */ + public void writeToStream(@NonNull OutputStream outputStream) throws IOException { + FastXmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(outputStream, UTF_8.name()); + serializer.startTag(null, "bundle"); + try { + saveToXml(serializer); + } catch (XmlPullParserException e) { + throw new IOException(e); + } + serializer.endTag(null, "bundle"); + serializer.flush(); + } + + /** + * Reads a {@link PersistableBundle} from an {@link InputStream}. + * + * <p>The stream must be generated by {@link #writeToStream}. + * + * @see #writeToStream + */ + @NonNull + public static PersistableBundle readFromStream(@NonNull InputStream inputStream) + throws IOException { + try { + XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); + parser.setInput(inputStream, UTF_8.name()); + parser.next(); + return PersistableBundle.restoreFromXml(parser); + } catch (XmlPullParserException e) { + throw new IOException(e); + } + } } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 94623bc71346..5d80ab6453cc 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -68,10 +68,9 @@ public class Process { public static final int LOG_UID = 1007; /** - * Defines the UID/GID for the WIFI supplicant process. - * @hide + * Defines the UID/GID for the WIFI native processes like wificond, supplicant, hostapd, + * vendor HAL, etc. */ - @UnsupportedAppUsage public static final int WIFI_UID = 1010; /** @@ -89,6 +88,12 @@ public class Process { public static final int DRM_UID = 1019; /** + * Defines the GID for the group that allows write access to the internal media storage. + * @hide + */ + public static final int SDCARD_RW_GID = 1015; + + /** * Defines the UID/GID for the group that controls VPN services. * @hide */ @@ -752,11 +757,12 @@ public class Process { /** * Set the priority of a thread, based on Linux priorities. - * - * @param tid The identifier of the thread/process to change. + * + * @param tid The identifier of the thread/process to change. It should be + * the native thread id but not the managed id of {@link java.lang.Thread}. * @param priority A Linux priority level, from -20 for highest scheduling * priority to 19 for lowest scheduling priority. - * + * * @throws IllegalArgumentException Throws IllegalArgumentException if * <var>tid</var> does not exist. * @throws SecurityException Throws SecurityException if your process does diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java index 1211dd60f591..064cf7d825b5 100644 --- a/core/java/android/os/TelephonyServiceManager.java +++ b/core/java/android/os/TelephonyServiceManager.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.Context; /** * Provides a way to register and obtain the system service binder objects managed by the telephony @@ -51,8 +52,8 @@ public class TelephonyServiceManager { /** * Register a system server binding object for a service. */ - public void register(@NonNull IBinder binder) { - ServiceManager.addService(mServiceName, binder); + public void register(@NonNull IBinder service) { + ServiceManager.addService(mServiceName, service); } /** @@ -114,25 +115,123 @@ public class TelephonyServiceManager { */ @NonNull public ServiceRegisterer getTelephonyServiceRegisterer() { - return new ServiceRegisterer("phone"); - } - - -// TODO: Add more services... -// -// /** -// * Returns {@link ServiceRegisterer} for the "subscription" service. -// */ -// @NonNull -// public ServiceRegisterer getSubscriptionServiceRegisterer() { -// return new ServiceRegisterer("isub"); -// } -// -// /** -// * Returns {@link ServiceRegisterer} for the "SMS" service. -// */ -// @NonNull -// public ServiceRegisterer getSmsServiceRegisterer() { -// return new ServiceRegisterer("isms"); -// } + return new ServiceRegisterer(Context.TELEPHONY_SERVICE); + } + + /** + * Returns {@link ServiceRegisterer} for the telephony registry service. + */ + @NonNull + public ServiceRegisterer getTelephonyRegistryServiceRegisterer() { + return new ServiceRegisterer("telephony.registry"); + } + + /** + * Returns {@link ServiceRegisterer} for the telephony IMS service. + */ + @NonNull + public ServiceRegisterer getTelephonyImsServiceRegisterer() { + return new ServiceRegisterer(Context.TELEPHONY_IMS_SERVICE); + } + + /** + * Returns {@link ServiceRegisterer} for the telephony RCS message service. + */ + @NonNull + public ServiceRegisterer getTelephonyRcsMessageServiceRegisterer() { + return new ServiceRegisterer(Context.TELEPHONY_RCS_MESSAGE_SERVICE); + } + + /** + * Returns {@link ServiceRegisterer} for the subscription service. + */ + @NonNull + public ServiceRegisterer getSubscriptionServiceRegisterer() { + return new ServiceRegisterer("isub"); + } + + /** + * Returns {@link ServiceRegisterer} for the network policy service. + */ + @NonNull + public ServiceRegisterer getNetworkPolicyServiceRegisterer() { + return new ServiceRegisterer(Context.NETWORK_POLICY_SERVICE); + } + + /** + * Returns {@link ServiceRegisterer} for the phone sub service. + */ + @NonNull + public ServiceRegisterer getPhoneSubServiceRegisterer() { + return new ServiceRegisterer("iphonesubinfo"); + } + + /** + * Returns {@link ServiceRegisterer} for the opportunistic network service. + */ + @NonNull + public ServiceRegisterer getOpportunisticNetworkServiceRegisterer() { + return new ServiceRegisterer("ions"); + } + + /** + * Returns {@link ServiceRegisterer} for the carrier config service. + */ + @NonNull + public ServiceRegisterer getCarrierConfigServiceRegisterer() { + return new ServiceRegisterer(Context.CARRIER_CONFIG_SERVICE); + } + + /** + * Returns {@link ServiceRegisterer} for the "SMS" service. + */ + @NonNull + public ServiceRegisterer getSmsServiceRegisterer() { + return new ServiceRegisterer("isms"); + } + + /** + * Returns {@link ServiceRegisterer} for the eUICC controller service. + */ + @NonNull + public ServiceRegisterer getEuiccControllerService() { + return new ServiceRegisterer("econtroller"); + } + + @NonNull + public ServiceRegisterer getEuiccCardControllerServiceRegisterer() { + return new ServiceRegisterer("euicc_card_controller"); + } + + /** + * Returns {@link ServiceRegisterer} for the package manager service. + */ + @NonNull + public ServiceRegisterer getPackageManagerServiceRegisterer() { + return new ServiceRegisterer("package"); + } + + /** + * Returns {@link ServiceRegisterer} for the permission manager service. + */ + @NonNull + public ServiceRegisterer getPermissionManagerServiceRegisterer() { + return new ServiceRegisterer("permissionmgr"); + } + + /** + * Returns {@link ServiceRegisterer} for the ICC phone book service. + */ + @NonNull + public ServiceRegisterer getIccPhoneBookServiceRegisterer() { + return new ServiceRegisterer("simphonebook"); + } + + /** + * Returns {@link ServiceRegisterer} for the window service. + */ + @NonNull + public ServiceRegisterer getWindowServiceRegisterer() { + return new ServiceRegisterer(Context.WINDOW_SERVICE); + } } diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/os/TimestampedValue.java index 45056730b08b..348574ed43c7 100644 --- a/core/java/android/util/TimestampedValue.java +++ b/core/java/android/os/TimestampedValue.java @@ -14,13 +14,10 @@ * limitations under the License. */ -package android.util; +package android.os; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.SystemClock; import java.util.Objects; diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index a9ddffe7d55c..73e1adf134f2 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -16,8 +16,10 @@ package android.os; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.content.res.AssetFileDescriptor; import android.os.IUpdateEngine; import android.os.IUpdateEngineCallback; import android.os.RemoteException; @@ -140,8 +142,43 @@ public class UpdateEngine { * {@code SWITCH_SLOT_ON_REBOOT=0}. See {@link #applyPayload}. */ public static final int UPDATED_BUT_NOT_ACTIVE = 52; + + /** + * Error code: there is not enough space on the device to apply the update. User should + * be prompted to free up space and re-try the update. + * + * <p>See {@link UpdateEngine#allocateSpace}. + */ + public static final int NOT_ENOUGH_SPACE = 60; + + /** + * Error code: the device is corrupted and no further updates may be applied. + * + * <p>See {@link UpdateEngine#cleanupAppliedPayload}. + */ + public static final int DEVICE_CORRUPTED = 61; } + /** @hide */ + @IntDef(value = { + ErrorCodeConstants.SUCCESS, + ErrorCodeConstants.ERROR, + ErrorCodeConstants.FILESYSTEM_COPIER_ERROR, + ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR, + ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR, + ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR, + ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR, + ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR, + ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, + ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR, + ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR, + ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR, + ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE, + ErrorCodeConstants.NOT_ENOUGH_SPACE, + ErrorCodeConstants.DEVICE_CORRUPTED, + }) + public @interface ErrorCode {} + /** * Status codes for update engine. Values must agree with the ones in * {@code system/update_engine/client_library/include/update_engine/update_status.h}. @@ -313,16 +350,17 @@ public class UpdateEngine { } /** - * Applies the payload passed as ParcelFileDescriptor {@code pfd} instead of - * using the {@code file://} scheme. + * Applies the payload passed as AssetFileDescriptor {@code assetFd} + * instead of using the {@code file://} scheme. * * <p>See {@link #applyPayload(String)} for {@code offset}, {@code size} and * {@code headerKeyValuePairs} parameters. */ - public void applyPayload(@NonNull ParcelFileDescriptor pfd, long offset, long size, + public void applyPayload(@NonNull AssetFileDescriptor assetFd, @NonNull String[] headerKeyValuePairs) { try { - mUpdateEngine.applyPayloadFd(pfd, offset, size, headerKeyValuePairs); + mUpdateEngine.applyPayloadFd(assetFd.getParcelFileDescriptor(), + assetFd.getStartOffset(), assetFd.getLength(), headerKeyValuePairs); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -419,4 +457,138 @@ public class UpdateEngine { throw e.rethrowFromSystemServer(); } } + + /** + * Return value of {@link #allocateSpace.} + */ + public static final class AllocateSpaceResult { + private @ErrorCode int mErrorCode = ErrorCodeConstants.SUCCESS; + private long mFreeSpaceRequired = 0; + private AllocateSpaceResult() {} + /** + * Error code. + * + * @return The following error codes: + * <ul> + * <li>{@link ErrorCodeConstants#SUCCESS} if space has been allocated + * successfully.</li> + * <li>{@link ErrorCodeConstants#NOT_ENOUGH_SPACE} if insufficient + * space.</li> + * <li>Other {@link ErrorCodeConstants} for other errors.</li> + * </ul> + */ + @ErrorCode + public int errorCode() { + return mErrorCode; + } + + /** + * Estimated total space that needs to be available on the userdata partition to apply the + * payload (in bytes). + * + * <p> + * Note that in practice, more space needs to be made available before applying the payload + * to keep the device working. + * + * @return The following values: + * <ul> + * <li>zero if {@link #errorCode} returns {@link ErrorCodeConstants#SUCCESS}</li> + * <li>non-zero if {@link #errorCode} returns {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}. + * Value is the estimated total space required on userdata partition.</li> + * </ul> + * @throws IllegalStateException if {@link #errorCode} is not one of the above. + * + */ + public long freeSpaceRequired() { + if (mErrorCode == ErrorCodeConstants.SUCCESS) { + return 0; + } + if (mErrorCode == ErrorCodeConstants.NOT_ENOUGH_SPACE) { + return mFreeSpaceRequired; + } + throw new IllegalStateException(String.format( + "freeSpaceRequired() is not available when error code is %d", mErrorCode)); + } + } + + /** + * Initialize partitions for a payload associated with the given payload + * metadata {@code payloadMetadataFilename} by preallocating required space. + * + * <p>This function should be called after payload has been verified after + * {@link #verifyPayloadMetadata}. This function does not verify whether + * the given payload is applicable or not. + * + * <p>Implementation of {@code allocateSpace} uses + * {@code headerKeyValuePairs} to determine whether space has been allocated + * for a different or same payload previously. If space has been allocated + * for a different payload before, space will be reallocated for the given + * payload. If space has been allocated for the same payload, no actions to + * storage devices are taken. + * + * <p>This function is synchronous and may take a non-trivial amount of + * time. Callers should call this function in a background thread. + * + * @param payloadMetadataFilename See {@link #verifyPayloadMetadata}. + * @param headerKeyValuePairs See {@link #applyPayload}. + * @return See {@link AllocateSpaceResult}. + */ + @NonNull + public AllocateSpaceResult allocateSpace( + @NonNull String payloadMetadataFilename, + @NonNull String[] headerKeyValuePairs) { + AllocateSpaceResult result = new AllocateSpaceResult(); + try { + result.mFreeSpaceRequired = mUpdateEngine.allocateSpaceForPayload( + payloadMetadataFilename, + headerKeyValuePairs); + result.mErrorCode = result.mFreeSpaceRequired == 0 + ? ErrorCodeConstants.SUCCESS + : ErrorCodeConstants.NOT_ENOUGH_SPACE; + return result; + } catch (ServiceSpecificException e) { + result.mErrorCode = e.errorCode; + result.mFreeSpaceRequired = 0; + return result; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Cleanup files used by the previous update and free up space after the + * device has been booted successfully into the new build. + * + * <p>In particular, this function waits until delta files for snapshots for + * Virtual A/B update are merged to OS partitions, then delete these delta + * files. + * + * <p>This function is synchronous and may take a non-trivial amount of + * time. Callers should call this function in a background thread. + * + * <p>This function does not delete payload binaries downloaded for a + * non-streaming OTA update. + * + * @return One of the following: + * <ul> + * <li>{@link ErrorCodeConstants#SUCCESS} if execution is successful.</li> + * <li>{@link ErrorCodeConstants#ERROR} if a transient error has occurred. + * The device should be able to recover after a reboot. The function should + * be retried after the reboot.</li> + * <li>{@link ErrorCodeConstants#DEVICE_CORRUPTED} if a permanent error is + * encountered. Device is corrupted, and future updates must not be applied. + * The device cannot recover without flashing and factory resets. + * </ul> + * + * @throws ServiceSpecificException if other transient errors has occurred. + * A reboot may or may not help resolving the issue. + */ + @ErrorCode + public int cleanupAppliedPayload() { + try { + return mUpdateEngine.cleanupSuccessfulUpdate(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java index f07294e222e2..7fe7024f25b3 100644 --- a/core/java/android/os/UpdateEngineCallback.java +++ b/core/java/android/os/UpdateEngineCallback.java @@ -44,5 +44,6 @@ public abstract class UpdateEngineCallback { * unsuccessfully. The value of {@code errorCode} will be one of the * values from {@link UpdateEngine.ErrorCodeConstants}. */ - public abstract void onPayloadApplicationComplete(int errorCode); + public abstract void onPayloadApplicationComplete( + @UpdateEngine.ErrorCode int errorCode); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 62b8953b158a..3846f894c4c3 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -594,6 +594,8 @@ public class ZygoteProcess { argsForZygote.add("--mount-external-legacy"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) { argsForZygote.add("--mount-external-pass-through"); + } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) { + argsForZygote.add("--mount-external-android-writable"); } argsForZygote.add("--target-sdk-version=" + targetSdkVersion); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 8959fcf7ac18..f0a11748fbd6 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -31,6 +31,7 @@ import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.BytesLong; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -72,6 +73,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.SystemProperties; +import android.os.UserHandle; import android.provider.MediaStore; import android.provider.Settings; import android.sysprop.VoldProperties; @@ -93,7 +95,6 @@ import com.android.internal.os.AppFuseMount; import com.android.internal.os.FuseAppLoop; import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.os.RoSystemProperties; -import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; import dalvik.system.BlockGuard; @@ -114,6 +115,7 @@ import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -305,109 +307,85 @@ public class StorageManager { private final Looper mLooper; private final AtomicInteger mNextNonce = new AtomicInteger(0); + @GuardedBy("mDelegates") private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); - private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements - Handler.Callback { - private static final int MSG_STORAGE_STATE_CHANGED = 1; - private static final int MSG_VOLUME_STATE_CHANGED = 2; - private static final int MSG_VOLUME_RECORD_CHANGED = 3; - private static final int MSG_VOLUME_FORGOTTEN = 4; - private static final int MSG_DISK_SCANNED = 5; - private static final int MSG_DISK_DESTROYED = 6; + private class StorageEventListenerDelegate extends IStorageEventListener.Stub { + final Executor mExecutor; + final StorageEventListener mListener; + final StorageVolumeCallback mCallback; - final StorageEventListener mCallback; - final Handler mHandler; - - public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) { + public StorageEventListenerDelegate(@NonNull Executor executor, + @NonNull StorageEventListener listener, @NonNull StorageVolumeCallback callback) { + mExecutor = executor; + mListener = listener; mCallback = callback; - mHandler = new Handler(looper, this); - } - - @Override - public boolean handleMessage(Message msg) { - final SomeArgs args = (SomeArgs) msg.obj; - switch (msg.what) { - case MSG_STORAGE_STATE_CHANGED: - mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2, - (String) args.arg3); - args.recycle(); - return true; - case MSG_VOLUME_STATE_CHANGED: - mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); - args.recycle(); - return true; - case MSG_VOLUME_RECORD_CHANGED: - mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1); - args.recycle(); - return true; - case MSG_VOLUME_FORGOTTEN: - mCallback.onVolumeForgotten((String) args.arg1); - args.recycle(); - return true; - case MSG_DISK_SCANNED: - mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); - args.recycle(); - return true; - case MSG_DISK_DESTROYED: - mCallback.onDiskDestroyed((DiskInfo) args.arg1); - args.recycle(); - return true; - } - args.recycle(); - return false; } @Override public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { - // Ignored + mExecutor.execute(() -> { + mListener.onUsbMassStorageConnectionChanged(connected); + }); } @Override public void onStorageStateChanged(String path, String oldState, String newState) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = path; - args.arg2 = oldState; - args.arg3 = newState; - mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); + mExecutor.execute(() -> { + mListener.onStorageStateChanged(path, oldState, newState); + + if (path != null) { + for (StorageVolume sv : getStorageVolumes()) { + if (Objects.equals(path, sv.getPath())) { + mCallback.onStateChanged(sv); + } + } + } + }); } @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = vol; - args.argi2 = oldState; - args.argi3 = newState; - mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); + mExecutor.execute(() -> { + mListener.onVolumeStateChanged(vol, oldState, newState); + + final File path = vol.getPathForUser(UserHandle.myUserId()); + if (path != null) { + for (StorageVolume sv : getStorageVolumes()) { + if (Objects.equals(path.getAbsolutePath(), sv.getPath())) { + mCallback.onStateChanged(sv); + } + } + } + }); } @Override public void onVolumeRecordChanged(VolumeRecord rec) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = rec; - mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); + mExecutor.execute(() -> { + mListener.onVolumeRecordChanged(rec); + }); } @Override public void onVolumeForgotten(String fsUuid) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = fsUuid; - mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); + mExecutor.execute(() -> { + mListener.onVolumeForgotten(fsUuid); + }); } @Override public void onDiskScanned(DiskInfo disk, int volumeCount) { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = disk; - args.argi2 = volumeCount; - mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); + mExecutor.execute(() -> { + mListener.onDiskScanned(disk, volumeCount); + }); } @Override public void onDiskDestroyed(DiskInfo disk) throws RemoteException { - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = disk; - mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); + mExecutor.execute(() -> { + mListener.onDiskDestroyed(disk); + }); } } @@ -525,8 +503,8 @@ public class StorageManager { @UnsupportedAppUsage public void registerListener(StorageEventListener listener) { synchronized (mDelegates) { - final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener, - mLooper); + final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate( + mContext.getMainExecutor(), listener, new StorageVolumeCallback()); try { mStorageManager.registerListener(delegate); } catch (RemoteException e) { @@ -548,7 +526,76 @@ public class StorageManager { synchronized (mDelegates) { for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { final StorageEventListenerDelegate delegate = i.next(); - if (delegate.mCallback == listener) { + if (delegate.mListener == listener) { + try { + mStorageManager.unregisterListener(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + i.remove(); + } + } + } + } + + /** + * Callback that delivers {@link StorageVolume} related events. + * <p> + * For example, this can be used to detect when a volume changes to the + * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED} + * states. + * + * @see StorageManager#registerStorageVolumeCallback + * @see StorageManager#unregisterStorageVolumeCallback + */ + public static class StorageVolumeCallback { + /** + * Called when {@link StorageVolume#getState()} changes, such as + * changing to the {@link Environment#MEDIA_MOUNTED} or + * {@link Environment#MEDIA_UNMOUNTED} states. + * <p> + * The given argument is a snapshot in time and can be used to process + * events in the order they occurred, or you can call + * {@link StorageManager#getStorageVolumes()} to observe the latest + * value. + */ + public void onStateChanged(@NonNull StorageVolume volume) { } + } + + /** + * Registers the given callback to listen for {@link StorageVolume} changes. + * <p> + * For example, this can be used to detect when a volume changes to the + * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED} + * states. + * + * @see StorageManager#unregisterStorageVolumeCallback + */ + public void registerStorageVolumeCallback(@CallbackExecutor @NonNull Executor executor, + @NonNull StorageVolumeCallback callback) { + synchronized (mDelegates) { + final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate( + executor, new StorageEventListener(), callback); + try { + mStorageManager.registerListener(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mDelegates.add(delegate); + } + } + + /** + * Unregisters the given callback from listening for {@link StorageVolume} + * changes. + * + * @see StorageManager#registerStorageVolumeCallback + */ + public void unregisterStorageVolumeCallback(@NonNull StorageVolumeCallback callback) { + synchronized (mDelegates) { + for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { + final StorageEventListenerDelegate delegate = i.next(); + if (delegate.mCallback == callback) { try { mStorageManager.unregisterListener(delegate); } catch (RemoteException e) { @@ -829,7 +876,14 @@ public class StorageManager { */ public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException { Preconditions.checkNotNull(path); - final String pathString = path.getCanonicalPath(); + String pathString = path.getCanonicalPath(); + if (path.getPath().startsWith("/sdcard")) { + // On FUSE enabled devices, realpath(2) /sdcard is /mnt/user/<userid>/emulated/<userid> + // as opposed to /storage/emulated/<userid>. + // And vol.path below expects to match with a path starting with /storage + pathString = pathString.replaceFirst("^/mnt/user/[0-9]+/", "/storage/"); + } + if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) { return UUID_DEFAULT; } diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 2ab226f81bb4..e251f8072b1f 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -18,6 +18,7 @@ package android.os.storage; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -162,9 +163,13 @@ public final class StorageVolume implements Parcelable { mState = in.readString(); } - /** {@hide} */ - @UnsupportedAppUsage - public String getId() { + /** + * Return an opaque ID that can be used to identify this volume. + * + * @hide + */ + @SystemApi + public @NonNull String getId() { return mId; } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 0a3c333ae7b1..ef8a2860cf4f 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -553,7 +553,9 @@ public final class DocumentsContract { /** * Flag indicating that a document is a directory that wants to block itself * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} - * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}. + * intent. Individual files can still be selected when launched via other intents + * like {@link Intent#ACTION_OPEN_DOCUMENT} and {@link Intent#ACTION_GET_CONTENT}. + * Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}. * <p> * Note that this flag <em>only</em> applies to the single directory to which it is * applied. It does <em>not</em> block the user from selecting either a parent or diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index bbaf94ad6ea0..06805238e6f5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -68,7 +68,6 @@ import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.UserHandle; import android.speech.tts.TextToSpeech; -import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.AndroidException; import android.util.ArrayMap; @@ -219,7 +218,9 @@ public final class Settings { * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_TETHER_PROVISIONING = + @SystemApi + @TestApi + public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; /** @@ -9698,6 +9699,8 @@ public final class Settings { * is interpreted as |false|. * @hide */ + @SystemApi + @TestApi public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled"; /** @@ -12459,16 +12462,17 @@ public final class Settings { /** * Whether the Volte is enabled. If this setting is not set then we use the Carrier Config - * value {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. + * value + * {@link android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. * <p> * Type: int (0 for false, 1 for true) * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED} + * @deprecated Use {@link android.provider.Telephony.SimInfo#ENHANCED_4G_MODE_ENABLED} * instead. */ @Deprecated public static final String ENHANCED_4G_MODE_ENABLED = - SubscriptionManager.ENHANCED_4G_MODE_ENABLED; + Telephony.SimInfo.ENHANCED_4G_MODE_ENABLED; /** * Whether VT (Video Telephony over IMS) is enabled @@ -12476,10 +12480,10 @@ public final class Settings { * Type: int (0 for false, 1 for true) * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead. + * @deprecated Use {@link android.provider.Telephony.SimInfo#VT_IMS_ENABLED} instead. */ @Deprecated - public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED; + public static final String VT_IMS_ENABLED = Telephony.SimInfo.VT_IMS_ENABLED; /** * Whether WFC is enabled @@ -12487,10 +12491,10 @@ public final class Settings { * Type: int (0 for false, 1 for true) * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead. + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ENABLED} instead. */ @Deprecated - public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED; + public static final String WFC_IMS_ENABLED = Telephony.SimInfo.WFC_IMS_ENABLED; /** * WFC mode on home/non-roaming network. @@ -12498,10 +12502,10 @@ public final class Settings { * Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead. + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_MODE} instead. */ @Deprecated - public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE; + public static final String WFC_IMS_MODE = Telephony.SimInfo.WFC_IMS_MODE; /** * WFC mode on roaming network. @@ -12509,11 +12513,11 @@ public final class Settings { * Type: int - see {@link #WFC_IMS_MODE} for values * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE} + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_MODE} * instead. */ @Deprecated - public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE; + public static final String WFC_IMS_ROAMING_MODE = Telephony.SimInfo.WFC_IMS_ROAMING_MODE; /** * Whether WFC roaming is enabled @@ -12521,12 +12525,12 @@ public final class Settings { * Type: int (0 for false, 1 for true) * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED} + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_ENABLED} * instead */ @Deprecated public static final String WFC_IMS_ROAMING_ENABLED = - SubscriptionManager.WFC_IMS_ROAMING_ENABLED; + Telephony.SimInfo.WFC_IMS_ROAMING_ENABLED; /** * Whether user can enable/disable LTE as a preferred network. A carrier might control @@ -14400,6 +14404,42 @@ public final class Settings { }; /** + * Activity Action: Show screen for controlling which apps have access to manage external + * storage. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard against this. + * <p> + * If you want to control a specific app's access to manage external storage, use + * {@link #ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION} instead. + * <p> + * Output: Nothing. + * @see #ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION = + "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"; + + /** + * Activity Action: Show screen for controlling if the app specified in the data URI of the + * intent can manage external storage. + * <p> + * Launching the corresponding activity requires the permission + * {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard against this. + * <p> + * Input: The Intent's data URI MUST specify the application package name whose ability of + * managing external storage you want to control. + * For example "package:com.my.app". + * <p> + * Output: Nothing. + * @see #ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = + "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION"; + + /** * Performs a strict and comprehensive check of whether a calling package is allowed to * write/modify system settings, as the condition differs for pre-M, M+, and * privileged/preinstalled apps. If the provided uid does not match the @@ -14425,8 +14465,9 @@ public final class Settings { * current time. * @hide */ - public static boolean checkAndNoteWriteSettingsOperation(Context context, int uid, - String callingPackage, boolean throwException) { + @SystemApi + public static boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, + @NonNull String callingPackage, boolean throwException) { return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid, callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS, PM_WRITE_SETTINGS, true); diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 1d9bdb81a05f..2e7ac3f505fa 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -38,11 +38,13 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Parcel; +import android.telephony.CarrierConfigManager; import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.SmsMessage; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.UiccAccessRule; import android.text.TextUtils; import android.util.Patterns; @@ -4980,5 +4982,402 @@ public final class Telephony { */ @NonNull public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo"); + + /** + * TelephonyProvider unique key column name is the subscription id. + * <P>Type: TEXT (String)</P> + */ + public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id"; + + /** + * TelephonyProvider column name for a unique identifier for the subscription within the + * specific subscription type. For example, it contains SIM ICC Identifier subscriptions + * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices. + * <P>Type: TEXT (String)</P> + */ + public static final String ICC_ID = "icc_id"; + + /** + * TelephonyProvider column name for user SIM_SlOT_INDEX + * <P>Type: INTEGER (int)</P> + */ + public static final String SIM_SLOT_INDEX = "sim_id"; + + /** + * SIM is not inserted + */ + public static final int SIM_NOT_INSERTED = -1; + + /** + * TelephonyProvider column name Subscription-type. + * <P>Type: INTEGER (int)</P> {@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM + * Subscriptions, {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions. + * Default value is 0. + */ + public static final String SUBSCRIPTION_TYPE = "subscription_type"; + + /** + * This constant is to designate a subscription as a Local-SIM Subscription. + * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on + * the device. + * </p> + */ + public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; + + /** + * This constant is to designate a subscription as a Remote-SIM Subscription. + * <p> + * A Remote-SIM subscription is for a SIM on a phone connected to this device via some + * connectivity mechanism, for example bluetooth. Similar to Local SIM, this subscription + * can be used for SMS, Voice and data by proxying data through the connected device. + * Certain data of the SIM, such as IMEI, are not accessible for Remote SIMs. + * </p> + * + * <p> + * A Remote-SIM is available only as long the phone stays connected to this device. + * When the phone disconnects, Remote-SIM subscription is removed from this device and is + * no longer known. All data associated with the subscription, such as stored SMS, call + * logs, contacts etc, are removed from this device. + * </p> + * + * <p> + * If the phone re-connects to this device, a new Remote-SIM subscription is created for + * the phone. The Subscription Id associated with the new subscription is different from + * the Subscription Id of the previous Remote-SIM subscription created (and removed) for the + * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM + * that was never seen before. + * </p> + */ + public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; + + /** + * TelephonyProvider column name data_enabled_override_rules. + * It's a list of rules for overriding data enabled settings. The syntax is + * For example, "mms=nonDefault" indicates enabling data for mms in non-default + * subscription. + * "default=nonDefault&inVoiceCall" indicates enabling data for internet in non-default + * subscription and while is in voice call. + * + * Default value is empty string. + */ + public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules"; + + /** + * TelephonyProvider column name for user displayed name. + * <P>Type: TEXT (String)</P> + */ + public static final String DISPLAY_NAME = "display_name"; + + /** + * TelephonyProvider column name for the service provider name for the SIM. + * <P>Type: TEXT (String)</P> + */ + public static final String CARRIER_NAME = "carrier_name"; + + /** + * TelephonyProvider column name for source of the user displayed name. + * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below + */ + public static final String NAME_SOURCE = "name_source"; + + /** The name_source is the default, which is from the carrier id. */ + public static final int NAME_SOURCE_DEFAULT = 0; + + /** + * The name_source is from SIM EF_SPN. + */ + public static final int NAME_SOURCE_SIM_SPN = 1; + + /** + * The name_source is from user input + */ + public static final int NAME_SOURCE_USER_INPUT = 2; + + /** + * The name_source is carrier (carrier app, carrier config, etc.) + */ + public static final int NAME_SOURCE_CARRIER = 3; + + /** + * The name_source is from SIM EF_PNN. + */ + public static final int NAME_SOURCE_SIM_PNN = 4; + + /** + * TelephonyProvider column name for the color of a SIM. + * <P>Type: INTEGER (int)</P> + */ + public static final String COLOR = "color"; + + /** TelephonyProvider column name for the default color of a SIM {@hide} */ + public static final int COLOR_DEFAULT = 0; + + /** + * TelephonyProvider column name for the phone number of a SIM. + * <P>Type: TEXT (String)</P> + */ + public static final String NUMBER = "number"; + + /** + * TelephonyProvider column name for the number display format of a SIM. + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String DISPLAY_NUMBER_FORMAT = "display_number_format"; + + /** + * TelephonyProvider column name for the default display format of a SIM + * @hide + */ + public static final int DISPLAY_NUMBER_DEFAULT = 1; + + /** + * TelephonyProvider column name for whether data roaming is enabled. + * <P>Type: INTEGER (int)</P> + */ + public static final String DATA_ROAMING = "data_roaming"; + + /** Indicates that data roaming is enabled for a subscription */ + public static final int DATA_ROAMING_ENABLE = 1; + + /** Indicates that data roaming is disabled for a subscription */ + public static final int DATA_ROAMING_DISABLE = 0; + + /** TelephonyProvider column name for default data roaming setting: disable */ + public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE; + + /** + * TelephonyProvider column name for subscription carrier id. + * @see TelephonyManager#getSimCarrierId() + * <p>Type: INTEGER (int) </p> + */ + public static final String CARRIER_ID = "carrier_id"; + + /** + * A comma-separated list of EHPLMNs associated with the subscription + * <P>Type: TEXT (String)</P> + */ + public static final String EHPLMNS = "ehplmns"; + + /** + * A comma-separated list of HPLMNs associated with the subscription + * <P>Type: TEXT (String)</P> + */ + public static final String HPLMNS = "hplmns"; + + /** + * TelephonyProvider column name for the MCC associated with a SIM, stored as a string. + * <P>Type: TEXT (String)</P> + */ + public static final String MCC_STRING = "mcc_string"; + + /** + * TelephonyProvider column name for the MNC associated with a SIM, stored as a string. + * <P>Type: TEXT (String)</P> + */ + public static final String MNC_STRING = "mnc_string"; + + /** + * TelephonyProvider column name for the MCC associated with a SIM. + * <P>Type: INTEGER (int)</P> + */ + public static final String MCC = "mcc"; + + /** + * TelephonyProvider column name for the MNC associated with a SIM. + * <P>Type: INTEGER (int)</P> + */ + public static final String MNC = "mnc"; + + /** + * TelephonyProvider column name for the iso country code associated with a SIM. + * <P>Type: TEXT (String)</P> + */ + public static final String ISO_COUNTRY_CODE = "iso_country_code"; + + /** + * TelephonyProvider column name for the sim provisioning status associated with a SIM. + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status"; + + /** The sim is provisioned {@hide} */ + public static final int SIM_PROVISIONED = 0; + + /** + * TelephonyProvider column name for whether a subscription is embedded (that is, present on + * an eSIM). + * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded. + */ + public static final String IS_EMBEDDED = "is_embedded"; + + /** + * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of + * the current enabled profile on the card, while for eUICC card it is the EID of the card. + * <P>Type: TEXT (String)</P> + */ + public static final String CARD_ID = "card_id"; + + /** + * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from + * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1. + * <p>TYPE: BLOB + */ + public static final String ACCESS_RULES = "access_rules"; + + /** + * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from + * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs. + * Only present if there are access rules in CarrierConfigs + * <p>TYPE: BLOB + */ + public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = + "access_rules_from_carrier_configs"; + + /** + * TelephonyProvider column name identifying whether an embedded subscription is on a + * removable card. Such subscriptions are marked inaccessible as soon as the current card + * is removed. Otherwise, they will remain accessible unless explicitly deleted. Only + * present if {@link #IS_EMBEDDED} is 1. + * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable. + */ + public static final String IS_REMOVABLE = "is_removable"; + + /** TelephonyProvider column name for extreme threat in CB settings */ + public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts"; + + /** TelephonyProvider column name for severe threat in CB settings */ + public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts"; + + /** TelephonyProvider column name for amber alert in CB settings */ + public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts"; + + /** TelephonyProvider column name for emergency alert in CB settings */ + public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts"; + + /** TelephonyProvider column name for alert sound duration in CB settings */ + public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration"; + + /** TelephonyProvider column name for alert reminder interval in CB settings */ + public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; + + /** TelephonyProvider column name for enabling vibrate in CB settings */ + public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate"; + + /** TelephonyProvider column name for enabling alert speech in CB settings */ + public static final String CB_ALERT_SPEECH = "enable_alert_speech"; + + /** TelephonyProvider column name for ETWS test alert in CB settings */ + public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts"; + + /** TelephonyProvider column name for enable channel50 alert in CB settings */ + public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts"; + + /** TelephonyProvider column name for CMAS test alert in CB settings */ + public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts"; + + /** TelephonyProvider column name for Opt out dialog in CB settings */ + public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; + + /** + * TelephonyProvider column name for enable Volte. + * + * If this setting is not initialized (set to -1) then we use the Carrier Config value + * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. + */ + public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled"; + + /** TelephonyProvider column name for enable VT (Video Telephony over IMS) */ + public static final String VT_IMS_ENABLED = "vt_ims_enabled"; + + /** TelephonyProvider column name for enable Wifi calling */ + public static final String WFC_IMS_ENABLED = "wfc_ims_enabled"; + + /** TelephonyProvider column name for Wifi calling mode */ + public static final String WFC_IMS_MODE = "wfc_ims_mode"; + + /** TelephonyProvider column name for Wifi calling mode in roaming */ + public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode"; + + /** TelephonyProvider column name for enable Wifi calling in roaming */ + public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled"; + + /** + * TelephonyProvider column name for whether a subscription is opportunistic, that is, + * whether the network it connects to is limited in functionality or coverage. + * For example, CBRS. + * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic. + */ + public static final String IS_OPPORTUNISTIC = "is_opportunistic"; + + /** + * TelephonyProvider column name for group ID. Subscriptions with same group ID + * are considered bundled together, and should behave as a single subscription at + * certain scenarios. + */ + public static final String GROUP_UUID = "group_uuid"; + + /** + * TelephonyProvider column name for group owner. It's the package name who created + * the subscription group. + */ + public static final String GROUP_OWNER = "group_owner"; + + /** + * TelephonyProvider column name for whether a subscription is metered or not, that is, + * whether the network it connects to charges for subscription or not. For example, paid + * CBRS or unpaid. + * @hide + */ + public static final String IS_METERED = "is_metered"; + + /** + * TelephonyProvider column name for the profile class of a subscription + * Only present if {@link #IS_EMBEDDED} is 1. + * <P>Type: INTEGER (int)</P> + */ + public static final String PROFILE_CLASS = "profile_class"; + + /** + * A testing profile can be pre-loaded or downloaded onto + * the eUICC and provides connectivity to test equipment + * for the purpose of testing the device and the eUICC. It + * is not intended to store any operator credentials. + */ + public static final int PROFILE_CLASS_TESTING = 0; + + /** + * A provisioning profile is pre-loaded onto the eUICC and + * provides connectivity to a mobile network solely for the + * purpose of provisioning profiles. + */ + public static final int PROFILE_CLASS_PROVISIONING = 1; + + /** + * An operational profile can be pre-loaded or downloaded + * onto the eUICC and provides services provided by the + * operator. + */ + public static final int PROFILE_CLASS_OPERATIONAL = 2; + + /** + * The profile class is unset. This occurs when profile class + * info is not available. The subscription either has no profile + * metadata or the profile metadata did not encode profile class. + */ + public static final int PROFILE_CLASS_UNSET = -1; + + /** Default profile class */ + public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET; + + /** + * IMSI (International Mobile Subscriber Identity). + * <P>Type: TEXT </P> + */ + public static final String IMSI = "imsi"; + + /** Whether uicc applications is set to be enabled or disabled. By default it's enabled. */ + public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled"; } } diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index d51e4ca6398f..939ae878816a 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -609,10 +609,12 @@ public final class FillResponse implements Parcelable { "must add at least 1 dataset when using header or footer"); } - for (final Dataset dataset : mDatasets) { - if (dataset.getFieldInlinePresentation(0) != null) { - mSupportsInlineSuggestions = true; - break; + if (mDatasets != null) { + for (final Dataset dataset : mDatasets) { + if (dataset.getFieldInlinePresentation(0) != null) { + mSupportsInlineSuggestions = true; + break; + } } } diff --git a/core/java/android/telephony/WapPushManagerConnector.java b/core/java/android/telephony/WapPushManagerConnector.java new file mode 100644 index 000000000000..a9df50685d5d --- /dev/null +++ b/core/java/android/telephony/WapPushManagerConnector.java @@ -0,0 +1,178 @@ +/* + * 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.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SystemApi; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; + +import com.android.internal.telephony.IWapPushManager; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * APIs for platform to connect to the WAP push manager service. + * + * <p>To start connection, {@link #bindToWapPushManagerService} should be called. + * + * <p>Upon completion {@link #unbindWapPushManagerService} should be called to unbind the service. + * + * @hide + */ +@SystemApi +public final class WapPushManagerConnector { + private final Context mContext; + + private volatile WapPushManagerConnection mConnection; + private volatile IWapPushManager mWapPushManager; + private String mWapPushManagerPackage; + + /** + * The {@link android.content.Intent} that must be declared as handled by the + * WAP push manager service. + * @hide + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = + "com.android.internal.telephony.IWapPushManager"; + + /** @hide */ + @IntDef(flag = true, prefix = {"RESULT_"}, value = { + RESULT_MESSAGE_HANDLED, + RESULT_APP_QUERY_FAILED, + RESULT_SIGNATURE_NO_MATCH, + RESULT_INVALID_RECEIVER_NAME, + RESULT_EXCEPTION_CAUGHT, + RESULT_FURTHER_PROCESSING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProcessMessageResult{} + + /** {@link #processMessage} return value: Message is handled. */ + public static final int RESULT_MESSAGE_HANDLED = 0x1; + /** {@link #processMessage} return value: Application ID or content type was not found. */ + public static final int RESULT_APP_QUERY_FAILED = 0x2; + /** {@link #processMessage} return value: Receiver application signature check failed. */ + public static final int RESULT_SIGNATURE_NO_MATCH = 0x4; + /** {@link #processMessage} return value: Receiver application was not found. */ + public static final int RESULT_INVALID_RECEIVER_NAME = 0x8; + /** {@link #processMessage} return value: Unknown exception. */ + public static final int RESULT_EXCEPTION_CAUGHT = 0x10; + /** {@link #processMessage} return value: further processing needed. */ + public static final int RESULT_FURTHER_PROCESSING = 0x8000; + + /** The application package name of the WAP push manager service. */ + private static final String SERVICE_PACKAGE = "com.android.smspush"; + + public WapPushManagerConnector(@NonNull Context context) { + mContext = context; + } + + /** + * Binds to the WAP push manager service. This method should be called exactly once. + * + * @return {@code true} upon successfully binding to a service, {@code false} otherwise + */ + public boolean bindToWapPushManagerService() { + Preconditions.checkState(mConnection == null); + + Intent intent = new Intent(SERVICE_INTERFACE); + ComponentName component = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(component); + mConnection = new WapPushManagerConnection(); + if (component != null + && mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { + mWapPushManagerPackage = component.getPackageName(); + return true; + } + return false; + } + + /** + * Returns the package name of WAP push manager service application connected to, + * or {@code null} if not connected. + */ + @Nullable + public String getConnectedWapPushManagerServicePackage() { + return mWapPushManagerPackage; + } + + /** + * Processes WAP push message and triggers the {@code intent}. + * + * @see RESULT_MESSAGE_HANDLED + * @see RESULT_APP_QUERY_FAILED + * @see RESULT_SIGNATURE_NO_MATCH + * @see RESULT_INVALID_RECEIVER_NAME + * @see RESULT_EXCEPTION_CAUGHT + * @see RESULT_FURTHER_PROCESSING + */ + @ProcessMessageResult + public int processMessage( + @NonNull String applicationId, @NonNull String contentType, @NonNull Intent intent) { + try { + return mWapPushManager.processMessage(applicationId, contentType, intent); + } catch (NullPointerException | RemoteException e) { + return RESULT_EXCEPTION_CAUGHT; + } + } + + /** + * Unbinds the WAP push manager service. This method should be called exactly once. + */ + public void unbindWapPushManagerService() { + Preconditions.checkNotNull(mConnection); + + mContext.unbindService(mConnection); + mConnection = null; + } + + private class WapPushManagerConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + // Because we have bound to an explicit + // service that is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + mWapPushManager = IWapPushManager.Stub.asInterface(service); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mWapPushManager = null; + } + + @Override + public void onNullBinding(ComponentName name) { + onServiceDisconnected(name); + } + + @Override + public void onBindingDied(ComponentName name) { + onServiceDisconnected(name); + } + } +} diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index b85ef76bd2d5..4c9328a1c372 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -40,9 +40,10 @@ import java.lang.reflect.Array; if (source instanceof Spanned) { if (source instanceof SpannableStringInternal) { - copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan); + copySpansFromInternal( + (SpannableStringInternal) source, start, end, ignoreNoCopySpan); } else { - copySpans((Spanned) source, start, end, ignoreNoCopySpan); + copySpansFromSpanned((Spanned) source, start, end, ignoreNoCopySpan); } } } @@ -65,7 +66,7 @@ import java.lang.reflect.Array; * @param end End index in the source object. * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} */ - private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) { + private void copySpansFromSpanned(Spanned src, int start, int end, boolean ignoreNoCopySpan) { Object[] spans = src.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { @@ -94,7 +95,7 @@ import java.lang.reflect.Array; * @param end End index in the source object. * @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons. */ - private void copySpans(SpannableStringInternal src, int start, int end, + private void copySpansFromInternal(SpannableStringInternal src, int start, int end, boolean ignoreNoCopySpan) { int count = 0; final int[] srcData = src.mSpanData; @@ -555,12 +556,12 @@ import java.lang.reflect.Array; */ @UnsupportedAppUsage private void copySpans(Spanned src, int start, int end) { - copySpans(src, start, end, false); + copySpansFromSpanned(src, start, end, false); } @UnsupportedAppUsage private void copySpans(SpannableStringInternal src, int start, int end) { - copySpans(src, start, end, false); + copySpansFromInternal(src, start, end, false); } diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index 3f679bba88cd..f324113da925 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -387,6 +387,26 @@ public final class Log { public static native int println_native(int bufID, int priority, String tag, String msg); /** + * Send a log message to the "radio" log buffer, which can be dumped with + * {@code adb logcat -b radio}. + * + * <p>Only the telephony mainline module should use it. + * + * <p>Note ART will protect {@code MODULE_LIBRARIES} system APIs from regular app code. + * + * @param priority Log priority. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param message The message you would like logged. + * @hide + */ + // @SystemApi(client= SystemApi.Client.MODULE_LIBRARIES) // TODO Uncomment once http://ag/9956147 is in. + public static int logToRadioBuffer(@Level int priority, @Nullable String tag, + @Nullable String message) { + return println_native(LOG_ID_RADIO, priority, tag, message); + } + + /** * Return the maximum payload the log daemon accepts without truncation. * @return LOGGER_ENTRY_MAX_PAYLOAD. */ diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 2223d14ad658..fa994ba76fd7 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -25,6 +25,7 @@ import android.net.Network; import android.net.NetworkInfo; import android.net.SntpClient; import android.os.SystemClock; +import android.os.TimestampedValue; import android.provider.Settings; import android.text.TextUtils; diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl new file mode 100644 index 000000000000..429c3aeba9b3 --- /dev/null +++ b/core/java/android/view/IDisplayWindowInsetsController.aidl @@ -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 android.view; + +import android.view.InsetsSourceControl; +import android.view.InsetsState; + +/** + * Singular controller of insets to use when there isn't another obvious controller available. + * Specifically, this will take over insets control in multi-window. + * @hide + */ +oneway interface IDisplayWindowInsetsController { + + /** + * @see IWindow#insetsChanged + */ + void insetsChanged(in InsetsState insetsState); + + /** + * @see IWindow#insetsControlChanged + */ + void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls); + + /** + * @see IWindow#showInsets + */ + void showInsets(int types, boolean fromIme); + + /** + * @see IWindow#hideInsets + */ + void hideInsets(int types, boolean fromIme); +} diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 9496827b1a84..993bdc4d6543 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -35,6 +35,7 @@ import android.os.ParcelFileDescriptor; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDockedStackListener; +import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayFoldListener; import android.view.IDisplayWindowRotationController; @@ -49,6 +50,7 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.KeyEvent; import android.view.InputEvent; +import android.view.InsetsState; import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.InputChannel; @@ -711,4 +713,16 @@ interface IWindowManager * @return true if the display was successfully mirrored. */ boolean mirrorDisplay(int displayId, out SurfaceControl outSurfaceControl); + + /** + * When in multi-window mode, the provided displayWindowInsetsController will control insets + * animations. + */ + void setDisplayWindowInsetsController( + int displayId, in IDisplayWindowInsetsController displayWindowInsetsController); + + /** + * Called when a remote process modifies insets on a display window container. + */ + void modifyDisplayWindowInsets(int displayId, in InsetsState state); } diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java index 6fdadc60afea..27edb0b69bdd 100644 --- a/core/java/android/view/InsetsAnimationControlCallbacks.java +++ b/core/java/android/view/InsetsAnimationControlCallbacks.java @@ -16,17 +16,28 @@ package android.view; +import android.view.InsetsController.LayoutInsetsDuringAnimation; +import android.view.WindowInsetsAnimationCallback.AnimationBounds; +import android.view.WindowInsetsAnimationCallback.InsetsAnimation; + /** * Provide an interface to let InsetsAnimationControlImpl call back into its owner. * @hide */ public interface InsetsAnimationControlCallbacks { + /** - * Dispatch the animation started event to all listeners. - * @param animation + * Executes the necessary code to start the animation in the correct order, including: + * <ul> + * <li>Dispatch {@link WindowInsetsAnimationCallback#onPrepare}</li> + * <li>Update insets state and run layout according to {@code layoutDuringAnimation}</li> + * <li>Dispatch {@link WindowInsetsAnimationCallback#onStart}</li> + * <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li> + * </ul> */ - void dispatchAnimationStarted(WindowInsetsAnimationCallback.InsetsAnimation animation, - WindowInsetsAnimationCallback.AnimationBounds bounds); + void startAnimation(InsetsAnimationControlImpl controller, + WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation, + AnimationBounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation); /** * Schedule the apply by posting the animation callback. diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 771695c2b873..6589e75c7bc2 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; +import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; import static android.view.InsetsState.ISIDE_BOTTOM; import static android.view.InsetsState.ISIDE_FLOATING; import static android.view.InsetsState.ISIDE_LEFT; @@ -30,6 +32,7 @@ import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseSetArray; +import android.view.InsetsController.LayoutInsetsDuringAnimation; import android.view.InsetsState.InternalInsetsSide; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowInsets.Type.InsetsType; @@ -80,7 +83,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, - InsetsAnimationControlCallbacks controller, long durationMs, boolean fade) { + InsetsAnimationControlCallbacks controller, long durationMs, boolean fade, + @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { mControls = controls; mListener = listener; mTypes = types; @@ -95,14 +99,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mFrame = new Rect(frame); buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls); - // TODO: Check for controllability first and wait for IME if needed. - listener.onReady(this, types); - mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, InsetsController.INTERPOLATOR, durationMs); mAnimation.setAlpha(getCurrentAlpha()); - mController.dispatchAnimationStarted(mAnimation, - new AnimationBounds(mHiddenInsets, mShownInsets)); + mController.startAnimation(this, listener, types, mAnimation, + new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation); } @Override @@ -257,10 +258,6 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll for (int i = items.size() - 1; i >= 0; i--) { final InsetsSourceControl control = items.valueAt(i); final InsetsSource source = mInitialInsetsState.getSource(control.getType()); - if (control == null) { - // TODO: remove this check when we ensure the elements will not be null. - continue; - } final SurfaceControl leash = control.getLeash(); mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 4d4ace27cac9..0207abdda355 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -37,6 +37,7 @@ import android.util.SparseArray; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; +import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimationCallback.AnimationBounds; @@ -47,6 +48,8 @@ import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; /** @@ -67,6 +70,37 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private @interface AnimationDirection{} /** + * Layout mode during insets animation: The views should be laid out as if the changing inset + * types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will + * be called as if the changing insets types are shown, which will result in the views being + * laid out as if the insets are fully shown. + */ + static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0; + + /** + * Layout mode during insets animation: The views should be laid out as if the changing inset + * types are fully hidden. Before starting the animation, {@link View#onApplyWindowInsets} will + * be called as if the changing insets types are hidden, which will result in the views being + * laid out as if the insets are fully hidden. + */ + static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1; + + /** + * Determines the behavior of how the views should be laid out during an insets animation that + * is controlled by the application by calling {@link #controlWindowInsetsAnimation}. + * <p> + * When the animation is system-initiated, the layout mode is always chosen such that the + * pre-animation layout will represent the opposite of the starting state, i.e. when insets + * are appearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_SHOWN} will be used. When insets + * are disappearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_HIDDEN} will be used. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = {LAYOUT_INSETS_DURING_ANIMATION_SHOWN, + LAYOUT_INSETS_DURING_ANIMATION_HIDDEN}) + @interface LayoutInsetsDuringAnimation { + } + + /** * Translation animation evaluator. */ private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of( @@ -109,11 +143,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void onReady(WindowInsetsAnimationController controller, int types) { mController = controller; - if (mShow) { - showDirectly(types); - } else { - hideDirectly(types); - } + mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE; mAnimator = ObjectAnimator.ofObject( controller, @@ -131,7 +161,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation onAnimationFinish(); } }); + mStartingAnimation = true; mAnimator.start(); + mStartingAnimation = false; } @Override @@ -185,6 +217,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mPendingTypesToShow; private int mLastLegacySoftInputMode; + private boolean mStartingAnimation; private SyncRtSurfaceTransactionApplier mApplier; @@ -312,7 +345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // Only one animator (with multiple InsetsType) can run at a time. // previous one should be cancelled for simplicity. cancelExistingAnimation(); - } else if (consumer.isVisible() + } else if (consumer.isRequestedVisible() && (mAnimationDirection == DIRECTION_NONE || mAnimationDirection == DIRECTION_HIDE)) { // no-op: already shown or animating in (because window visibility is @@ -338,7 +371,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); if (mAnimationDirection == DIRECTION_SHOW) { cancelExistingAnimation(); - } else if (!consumer.isVisible() + } else if (!consumer.isRequestedVisible() && (mAnimationDirection == DIRECTION_NONE || mAnimationDirection == DIRECTION_HIDE)) { // no-op: already hidden or animating out. @@ -363,20 +396,21 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation listener.onCancelled(); return; } - controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */); + controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */, + getLayoutInsetsDuringAnimationMode(types)); } private void controlAnimationUnchecked(@InsetsType int types, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, - long durationMs, boolean fade) { + long durationMs, boolean fade, + @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) { if (types == 0) { // nothing to animate. return; } cancelExistingControllers(types); - final ArraySet<Integer> internalTypes = mState.toInternalType(types); - final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>(); + final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); Pair<Integer, Boolean> typesReadyPair = collectSourceControls( @@ -399,7 +433,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls, - frame, mState, listener, typesReady, this, durationMs, fade); + frame, mState, listener, typesReady, this, durationMs, fade, + layoutInsetsDuringAnimation); mAnimationControls.add(controller); } @@ -413,7 +448,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean isReady = true; for (int i = internalTypes.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); - boolean setVisible = !consumer.isVisible(); + boolean setVisible = !consumer.isRequestedVisible(); if (setVisible) { // Show request switch(consumer.requestShow(fromIme)) { @@ -441,7 +476,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } typesReady |= InsetsState.toPublicType(consumer.getType()); } - controls.put(consumer.getType(), consumer.getControl()); + final InsetsSourceControl control = consumer.getControl(); + if (control != null) { + controls.put(consumer.getType(), control); + } } return new Pair<>(typesReady, isReady); } @@ -452,6 +490,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return typesReady; } + private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode( + @InsetsType int types) { + + final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); + + // Generally, we want to layout the opposite of the current state. This is to make animation + // callbacks easy to use: The can capture the layout values and then treat that as end-state + // during the animation. + // + // However, if controlling multiple sources, we want to treat it as shown if any of the + // types is currently hidden. + for (int i = internalTypes.size() - 1; i >= 0; i--) { + InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i)); + if (consumer == null) { + continue; + } + if (!consumer.isRequestedVisible()) { + return LAYOUT_INSETS_DURING_ANIMATION_SHOWN; + } + } + return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; + } + private void cancelExistingControllers(@InsetsType int types) { for (int i = mAnimationControls.size() - 1; i >= 0; i--) { InsetsAnimationControlImpl control = mAnimationControls.get(i); @@ -595,7 +656,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // and hidden state insets are correct. controlAnimationUnchecked( types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(), - true /* fade */); + true /* fade */, show + ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN + : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN); } private void hideDirectly(@InsetsType int types) { @@ -627,18 +690,40 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @Override - public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) { - mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds); + public void startAnimation(InsetsAnimationControlImpl controller, + WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation, + AnimationBounds bounds, int layoutDuringAnimation) { + if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) { + showDirectly(types); + } else { + hideDirectly(types); + } + mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation); + mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this); + mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds); + listener.onReady(controller, types); + return true; + } + }); + mViewRoot.mView.invalidate(); } @VisibleForTesting public void dispatchAnimationFinished(InsetsAnimation animation) { - mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation); + mViewRoot.mView.dispatchWindowInsetsAnimationFinish(animation); } @VisibleForTesting @Override public void scheduleApplyChangeInsets() { + if (mStartingAnimation) { + mAnimCallback.run(); + mAnimCallbackScheduled = false; + return; + } if (!mAnimCallbackScheduled) { mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, mAnimCallback, null /* token*/); diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index c6d9898a425c..b2a5d915c2a6 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -53,7 +53,7 @@ public class InsetsSourceConsumer { } protected final InsetsController mController; - protected boolean mVisible; + protected boolean mRequestedVisible; private final Supplier<Transaction> mTransactionSupplier; private final @InternalInsetsType int mType; private final InsetsState mState; @@ -66,7 +66,7 @@ public class InsetsSourceConsumer { mState = state; mTransactionSupplier = transactionSupplier; mController = controller; - mVisible = InsetsState.getDefaultVisibility(type); + mRequestedVisible = InsetsState.getDefaultVisibility(type); } public void setControl(@Nullable InsetsSourceControl control) { @@ -94,12 +94,12 @@ public class InsetsSourceConsumer { @VisibleForTesting public void show() { - setVisible(true); + setRequestedVisible(true); } @VisibleForTesting public void hide() { - setVisible(false); + setRequestedVisible(false); } /** @@ -126,16 +126,16 @@ public class InsetsSourceConsumer { if (mSourceControl == null) { return false; } - if (mState.getSource(mType).isVisible() == mVisible) { + if (mState.getSource(mType).isVisible() == mRequestedVisible) { return false; } - mState.getSource(mType).setVisible(mVisible); + mState.getSource(mType).setVisible(mRequestedVisible); return true; } @VisibleForTesting - public boolean isVisible() { - return mVisible; + public boolean isRequestedVisible() { + return mRequestedVisible; } /** @@ -157,11 +157,15 @@ public class InsetsSourceConsumer { // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. } - private void setVisible(boolean visible) { - if (mVisible == visible) { + /** + * Sets requested visibility from the client, regardless of whether we are able to control it at + * the moment. + */ + private void setRequestedVisible(boolean requestedVisible) { + if (mRequestedVisible == requestedVisible) { return; } - mVisible = visible; + mRequestedVisible = requestedVisible; applyLocalVisibilityOverride(); mController.notifyVisibilityChanged(); } @@ -173,7 +177,7 @@ public class InsetsSourceConsumer { } final Transaction t = mTransactionSupplier.get(); - if (mVisible) { + if (mRequestedVisible) { t.show(mSourceControl.getLeash()); } else { t.hide(mSourceControl.getLeash()); diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index e3fed3a5dce3..ae1e579da8f6 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -19,10 +19,15 @@ package android.view; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME; import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; +import static android.view.WindowInsets.Type.IME; import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; +import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; +import static android.view.WindowInsets.Type.systemBars; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import android.annotation.IntDef; import android.annotation.Nullable; @@ -156,11 +161,10 @@ public class InsetsState implements Parcelable { && source.getType() != ITYPE_IME; boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR); - boolean skipIme = source.getType() == ITYPE_IME - && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0; boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE - && (toPublicType(type) & Type.compatSystemInsets()) != 0; - if (skipSystemBars || skipIme || skipLegacyTypes || skipNonImeInImeMode) { + && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR + || type == ITYPE_IME); + if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) { typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible(); continue; } @@ -175,8 +179,11 @@ public class InsetsState implements Parcelable { typeMaxInsetsMap, null /* typeSideMap */, null /* typeVisibilityMap */); } } + final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST; return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, - alwaysConsumeSystemBars, cutout); + alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE + ? systemBars() | ime() + : systemBars()); } private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index f6d6522f80d6..ff8455ab0915 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -677,6 +677,22 @@ public final class SurfaceControl implements Parcelable { } /** + * Set the initial visibility for the SurfaceControl. + * + * @param hidden Whether the Surface is initially HIDDEN. + * @hide + */ + @NonNull + public Builder setHidden(boolean hidden) { + if (hidden) { + mFlags |= HIDDEN; + } else { + mFlags &= ~HIDDEN; + } + return this; + } + + /** * Set a parent surface for our new SurfaceControl. * * Child surfaces are constrained to the onscreen region of their parent. @@ -789,8 +805,7 @@ public final class SurfaceControl implements Parcelable { * @param name The surface name, must not be null. * @param w The surface initial width. * @param h The surface initial height. - * @param flags The surface creation flags. Should always include {@link #HIDDEN} - * in the creation flags. + * @param flags The surface creation flags. * @param metadata Initial metadata. * @throws throws OutOfResourcesException If the SurfaceControl cannot be created. */ @@ -801,15 +816,6 @@ public final class SurfaceControl implements Parcelable { throw new IllegalArgumentException("name must not be null"); } - if ((flags & SurfaceControl.HIDDEN) == 0) { - Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set " - + "to ensure that they are not made visible prematurely before " - + "all of the surface's properties have been configured. " - + "Set the other properties and make the surface visible within " - + "a transaction. New surface name: " + name, - new Throwable()); - } - mName = name; mWidth = w; mHeight = h; diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 52ea2b2142ad..17825444a524 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -379,7 +379,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * This gets called on a RenderThread worker thread, so members accessed here must * be protected by a lock. */ - final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE; + final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER; viewRoot.registerRtFrameCallback(frame -> { try { final SurfaceControl.Transaction t = useBLAST ? @@ -1107,7 +1107,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t, Rect position, long frameNumber) { - if (frameNumber > 0 && ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) { + if (frameNumber > 0 && !WindowManagerGlobal.USE_BLAST_ADAPTER) { final ViewRootImpl viewRoot = getViewRootImpl(); t.deferTransactionUntilSurface(surface, viewRoot.mSurface, @@ -1125,7 +1125,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private void setParentSpaceRectangle(Rect position, long frameNumber) { - final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE; + final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER; final ViewRootImpl viewRoot = getViewRootImpl(); final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() : mRtTransaction; @@ -1186,7 +1186,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void positionLost(long frameNumber) { - boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE; + boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER; if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", System.identityHashCode(this), frameNumber)); @@ -1524,7 +1524,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void invalidate(boolean invalidateCache) { super.invalidate(invalidateCache); - if (ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) { + if (!WindowManagerGlobal.USE_BLAST_ADAPTER) { return; } final ViewRootImpl viewRoot = getViewRootImpl(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0db80e2749c3..13d609b16541 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11117,7 +11117,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Dispatches {@link WindowInsetsAnimationCallback#onStarted(InsetsAnimation, AnimationBounds)} + * Dispatches {@link WindowInsetsAnimationCallback#onPrepare(InsetsAnimation)} + * when Window Insets animation is being prepared. + * @param animation current animation + * + * @see WindowInsetsAnimationCallback#onPrepare(InsetsAnimation) + */ + public void dispatchWindowInsetsAnimationPrepare( + @NonNull InsetsAnimation animation) { + if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) { + mListenerInfo.mWindowInsetsAnimationCallback.onPrepare(animation); + } + } + + /** + * Dispatches {@link WindowInsetsAnimationCallback#onStart(InsetsAnimation, AnimationBounds)} * when Window Insets animation is started. * @param animation current animation * @param bounds the upper and lower {@link AnimationBounds} that provides range of @@ -11125,10 +11139,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return the upper and lower {@link AnimationBounds}. */ @NonNull - public AnimationBounds dispatchWindowInsetsAnimationStarted( + public AnimationBounds dispatchWindowInsetsAnimationStart( @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) { if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) { - return mListenerInfo.mWindowInsetsAnimationCallback.onStarted(animation, bounds); + return mListenerInfo.mWindowInsetsAnimationCallback.onStart(animation, bounds); } return bounds; } @@ -11149,13 +11163,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Dispatches {@link WindowInsetsAnimationCallback#onFinished(InsetsAnimation)} + * Dispatches {@link WindowInsetsAnimationCallback#onFinish(InsetsAnimation)} * when Window Insets animation finishes. * @param animation The current ongoing {@link InsetsAnimation}. */ - public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) { + public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) { if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) { - mListenerInfo.mWindowInsetsAnimationCallback.onFinished(animation); + mListenerInfo.mWindowInsetsAnimationCallback.onFinish(animation); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 5fb71773db8f..047d7da7536f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -7199,13 +7199,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override + public void dispatchWindowInsetsAnimationPrepare( + @NonNull InsetsAnimation animation) { + super.dispatchWindowInsetsAnimationPrepare(animation); + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + getChildAt(i).dispatchWindowInsetsAnimationPrepare(animation); + } + } + + @Override @NonNull - public AnimationBounds dispatchWindowInsetsAnimationStarted( + public AnimationBounds dispatchWindowInsetsAnimationStart( @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) { - super.dispatchWindowInsetsAnimationStarted(animation, bounds); + super.dispatchWindowInsetsAnimationStart(animation, bounds); final int count = getChildCount(); for (int i = 0; i < count; i++) { - getChildAt(i).dispatchWindowInsetsAnimationStarted(animation, bounds); + getChildAt(i).dispatchWindowInsetsAnimationStart(animation, bounds); } return bounds; } @@ -7222,11 +7232,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) { - super.dispatchWindowInsetsAnimationFinished(animation); + public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) { + super.dispatchWindowInsetsAnimationFinish(animation); final int count = getChildCount(); for (int i = 0; i < count; i++) { - getChildAt(i).dispatchWindowInsetsAnimationFinished(animation); + getChildAt(i).dispatchWindowInsetsAnimationFinish(animation); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 522ff9a3bcb5..ab89ef46e09e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -192,11 +192,6 @@ public final class ViewRootImpl implements ViewParent, private static final boolean MT_RENDERER_AVAILABLE = true; /** - * @hide - */ - public static final boolean USE_BLAST_BUFFERQUEUE = false; - - /** * If set to 2, the view system will switch from using rectangles retrieved from window to * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets * directly from the full configuration, enabling richer information about the insets state, as @@ -1312,7 +1307,7 @@ public final class ViewRootImpl implements ViewParent, } mWindowAttributes.privateFlags |= compatibleWindowFlag; - if (USE_BLAST_BUFFERQUEUE) { + if (WindowManagerGlobal.USE_BLAST_ADAPTER) { mWindowAttributes.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; } @@ -1469,6 +1464,7 @@ public final class ViewRootImpl implements ViewParent, return; } mApplyInsetsRequested = true; + requestLayout(); // If this changes during traversal, no need to schedule another one as it will dispatch it // during the current traversal. @@ -7273,7 +7269,7 @@ public final class ViewRootImpl implements ViewParent, mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets); if (mSurfaceControl.isValid()) { - if (USE_BLAST_BUFFERQUEUE == false) { + if (!WindowManagerGlobal.USE_BLAST_ADAPTER) { mSurface.copyFrom(mSurfaceControl); } else { mSurface.transferFrom(getOrCreateBLASTSurface( diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index a9cc50f9e65e..9df131de6754 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -27,8 +27,8 @@ import static android.view.WindowInsets.Type.STATUS_BARS; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; import static android.view.WindowInsets.Type.TAPPABLE_ELEMENT; import static android.view.WindowInsets.Type.all; -import static android.view.WindowInsets.Type.compatSystemInsets; import static android.view.WindowInsets.Type.indexOf; +import static android.view.WindowInsets.Type.systemBars; import android.annotation.IntDef; import android.annotation.IntRange; @@ -87,6 +87,8 @@ public final class WindowInsets { private final boolean mStableInsetsConsumed; private final boolean mDisplayCutoutConsumed; + private final int mCompatInsetTypes; + /** * Since new insets may be added in the future that existing apps couldn't * know about, this fully empty constant shouldn't be made available to apps @@ -112,7 +114,7 @@ public final class WindowInsets { boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) { this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect), createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)), - isRound, alwaysConsumeSystemBars, displayCutout); + isRound, alwaysConsumeSystemBars, displayCutout, systemBars()); } /** @@ -131,7 +133,7 @@ public final class WindowInsets { @Nullable Insets[] typeMaxInsetsMap, boolean[] typeVisibilityMap, boolean isRound, - boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) { + boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, int compatInsetTypes) { mSystemWindowInsetsConsumed = typeInsetsMap == null; mTypeInsetsMap = mSystemWindowInsetsConsumed ? new Insets[SIZE] @@ -145,6 +147,7 @@ public final class WindowInsets { mTypeVisibilityMap = typeVisibilityMap; mIsRound = isRound; mAlwaysConsumeSystemBars = alwaysConsumeSystemBars; + mCompatInsetTypes = compatInsetTypes; mDisplayCutoutConsumed = displayCutout == null; mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty()) @@ -160,7 +163,8 @@ public final class WindowInsets { this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap, src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound, - src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src)); + src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src), + src.mCompatInsetTypes); } private static DisplayCutout displayCutoutCopyConstructorArgument(WindowInsets w) { @@ -211,7 +215,8 @@ public final class WindowInsets { /** @hide */ @UnsupportedAppUsage public WindowInsets(Rect systemWindowInsets) { - this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null); + this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null, + systemBars()); } /** @@ -280,7 +285,7 @@ public final class WindowInsets { */ @NonNull public Insets getSystemWindowInsets() { - return getInsets(mTypeInsetsMap, compatSystemInsets()); + return getInsets(mTypeInsetsMap, mCompatInsetTypes); } /** @@ -439,7 +444,8 @@ public final class WindowInsets { mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, - null /* displayCutout */); + null /* displayCutout */, + mCompatInsetTypes); } @@ -485,7 +491,8 @@ public final class WindowInsets { return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, - displayCutoutCopyConstructorArgument(this)); + displayCutoutCopyConstructorArgument(this), + mCompatInsetTypes); } // TODO(b/119190588): replace @code with @link below @@ -555,7 +562,7 @@ public final class WindowInsets { */ @NonNull public Insets getStableInsets() { - return getInsets(mTypeMaxInsetsMap, compatSystemInsets()); + return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes); } /** @@ -733,7 +740,8 @@ public final class WindowInsets { public WindowInsets consumeStableInsets() { return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null, mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, - displayCutoutCopyConstructorArgument(this)); + displayCutoutCopyConstructorArgument(this), + mCompatInsetTypes); } /** @@ -817,7 +825,8 @@ public final class WindowInsets { ? null : mDisplayCutout == null ? DisplayCutout.NO_CUTOUT - : mDisplayCutout.inset(left, top, right, bottom)); + : mDisplayCutout.inset(left, top, right, bottom), + mCompatInsetTypes); } @Override @@ -1134,7 +1143,8 @@ public final class WindowInsets { public WindowInsets build() { return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, - mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout); + mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout, + systemBars()); } } @@ -1271,15 +1281,6 @@ public final class WindowInsets { } /** - * @return Inset types representing the list of bars that traditionally were denoted as - * system insets. - * @hide - */ - static @InsetsType int compatSystemInsets() { - return STATUS_BARS | NAVIGATION_BARS | IME; - } - - /** * @return All inset types combined. * * TODO: Figure out if this makes sense at all, mixing e.g {@link #systemGestures()} and diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java index 5e71f271f1d4..e84c3e33c000 100644 --- a/core/java/android/view/WindowInsetsAnimationCallback.java +++ b/core/java/android/view/WindowInsetsAnimationCallback.java @@ -20,6 +20,7 @@ import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; +import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.animation.Interpolator; @@ -30,7 +31,47 @@ import android.view.animation.Interpolator; public interface WindowInsetsAnimationCallback { /** - * Called when an inset animation gets started. + * Called when an insets animation is about to start and before the views have been laid out in + * the end state of the animation. The ordering of events during an insets animation is the + * following: + * <p> + * <ul> + * <li>Application calls {@link WindowInsetsController#hideInputMethod()}, + * {@link WindowInsetsController#showInputMethod()}, + * {@link WindowInsetsController#controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)}</li> + * <li>onPrepare is called on the view hierarchy listeners</li> + * <li>{@link View#onApplyWindowInsets} will be called with the end state of the + * animation</li> + * <li>View hierarchy gets laid out according to the changes the application has requested + * due to the new insets being dispatched</li> + * <li>{@link #onStart} is called <em>before</em> the view + * hierarchy gets drawn in the new laid out state</li> + * <li>{@link #onProgress} is called immediately after with the animation start state</li> + * <li>The frame gets drawn.</li> + * </ul> + * <p> + * This ordering allows the application to inspect the end state after the animation has + * finished, and then revert to the starting state of the animation in the first + * {@link #onProgress} callback by using post-layout view properties like {@link View#setX} and + * related methods. + * <p> + * Note: If the animation is application controlled by using + * {@link WindowInsetsController#controlInputMethodAnimation}, the end state of the animation + * is undefined as the application may decide on the end state only by passing in the + * {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In this + * situation, the system will dispatch the insets in the opposite visibility state before the + * animation starts. Example: When controlling the input method with + * {@link WindowInsetsController#controlInputMethodAnimation} and the input method is currently + * showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets} instance for + * which {@link WindowInsets#isVisible} will return {@code false} for {@link Type#ime}. + * + * @param animation The animation that is about to start. + */ + default void onPrepare(@NonNull InsetsAnimation animation) { + } + + /** + * Called when an insets animation gets started. * <p> * Note that, like {@link #onProgress}, dispatch of the animation start event is hierarchical: * It will starts at the root of the view hierarchy and then traverse it and invoke the callback @@ -45,7 +86,7 @@ public interface WindowInsetsAnimationCallback { * subtree of the hierarchy. */ @NonNull - default AnimationBounds onStarted( + default AnimationBounds onStart( @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) { return bounds; } @@ -72,12 +113,12 @@ public interface WindowInsetsAnimationCallback { WindowInsets onProgress(@NonNull WindowInsets insets); /** - * Called when an inset animation has finished. + * Called when an insets animation has finished. * * @param animation The animation that has finished running. This will be the same instance as - * passed into {@link #onStarted} + * passed into {@link #onStart} */ - default void onFinished(@NonNull InsetsAnimation animation) { + default void onFinish(@NonNull InsetsAnimation animation) { } /** @@ -253,14 +294,14 @@ public interface WindowInsetsAnimationCallback { /** * Insets both the lower and upper bound by the specified insets. This is to be used in - * {@link WindowInsetsAnimationCallback#onStarted} to indicate that a part of the insets has + * {@link WindowInsetsAnimationCallback#onStart} to indicate that a part of the insets has * been used to offset or clip its children, and the children shouldn't worry about that * part anymore. * * @param insets The amount to inset. * @return A copy of this instance inset in the given directions. * @see WindowInsets#inset - * @see WindowInsetsAnimationCallback#onStarted + * @see WindowInsetsAnimationCallback#onStart */ @NonNull public AnimationBounds inset(@NonNull Insets insets) { diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java index 8a226c1bbe23..f91254de33ff 100644 --- a/core/java/android/view/WindowInsetsAnimationControlListener.java +++ b/core/java/android/view/WindowInsetsAnimationControlListener.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.Hide; import android.annotation.NonNull; import android.view.WindowInsets.Type.InsetsType; import android.view.inputmethod.EditorInfo; @@ -26,6 +27,12 @@ import android.view.inputmethod.EditorInfo; public interface WindowInsetsAnimationControlListener { /** + * @hide + */ + default void onPrepare(int types) { + } + + /** * Called when the animation is ready to be controlled. This may be delayed when the IME needs * to redraw because of an {@link EditorInfo} change, or when the window is starting up. * diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index 6de56be2f3c5..9d7f292dbdf5 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -149,7 +149,8 @@ public interface WindowInsetsController { * * @param types The {@link InsetsType}s the application has requested to control. * @param durationMillis duration of animation in - * {@link java.util.concurrent.TimeUnit#MILLISECONDS} + * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the + * animation doesn't have a predetermined duration. * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the * windows are ready to be controlled, among other callbacks. * @hide @@ -162,7 +163,8 @@ public interface WindowInsetsController { * modifying the position of the IME when it's causing insets. * * @param durationMillis duration of the animation in - * {@link java.util.concurrent.TimeUnit#MILLISECONDS} + * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the + * animation doesn't have a predetermined duration. * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the * IME are ready to be controlled, among other callbacks. */ diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 95780023563d..7d5564e1c8be 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -57,6 +57,12 @@ public final class WindowManagerGlobal { private static final String TAG = "WindowManager"; /** + * This flag controls whether ViewRootImpl will utilize the Blast Adapter + * to send buffer updates to SurfaceFlinger + */ + public static final boolean USE_BLAST_ADAPTER = false; + + /** * The user is navigating with keys (not the touch screen), so * navigational focus should be shown. */ diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index ff31bccfa648..2e5a4b57da18 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -31,6 +31,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.app.RemoteAction; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -1209,6 +1210,61 @@ public final class AccessibilityManager { } /** + * Register the provided {@link RemoteAction} with the given actionId + * + * @param action The remote action to be registered with the given actionId as system action. + * @param actionId The id uniquely identify the system action. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + public void registerSystemAction(@NonNull RemoteAction action, int actionId) { + final IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.registerSystemAction(action, actionId); + + if (DEBUG) { + Log.i(LOG_TAG, "System action " + action.getTitle() + " is registered."); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error registering system action " + action.getTitle() + " ", re); + } + } + + /** + * Unregister a system action with the given actionId + * + * @param actionId The id uniquely identify the system action to be unregistered. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + public void unregisterSystemAction(int actionId) { + final IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.unregisterSystemAction(actionId); + + if (DEBUG) { + Log.i(LOG_TAG, "System action with actionId " + actionId + " is unregistered."); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error unregistering system action with actionId " + actionId + " ", re); + } + } + + /** * Notifies that the accessibility button in the system's navigation area has been clicked * * @param displayId The logical display id. diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 36515b3ba094..392db574d988 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -16,6 +16,7 @@ package android.view.accessibility; +import android.app.RemoteAction; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceConnection; import android.accessibilityservice.IAccessibilityServiceClient; @@ -82,4 +83,7 @@ interface IAccessibilityManager { int getAccessibilityWindowId(IBinder windowToken); long getRecommendedTimeoutMillis(); + + oneway void registerSystemAction(in RemoteAction action, int actionId); + oneway void unregisterSystemAction(int actionId); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 67ce8d2e49b5..f3007a794344 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -93,9 +93,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** @@ -429,7 +432,10 @@ public final class InputMethodManager { * in a background thread. Later, if there is an actual startInput it will wait on * main thread till the background thread completes. */ - private CompletableFuture<Void> mWindowFocusGainFuture; + private Future<?> mWindowFocusGainFuture; + + private ExecutorService mStartInputWorker = Executors.newSingleThreadExecutor( + new ImeThreadFactory("StartInputWorker")); /** * The instance that has previously been sent to the input method. @@ -790,6 +796,19 @@ public final class InputMethodManager { } } + private static class ImeThreadFactory implements ThreadFactory { + private final String mThreadName; + + ImeThreadFactory(String name) { + mThreadName = name; + } + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, mThreadName); + } + } + final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { @@ -1978,7 +1997,7 @@ public final class InputMethodManager { if (mWindowFocusGainFuture != null) { mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */); } - mWindowFocusGainFuture = CompletableFuture.runAsync(() -> { + mWindowFocusGainFuture = mStartInputWorker.submit(() -> { if (checkFocusNoStartInput(forceNewFocus1)) { // We need to restart input on the current focus view. This // should be done in conjunction with telling the system service diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index a0cf53437a50..20af76b0d5ca 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -5921,8 +5921,6 @@ public class Editor { // The offsets of that last touch down event. Remembered to start selection there. private int mMinTouchOffset, mMaxTouchOffset; - private boolean mGestureStayedInTapRegion; - // Where the user first starts the drag motion. private int mStartOffset = -1; @@ -6029,8 +6027,7 @@ public class Editor { eventX, eventY); // Double tap detection - if (mGestureStayedInTapRegion - && mTouchState.isMultiTapInSameArea() + if (mTouchState.isMultiTapInSameArea() && (isMouse || isPositionOnText(eventX, eventY))) { if (TextView.DEBUG_CURSOR) { logCursor("SelectionModifierCursorController: onTouchEvent", @@ -6043,7 +6040,6 @@ public class Editor { } mDiscardNextActionUp = true; } - mGestureStayedInTapRegion = true; mHaventMovedEnoughToStartDrag = true; } break; @@ -6059,25 +6055,8 @@ public class Editor { break; case MotionEvent.ACTION_MOVE: - final ViewConfiguration viewConfig = ViewConfiguration.get( - mTextView.getContext()); - - if (mGestureStayedInTapRegion || mHaventMovedEnoughToStartDrag) { - final float deltaX = eventX - mTouchState.getLastDownX(); - final float deltaY = eventY - mTouchState.getLastDownY(); - final float distanceSquared = deltaX * deltaX + deltaY * deltaY; - - if (mGestureStayedInTapRegion) { - int doubleTapTouchSlop = viewConfig.getScaledDoubleTapTouchSlop(); - mGestureStayedInTapRegion = - distanceSquared <= doubleTapTouchSlop * doubleTapTouchSlop; - } - if (mHaventMovedEnoughToStartDrag) { - // We don't start dragging until the user has moved enough. - int touchSlop = viewConfig.getScaledTouchSlop(); - mHaventMovedEnoughToStartDrag = - distanceSquared <= touchSlop * touchSlop; - } + if (mHaventMovedEnoughToStartDrag) { + mHaventMovedEnoughToStartDrag = !mTouchState.isMovedEnoughForDrag(); } if (isMouse && !isDragAcceleratorActive()) { diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index d53099d44f6f..6277afe2f613 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -31,13 +31,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Helper class used by {@link Editor} to track state for touch events. + * Helper class used by {@link Editor} to track state for touch events. Ideally the logic here + * should be replaced with {@link android.view.GestureDetector}. * * @hide */ @VisibleForTesting(visibility = PACKAGE) public class EditorTouchState { private float mLastDownX, mLastDownY; + private long mLastDownMillis; private float mLastUpX, mLastUpY; private long mLastUpMillis; @@ -106,9 +108,18 @@ public class EditorTouchState { final int action = event.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE); + + // We check both the time between the last up and current down event, as well as the + // time between the first down and up events. The latter check is necessary to handle + // the case when the user taps, drags/holds for some time, and then lifts up and + // quickly taps in the same area. This scenario should not be treated as a double-tap. + // This follows the behavior in GestureDetector. final long millisSinceLastUp = event.getEventTime() - mLastUpMillis; + final long millisBetweenLastDownAndLastUp = mLastUpMillis - mLastDownMillis; + // Detect double tap and triple click. if (millisSinceLastUp <= ViewConfiguration.getDoubleTapTimeout() + && millisBetweenLastDownAndLastUp <= ViewConfiguration.getDoubleTapTimeout() && (mMultiTapStatus == MultiTapStatus.FIRST_TAP || (mMultiTapStatus == MultiTapStatus.DOUBLE_TAP && isMouse))) { if (mMultiTapStatus == MultiTapStatus.FIRST_TAP) { @@ -133,6 +144,7 @@ public class EditorTouchState { } mLastDownX = event.getX(); mLastDownY = event.getY(); + mLastDownMillis = event.getEventTime(); mMovedEnoughForDrag = false; mIsDragCloseToVertical = false; } else if (action == MotionEvent.ACTION_UP) { diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index f9ba34ed1c3a..de204badfd0d 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -16,6 +16,7 @@ package com.android.internal.app; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; +import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static android.view.accessibility.AccessibilityManager.ShortcutType; import android.accessibilityservice.AccessibilityServiceInfo; @@ -24,13 +25,17 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.AlertDialog; +import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArraySet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -49,7 +54,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.StringJoiner; /** * Activity used to display and persist a service or feature target for the Accessibility button. @@ -59,13 +67,46 @@ public class AccessibilityButtonChooserActivity extends Activity { private static final String MAGNIFICATION_COMPONENT_ID = "com.android.server.accessibility.MagnificationController"; + private static final char SERVICES_SEPARATOR = ':'; + private static final TextUtils.SimpleStringSplitter sStringColonSplitter = + new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); + private static final int ACCESSIBILITY_BUTTON_USER_TYPE = convertToUserType( + ACCESSIBILITY_BUTTON); + private static final int ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE = convertToUserType( + ACCESSIBILITY_SHORTCUT_KEY); + private int mShortcutType; private List<AccessibilityButtonTarget> mTargets = new ArrayList<>(); - private List<AccessibilityButtonTarget> mReadyToBeDisabledTargets = new ArrayList<>(); private AlertDialog mAlertDialog; private TargetAdapter mTargetAdapter; /** + * Annotation for different user shortcut type UI type. + * + * {@code DEFAULT} for displaying default value. + * {@code SOFTWARE} for displaying specifying the accessibility services or features which + * choose accessibility button in the navigation bar as preferred shortcut. + * {@code HARDWARE} for displaying specifying the accessibility services or features which + * choose accessibility shortcut as preferred shortcut. + * {@code TRIPLETAP} for displaying specifying magnification to be toggled via quickly + * tapping screen 3 times as preferred shortcut. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + UserShortcutType.DEFAULT, + UserShortcutType.SOFTWARE, + UserShortcutType.HARDWARE, + UserShortcutType.TRIPLETAP, + }) + /** Denotes the user shortcut type. */ + public @interface UserShortcutType { + int DEFAULT = 0; + int SOFTWARE = 1; // 1 << 0 + int HARDWARE = 2; // 1 << 1 + int TRIPLETAP = 4; // 1 << 2 + } + + /** * Annotation for different accessibilityService fragment UI type. * * {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service @@ -117,7 +158,6 @@ public class AccessibilityButtonChooserActivity extends Activity { ACCESSIBILITY_BUTTON); mTargets.addAll(getServiceTargets(this, mShortcutType)); - // TODO(b/146815548): Will add title to separate which one type mTargetAdapter = new TargetAdapter(mTargets); mAlertDialog = new AlertDialog.Builder(this) .setAdapter(mTargetAdapter, /* listener= */ null) @@ -269,8 +309,10 @@ public class AccessibilityButtonChooserActivity extends Activity { switch (target.getFragmentType()) { case AccessibilityServiceFragmentType.LEGACY: + updateLegacyActionItemVisibility(context, holder); + break; case AccessibilityServiceFragmentType.INVISIBLE: - updateLegacyOrInvisibleActionItemVisibility(context, holder); + updateInvisibleActionItemVisibility(context, holder); break; case AccessibilityServiceFragmentType.INTUITIVE: updateIntuitiveActionItemVisibility(context, holder, target); @@ -283,9 +325,21 @@ public class AccessibilityButtonChooserActivity extends Activity { } } - private void updateLegacyOrInvisibleActionItemVisibility(@NonNull Context context, + private void updateLegacyActionItemVisibility(@NonNull Context context, @NonNull ViewHolder holder) { - final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT; + final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); + + holder.mLabelView.setEnabled(!isEditMenuMode); + holder.mViewItem.setEnabled(!isEditMenuMode); + holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); + holder.mViewItem.setVisibility(View.VISIBLE); + holder.mSwitchItem.setVisibility(View.GONE); + holder.mItemContainer.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); + } + + private void updateInvisibleActionItemVisibility(@NonNull Context context, + @NonNull ViewHolder holder) { + final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); holder.mViewItem.setVisibility(View.VISIBLE); @@ -295,7 +349,7 @@ public class AccessibilityButtonChooserActivity extends Activity { private void updateIntuitiveActionItemVisibility(@NonNull Context context, @NonNull ViewHolder holder, AccessibilityButtonTarget target) { - final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT; + final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); @@ -306,7 +360,7 @@ public class AccessibilityButtonChooserActivity extends Activity { private void updateBounceActionItemVisibility(@NonNull Context context, @NonNull ViewHolder holder) { - final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT; + final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); holder.mViewItem.setImageDrawable( isEditMenuMode ? context.getDrawable(R.drawable.ic_delete_item) @@ -383,73 +437,252 @@ public class AccessibilityButtonChooserActivity extends Activity { } private void onTargetDeleted(AdapterView<?> parent, View view, int position, long id) { - // TODO(b/147027236): Will discuss with UX designer what UX behavior about deleting item - // is good for user. - mReadyToBeDisabledTargets.add(mTargets.get(position)); + final AccessibilityButtonTarget target = mTargets.get(position); + final ComponentName targetComponentName = + ComponentName.unflattenFromString(target.getId()); + + switch (target.getFragmentType()) { + case AccessibilityServiceFragmentType.INVISIBLE: + onInvisibleTargetDeleted(targetComponentName); + break; + case AccessibilityServiceFragmentType.INTUITIVE: + onIntuitiveTargetDeleted(targetComponentName); + break; + case AccessibilityServiceFragmentType.LEGACY: + case AccessibilityServiceFragmentType.BOUNCE: + // Do nothing + break; + default: + throw new IllegalStateException("Unexpected fragment type"); + } + mTargets.remove(position); mTargetAdapter.notifyDataSetChanged(); + + if (mTargets.isEmpty()) { + mAlertDialog.dismiss(); + } } - private void onCancelButtonClicked() { - resetAndUpdateTargets(); + private void onInvisibleTargetDeleted(ComponentName componentName) { + if (mShortcutType == ACCESSIBILITY_BUTTON) { + optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName); - mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH); - mTargetAdapter.notifyDataSetChanged(); + if (!hasValueInSettings(this, + ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName)) { + setAccessibilityServiceState(this, componentName, /* enabled= */ false); + } + } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName); - mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); - mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( - getString(R.string.edit_accessibility_shortcut_menu_button)); + if (!hasValueInSettings(this, + ACCESSIBILITY_BUTTON_USER_TYPE, componentName)) { + setAccessibilityServiceState(this, componentName, /* enabled= */ false); + } + } else { + throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType); + } + } - updateDialogListeners(); + private void onIntuitiveTargetDeleted(ComponentName componentName) { + if (mShortcutType == ACCESSIBILITY_BUTTON) { + optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName); + } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName); + } else { + throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType); + } } - private void onEditButtonClicked() { - mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT); + private void onCancelButtonClicked() { + mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH); mTargetAdapter.notifyDataSetChanged(); - mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setText( - getString(R.string.cancel_accessibility_shortcut_menu_button)); - mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.VISIBLE); mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( - getString(R.string.save_accessibility_shortcut_menu_button)); + getString(R.string.edit_accessibility_shortcut_menu_button)); updateDialogListeners(); } - private void onSaveButtonClicked() { - disableTargets(); - resetAndUpdateTargets(); - - mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH); + private void onEditButtonClicked() { + mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT); mTargetAdapter.notifyDataSetChanged(); - mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( - getString(R.string.edit_accessibility_shortcut_menu_button)); + getString(R.string.cancel_accessibility_shortcut_menu_button)); updateDialogListeners(); } private void updateDialogListeners() { final boolean isEditMenuMode = - mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT; + (mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT); - mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener( - view -> onCancelButtonClicked()); mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener( - isEditMenuMode ? view -> onSaveButtonClicked() : view -> onEditButtonClicked()); + isEditMenuMode ? view -> onCancelButtonClicked() : view -> onEditButtonClicked()); mAlertDialog.getListView().setOnItemClickListener( isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected); } - private void disableTargets() { - for (AccessibilityButtonTarget service : mReadyToBeDisabledTargets) { - // TODO(b/146967898): disable services. + /** + * @return the set of enabled accessibility services for {@param userId}. If there are no + * services, it returns the unmodifiable {@link Collections#emptySet()}. + */ + private Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) { + final String enabledServicesSetting = Settings.Secure.getStringForUser( + context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userId); + if (TextUtils.isEmpty(enabledServicesSetting)) { + return Collections.emptySet(); } + + final Set<ComponentName> enabledServices = new HashSet<>(); + final TextUtils.StringSplitter colonSplitter = + new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); + colonSplitter.setString(enabledServicesSetting); + + for (String componentNameString : colonSplitter) { + final ComponentName enabledService = ComponentName.unflattenFromString( + componentNameString); + if (enabledService != null) { + enabledServices.add(enabledService); + } + } + + return enabledServices; } - private void resetAndUpdateTargets() { - mTargets.clear(); - mTargets.addAll(getServiceTargets(this, mShortcutType)); + /** + * Changes an accessibility component's state. + */ + private void setAccessibilityServiceState(Context context, ComponentName componentName, + boolean enabled) { + setAccessibilityServiceState(context, componentName, enabled, UserHandle.myUserId()); + } + + /** + * Changes an accessibility component's state for {@param userId}. + */ + private void setAccessibilityServiceState(Context context, ComponentName componentName, + boolean enabled, int userId) { + Set<ComponentName> enabledServices = getEnabledServicesFromSettings( + context, userId); + + if (enabledServices.isEmpty()) { + enabledServices = new ArraySet<>(/* capacity= */ 1); + } + + if (enabled) { + enabledServices.add(componentName); + } else { + enabledServices.remove(componentName); + } + + final StringBuilder enabledServicesBuilder = new StringBuilder(); + for (ComponentName enabledService : enabledServices) { + enabledServicesBuilder.append(enabledService.flattenToString()); + enabledServicesBuilder.append( + SERVICES_SEPARATOR); + } + + final int enabledServicesBuilderLength = enabledServicesBuilder.length(); + if (enabledServicesBuilderLength > 0) { + enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1); + } + + Settings.Secure.putStringForUser(context.getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + enabledServicesBuilder.toString(), userId); + } + + /** + * Opts out component name into colon-separated {@code shortcutType} key's string in Settings. + * + * @param context The current context. + * @param shortcutType The preferred shortcut type user selected. + * @param componentName The component name that need to be opted out from Settings. + */ + private void optOutValueFromSettings( + Context context, int shortcutType, ComponentName componentName) { + final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR)); + final String targetsKey = convertToKey(shortcutType); + final String targetsValue = Settings.Secure.getString(context.getContentResolver(), + targetsKey); + + if (TextUtils.isEmpty(targetsValue)) { + return; + } + + sStringColonSplitter.setString(targetsValue); + while (sStringColonSplitter.hasNext()) { + final String name = sStringColonSplitter.next(); + if (TextUtils.isEmpty(name) || (componentName.flattenToString()).equals(name)) { + continue; + } + joiner.add(name); + } + + Settings.Secure.putString(context.getContentResolver(), targetsKey, joiner.toString()); + } + + /** + * Returns if component name existed in Settings. + * + * @param context The current context. + * @param shortcutType The preferred shortcut type user selected. + * @param componentName The component name that need to be checked existed in Settings. + * @return {@code true} if componentName existed in Settings. + */ + private boolean hasValueInSettings(Context context, @UserShortcutType int shortcutType, + @NonNull ComponentName componentName) { + final String targetKey = convertToKey(shortcutType); + final String targetString = Settings.Secure.getString(context.getContentResolver(), + targetKey); + + if (TextUtils.isEmpty(targetString)) { + return false; + } + + sStringColonSplitter.setString(targetString); + while (sStringColonSplitter.hasNext()) { + final String name = sStringColonSplitter.next(); + if ((componentName.flattenToString()).equals(name)) { + return true; + } + } + + return false; + } + + /** + * Converts {@link UserShortcutType} to key in Settings. + * + * @param type The shortcut type. + * @return Mapping key in Settings. + */ + private String convertToKey(@UserShortcutType int type) { + switch (type) { + case UserShortcutType.SOFTWARE: + return Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT; + case UserShortcutType.HARDWARE: + return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; + case UserShortcutType.TRIPLETAP: + return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED; + default: + throw new IllegalArgumentException( + "Unsupported user shortcut type: " + type); + } + } + + private static @UserShortcutType int convertToUserType(@ShortcutType int type) { + switch (type) { + case ACCESSIBILITY_BUTTON: + return UserShortcutType.SOFTWARE; + case ACCESSIBILITY_SHORTCUT_KEY: + return UserShortcutType.HARDWARE; + default: + throw new IllegalArgumentException( + "Unsupported shortcut type:" + type); + } } } diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java new file mode 100644 index 000000000000..0b937fad7df1 --- /dev/null +++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.compat; + +import android.os.Build; + +/** + * Platform private class for determining the type of Android build installed. + * + */ +public class AndroidBuildClassifier { + + public boolean isDebuggableBuild() { + return Build.IS_DEBUGGABLE; + } + + public boolean isFinalBuild() { + return "REL".equals(Build.VERSION.CODENAME); + } +} diff --git a/core/java/com/android/internal/compat/IOverrideValidator.aidl b/core/java/com/android/internal/compat/IOverrideValidator.aidl new file mode 100644 index 000000000000..add4708863aa --- /dev/null +++ b/core/java/com/android/internal/compat/IOverrideValidator.aidl @@ -0,0 +1,38 @@ +/* + * 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.compat; + +import android.content.pm.ApplicationInfo; + +import com.android.internal.compat.OverrideAllowedState; + +/** + * Platform private API for determining whether a changeId can be overridden. + * + * {@hide} + */ +interface IOverrideValidator +{ + /** + * Validation function. + * @param changeId id of the change to be toggled on or off. + * @param packageName package of the app for which the change should be overridden. + * @return {@link OverrideAllowedState} specifying whether the change can be overridden for + * the given package or a reason why not. + */ + OverrideAllowedState getOverrideAllowedState(long changeId, String packageName); +} diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 7dcb12c9e72b..4c203d394759 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -17,6 +17,7 @@ package com.android.internal.compat; import android.content.pm.ApplicationInfo; +import com.android.internal.compat.IOverrideValidator; import java.util.Map; parcelable CompatibilityChangeConfig; @@ -195,4 +196,9 @@ interface IPlatformCompat * @return An array of {@link CompatChangeInfo} known to the service. */ CompatibilityChangeInfo[] listAllChanges(); + + /** + * Get an instance that can determine whether a changeid can be overridden for a package name. + */ + IOverrideValidator getOverrideValidator(); } diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.aidl b/core/java/com/android/internal/compat/OverrideAllowedState.aidl new file mode 100644 index 000000000000..10ceac786841 --- /dev/null +++ b/core/java/com/android/internal/compat/OverrideAllowedState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.compat; + +parcelable OverrideAllowedState;
\ No newline at end of file diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java new file mode 100644 index 000000000000..56216c251070 --- /dev/null +++ b/core/java/com/android/internal/compat/OverrideAllowedState.java @@ -0,0 +1,153 @@ +/* + * 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.compat; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class contains all the possible override allowed states. + */ +public final class OverrideAllowedState implements Parcelable { + @IntDef({ + ALLOWED, + DISABLED_NOT_DEBUGGABLE, + DISABLED_NON_TARGET_SDK, + DISABLED_TARGET_SDK_TOO_HIGH, + PACKAGE_DOES_NOT_EXIST + }) + @Retention(RetentionPolicy.SOURCE) + public @interface State { + } + + /** + * Change can be overridden. + */ + public static final int ALLOWED = 0; + /** + * Change cannot be overridden, due to the app not being debuggable. + */ + public static final int DISABLED_NOT_DEBUGGABLE = 1; + /** + * Change cannot be overridden, due to the build being non-debuggable and the change being + * non-targetSdk. + */ + public static final int DISABLED_NON_TARGET_SDK = 2; + /** + * Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk. + */ + public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3; + /** + * Package does not exist. + */ + public static final int PACKAGE_DOES_NOT_EXIST = 4; + + @State + public final int state; + public final int appTargetSdk; + public final int changeIdTargetSdk; + + private OverrideAllowedState(Parcel parcel) { + state = parcel.readInt(); + appTargetSdk = parcel.readInt(); + changeIdTargetSdk = parcel.readInt(); + } + + public OverrideAllowedState(@State int state, int appTargetSdk, int changeIdTargetSdk) { + this.state = state; + this.appTargetSdk = appTargetSdk; + this.changeIdTargetSdk = changeIdTargetSdk; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(state); + out.writeInt(appTargetSdk); + out.writeInt(changeIdTargetSdk); + } + + /** + * Enforces the policy for overriding compat changes. + * + * @param changeId the change id that was attempted to be overridden. + * @param packageName the package for which the attempt was made. + * @throws SecurityException if the policy forbids this operation. + */ + public void enforce(long changeId, String packageName) + throws SecurityException { + switch (state) { + case ALLOWED: + return; + case DISABLED_NOT_DEBUGGABLE: + throw new SecurityException( + "Cannot override a change on a non-debuggable app and user build."); + case DISABLED_NON_TARGET_SDK: + throw new SecurityException( + "Cannot override a default enabled/disabled change on a user build."); + case DISABLED_TARGET_SDK_TOO_HIGH: + throw new SecurityException(String.format( + "Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is " + + "above the change's targetSdk threshold (%4$d)", + changeId, packageName, appTargetSdk, changeIdTargetSdk)); + case PACKAGE_DOES_NOT_EXIST: + throw new SecurityException(String.format( + "Cannot override %1$d for %2$s because the package does not exist, and " + + "the change is targetSdk gated.", + changeId, packageName)); + } + } + + public static final @NonNull + Parcelable.Creator<OverrideAllowedState> CREATOR = + new Parcelable.Creator<OverrideAllowedState>() { + public OverrideAllowedState createFromParcel(Parcel parcel) { + OverrideAllowedState info = new OverrideAllowedState(parcel); + return info; + } + + public OverrideAllowedState[] newArray(int size) { + return new OverrideAllowedState[size]; + } + }; + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OverrideAllowedState)) { + return false; + } + OverrideAllowedState otherState = (OverrideAllowedState) obj; + return state == otherState.state + && appTargetSdk == otherState.appTargetSdk + && changeIdTargetSdk == otherState.changeIdTargetSdk; + } +} diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java index b250578c8454..9f15d8991fa7 100644 --- a/core/java/com/android/internal/infra/AndroidFuture.java +++ b/core/java/com/android/internal/infra/AndroidFuture.java @@ -75,6 +75,7 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable private static final boolean DEBUG = false; private static final String LOG_TAG = AndroidFuture.class.getSimpleName(); + private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; private final @NonNull Object mLock = new Object(); @GuardedBy("mLock") @@ -95,15 +96,7 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable // Done if (in.readBoolean()) { // Failed - try { - in.readException(); - } catch (Throwable e) { - completeExceptionally(e); - } - if (!isCompletedExceptionally()) { - throw new IllegalStateException( - "Error unparceling AndroidFuture: exception expected"); - } + completeExceptionally(unparcelException(in)); } else { // Success complete((T) in.readValue(null)); @@ -512,14 +505,9 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable T result; try { result = get(); - } catch (Exception t) { - // Exceptions coming out of get() are wrapped in ExecutionException, which is not - // handled by Parcel. - if (t instanceof ExecutionException && t.getCause() instanceof Exception) { - t = (Exception) t.getCause(); - } + } catch (Throwable t) { dest.writeBoolean(true); - dest.writeException(t); + parcelException(dest, unwrapExecutionException(t)); return; } dest.writeBoolean(false); @@ -528,22 +516,76 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable dest.writeStrongBinder(new IAndroidFuture.Stub() { @Override public void complete(AndroidFuture resultContainer) { + boolean changed; try { - AndroidFuture.this.complete((T) resultContainer.get()); + changed = AndroidFuture.this.complete((T) resultContainer.get()); } catch (Throwable t) { - // If resultContainer was completed exceptionally, get() wraps the - // underlying exception in an ExecutionException. Unwrap it now to avoid - // double-layering ExecutionExceptions. - if (t instanceof ExecutionException && t.getCause() != null) { - t = t.getCause(); - } - completeExceptionally(t); + changed = completeExceptionally(unwrapExecutionException(t)); + } + if (!changed) { + Log.w(LOG_TAG, "Remote result " + resultContainer + + " ignored, as local future is already completed: " + + AndroidFuture.this); } } }.asBinder()); } } + /** + * Exceptions coming out of {@link #get} are wrapped in {@link ExecutionException} + */ + Throwable unwrapExecutionException(Throwable t) { + return t instanceof ExecutionException + ? t.getCause() + : t; + } + + /** + * Alternative to {@link Parcel#writeException} that stores the stack trace, in a + * way consistent with the binder IPC exception propagation behavior. + */ + private static void parcelException(Parcel p, @Nullable Throwable t) { + p.writeBoolean(t == null); + if (t == null) { + return; + } + + p.writeInt(Parcel.getExceptionCode(t)); + p.writeString(t.getClass().getName()); + p.writeString(t.getMessage()); + p.writeStackTrace(t); + parcelException(p, t.getCause()); + } + + /** + * @see #parcelException + */ + private static @Nullable Throwable unparcelException(Parcel p) { + if (p.readBoolean()) { + return null; + } + + int exCode = p.readInt(); + String cls = p.readString(); + String msg = p.readString(); + String stackTrace = p.readInt() > 0 ? p.readString() : "\t<stack trace unavailable>"; + msg += "\n" + stackTrace; + + Exception ex = p.createExceptionOrNull(exCode, msg); + if (ex == null) { + ex = new RuntimeException(cls + ": " + msg); + } + ex.setStackTrace(EMPTY_STACK_TRACE); + + Throwable cause = unparcelException(p); + if (cause != null) { + ex.initCause(ex); + } + + return ex; + } + @Override public int describeContents() { return 0; diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index a86d702ba323..179828c4b456 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -35,7 +35,6 @@ import com.android.internal.logging.AndroidConfig; import com.android.server.NetworkManagementSocketTagger; import dalvik.system.RuntimeHooks; -import dalvik.system.ThreadPrioritySetter; import dalvik.system.VMRuntime; import libcore.content.type.MimeMap; @@ -205,7 +204,6 @@ public class RuntimeInit { */ public static void preForkInit() { if (DEBUG) Slog.d(TAG, "Entered preForkInit."); - RuntimeHooks.setThreadPrioritySetter(new RuntimeThreadPrioritySetter()); RuntimeInit.enableDdms(); // TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e. // MimeMap.setDefault(DefaultMimeMapFactory.create()); @@ -218,35 +216,6 @@ public class RuntimeInit { MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create); } - private static class RuntimeThreadPrioritySetter implements ThreadPrioritySetter { - // Should remain consistent with kNiceValues[] in system/libartpalette/palette_android.cc - private static final int[] NICE_VALUES = { - Process.THREAD_PRIORITY_LOWEST, // 1 (MIN_PRIORITY) - Process.THREAD_PRIORITY_BACKGROUND + 6, - Process.THREAD_PRIORITY_BACKGROUND + 3, - Process.THREAD_PRIORITY_BACKGROUND, - Process.THREAD_PRIORITY_DEFAULT, // 5 (NORM_PRIORITY) - Process.THREAD_PRIORITY_DEFAULT - 2, - Process.THREAD_PRIORITY_DEFAULT - 4, - Process.THREAD_PRIORITY_URGENT_DISPLAY + 3, - Process.THREAD_PRIORITY_URGENT_DISPLAY + 2, - Process.THREAD_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY) - }; - - @Override - public void setPriority(int priority) { - // Check NICE_VALUES[] length first. - if (NICE_VALUES.length != (1 + Thread.MAX_PRIORITY - Thread.MIN_PRIORITY)) { - throw new AssertionError("Unexpected NICE_VALUES.length=" + NICE_VALUES.length); - } - // Priority should be in the range of MIN_PRIORITY (1) to MAX_PRIORITY (10). - if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) { - throw new IllegalArgumentException("Priority out of range: " + priority); - } - Process.setThreadPriority(NICE_VALUES[priority - Thread.MIN_PRIORITY]); - } - } - @UnsupportedAppUsage protected static final void commonInit() { if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 2248b8853f8c..f0a346ab25fd 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -145,6 +145,11 @@ public final class Zygote { /** The lower file system should be bind mounted directly on external storage */ public static final int MOUNT_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH; + /** Use the regular scoped storage filesystem, but Android/ should be writable. + * Used to support the applications hosting DownloadManager and the MTP server. + */ + public static final int MOUNT_EXTERNAL_ANDROID_WRITABLE = IVold.REMOUNT_MODE_ANDROID_WRITABLE; + /** Number of bytes sent to the Zygote over USAP pipes or the pool event FD */ static final int USAP_MANAGEMENT_MESSAGE_BYTES = 8; diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index d3499541a3a3..37f570bba238 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -376,6 +376,8 @@ class ZygoteArguments { mMountExternal = Zygote.MOUNT_EXTERNAL_LEGACY; } else if (arg.equals("--mount-external-pass-through")) { mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH; + } else if (arg.equals("--mount-external-android-writable")) { + mMountExternal = Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; } else if (arg.equals("--query-abi-list")) { mAbiListQuery = true; } else if (arg.equals("--get-pid")) { diff --git a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl b/core/java/com/android/internal/telephony/IWapPushManager.aidl index 1c3df65336f8..9f6851b8c254 100644 --- a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl +++ b/core/java/com/android/internal/telephony/IWapPushManager.aidl @@ -18,6 +18,7 @@ package com.android.internal.telephony; import android.content.Intent; +/** @hide */ interface IWapPushManager { /** * Processes WAP push message and triggers the receiver application registered @@ -26,11 +27,10 @@ interface IWapPushManager { int processMessage(String app_id, String content_type, in Intent intent); /** - * Add receiver application into the application ID table. - * Returns true if inserting the information is successfull. Inserting the duplicated + * Adds receiver application into the application ID table. + * Returns true if inserting the information is successful. Inserting duplicated * record in the application ID table is not allowed. Use update/delete method. */ - @UnsupportedAppUsage boolean addPackage(String x_app_id, String content_type, String package_name, String class_name, int app_type, boolean need_signature, boolean further_processing); @@ -39,17 +39,14 @@ interface IWapPushManager { * Updates receiver application that is last added. * Returns true if updating the information is successfull. */ - @UnsupportedAppUsage boolean updatePackage(String x_app_id, String content_type, String package_name, String class_name, int app_type, boolean need_signature, boolean further_processing); /** - * Delites receiver application information. + * Deletes receiver application information. * Returns true if deleting is successfull. */ - @UnsupportedAppUsage boolean deletePackage(String x_app_id, String content_type, - String package_name, String class_name); + String package_name, String class_name); } - diff --git a/core/java/com/android/internal/telephony/WapPushManagerParams.java b/core/java/com/android/internal/telephony/WapPushManagerParams.java new file mode 100644 index 000000000000..eafb8f1b423f --- /dev/null +++ b/core/java/com/android/internal/telephony/WapPushManagerParams.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.telephony.WapPushManagerConnector; + +/** + * WapPushManager constant value definitions. + * @hide + */ +public class WapPushManagerParams { + /** + * Application type activity + */ + public static final int APP_TYPE_ACTIVITY = 0; + + /** + * Application type service + */ + public static final int APP_TYPE_SERVICE = 1; + + /** + * Process Message return value + * Message is handled + */ + public static final int MESSAGE_HANDLED = WapPushManagerConnector.RESULT_MESSAGE_HANDLED; + + /** + * Process Message return value + * Application ID or content type was not found in the application ID table + */ + public static final int APP_QUERY_FAILED = WapPushManagerConnector.RESULT_APP_QUERY_FAILED; + + /** + * Process Message return value + * Receiver application signature check failed + */ + public static final int SIGNATURE_NO_MATCH = WapPushManagerConnector.RESULT_SIGNATURE_NO_MATCH; + + /** + * Process Message return value + * Receiver application was not found + */ + public static final int INVALID_RECEIVER_NAME = + WapPushManagerConnector.RESULT_INVALID_RECEIVER_NAME; + + /** + * Process Message return value + * Unknown exception + */ + public static final int EXCEPTION_CAUGHT = WapPushManagerConnector.RESULT_EXCEPTION_CAUGHT; + + /** + * Process Message return value + * Need further processing after WapPushManager message processing + */ + public static final int FURTHER_PROCESSING = WapPushManagerConnector.RESULT_FURTHER_PROCESSING; +} diff --git a/core/java/com/android/internal/util/GrowingArrayUtils.java b/core/java/com/android/internal/util/GrowingArrayUtils.java index 9f563667e019..597fe6b53d11 100644 --- a/core/java/com/android/internal/util/GrowingArrayUtils.java +++ b/core/java/com/android/internal/util/GrowingArrayUtils.java @@ -16,7 +16,7 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive diff --git a/core/java/com/android/internal/util/HexDump.java b/core/java/com/android/internal/util/HexDump.java index 6ffc92853b08..ad88dd6deec6 100644 --- a/core/java/com/android/internal/util/HexDump.java +++ b/core/java/com/android/internal/util/HexDump.java @@ -17,7 +17,7 @@ package com.android.internal.util; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; public class HexDump { diff --git a/core/java/com/android/internal/util/IState.java b/core/java/com/android/internal/util/IState.java index eb66e2ce94d7..07837bf8f587 100644 --- a/core/java/com/android/internal/util/IState.java +++ b/core/java/com/android/internal/util/IState.java @@ -16,7 +16,7 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Message; /** diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java index 03a555edf4a8..34c6a055d5bd 100644 --- a/core/java/com/android/internal/util/IndentingPrintWriter.java +++ b/core/java/com/android/internal/util/IndentingPrintWriter.java @@ -16,7 +16,8 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.io.PrintWriter; import java.io.Writer; import java.util.Arrays; diff --git a/core/java/com/android/internal/util/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java index 065cc5b2416b..a9d8f7239d03 100644 --- a/core/java/com/android/internal/util/JournaledFile.java +++ b/core/java/com/android/internal/util/JournaledFile.java @@ -16,7 +16,7 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import java.io.File; diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java index 580c2fa66de2..5de77d9b0545 100644 --- a/core/java/com/android/internal/util/MemInfoReader.java +++ b/core/java/com/android/internal/util/MemInfoReader.java @@ -16,7 +16,7 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Debug; import android.os.StrictMode; diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 3fff5c233890..5bc96d8ee1d3 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -18,7 +18,7 @@ package com.android.internal.util; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.text.TextUtils; import java.util.Collection; diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java index 3c61e035e886..636378e32091 100644 --- a/core/java/com/android/internal/util/State.java +++ b/core/java/com/android/internal/util/State.java @@ -16,7 +16,7 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Message; /** diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index 6c217e5a37bf..0c2406559dcc 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -16,7 +16,7 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 8799e3d4c6bf..c1be33a215b8 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -16,10 +16,10 @@ package com.android.internal.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Bitmap.CompressFormat; +import android.graphics.BitmapFactory; import android.net.Uri; import android.text.TextUtils; import android.util.ArrayMap; diff --git a/core/java/com/android/internal/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java index d18c35e703da..d16cb4362f1b 100644 --- a/core/java/com/android/internal/view/ActionBarPolicy.java +++ b/core/java/com/android/internal/view/ActionBarPolicy.java @@ -16,15 +16,15 @@ package com.android.internal.view; -import com.android.internal.R; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Build; +import com.android.internal.R; + /** * Allows components to query for various configuration policy decisions * about how the action bar should lay out and behave on the current device. diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 2ac2975d0a44..5dd3389b5d4c 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -16,6 +16,7 @@ package com.android.internal.view; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Point; import android.graphics.Rect; import android.hardware.input.InputManager; @@ -34,8 +35,6 @@ import android.view.WindowInsets.Type.InsetsType; import com.android.internal.os.IResultReceiver; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.io.IOException; public class BaseIWindow extends IWindow.Stub { diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index ececba13c760..6278d4a35329 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -18,7 +18,7 @@ package com.android.internal.view; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; import android.os.Handler; diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index 1b133d2a8393..a5964b509b3c 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -20,7 +20,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 0c057ea6df59..a41048c0f426 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -19,7 +19,7 @@ package com.android.internal.view; import android.annotation.AnyThread; import android.annotation.BinderThread; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.inputmethodservice.AbstractInputMethodService; import android.os.Bundle; import android.os.Handler; diff --git a/core/java/com/android/internal/view/WindowManagerPolicyThread.java b/core/java/com/android/internal/view/WindowManagerPolicyThread.java index b009a2d8ca30..6d691fce4fb0 100644 --- a/core/java/com/android/internal/view/WindowManagerPolicyThread.java +++ b/core/java/com/android/internal/view/WindowManagerPolicyThread.java @@ -16,7 +16,7 @@ package com.android.internal.view; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Looper; /** diff --git a/core/java/com/android/internal/view/menu/ActionMenu.java b/core/java/com/android/internal/view/menu/ActionMenu.java index 977c1f6fda7b..648262965ab1 100644 --- a/core/java/com/android/internal/view/menu/ActionMenu.java +++ b/core/java/com/android/internal/view/menu/ActionMenu.java @@ -16,10 +16,7 @@ package com.android.internal.view.menu; -import java.util.ArrayList; -import java.util.List; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,6 +27,9 @@ import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; +import java.util.ArrayList; +import java.util.List; + /** * @hide */ diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java index ed253d58fb82..bd8bcb9cf81e 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItem.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java @@ -17,7 +17,7 @@ package com.android.internal.view.menu; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index eb94db33ba1a..7622b939b6eb 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -16,7 +16,7 @@ package com.android.internal.view.menu; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; diff --git a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java index 3d3aceb4a85f..a9f5e47fc83f 100644 --- a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java +++ b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java @@ -16,7 +16,7 @@ package com.android.internal.view.menu; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.IBinder; diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java index 3d888d347d65..539c71e5c473 100644 --- a/core/java/com/android/internal/view/menu/IconMenuItemView.java +++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java @@ -16,13 +16,12 @@ package com.android.internal.view.menu; -import com.android.internal.view.menu.MenuBuilder.ItemInvoker; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.text.Layout; import android.text.TextUtils; import android.util.AttributeSet; import android.view.Gravity; @@ -30,7 +29,8 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.widget.TextView; -import android.text.Layout; + +import com.android.internal.view.menu.MenuBuilder.ItemInvoker; /** * The item view for each item in the {@link IconMenuView}. diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java index 6f264341f7b4..9e240dbd8a21 100644 --- a/core/java/com/android/internal/view/menu/IconMenuView.java +++ b/core/java/com/android/internal/view/menu/IconMenuView.java @@ -16,9 +16,7 @@ package com.android.internal.view.menu; -import com.android.internal.view.menu.MenuBuilder.ItemInvoker; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -29,10 +27,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.LayoutInflater; + +import com.android.internal.view.menu.MenuBuilder.ItemInvoker; import java.util.ArrayList; diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 0e07ca79faf7..b31ae38b4566 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -18,7 +18,7 @@ package com.android.internal.view.menu; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/core/java/com/android/internal/view/menu/MenuDialogHelper.java b/core/java/com/android/internal/view/menu/MenuDialogHelper.java index 88d0a03bd55f..d02b8f6ceb63 100644 --- a/core/java/com/android/internal/view/menu/MenuDialogHelper.java +++ b/core/java/com/android/internal/view/menu/MenuDialogHelper.java @@ -16,9 +16,9 @@ package com.android.internal.view.menu; -import android.annotation.UnsupportedAppUsage; import android.app.AlertDialog; import android.app.Dialog; +import android.compat.annotation.UnsupportedAppUsage; import android.content.DialogInterface; import android.os.IBinder; import android.view.KeyEvent; diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index 994a9c117ce9..218f5185ec47 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -16,10 +16,8 @@ package com.android.internal.view.menu; -import com.android.internal.view.menu.MenuView.ItemView; - import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -39,6 +37,8 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.widget.LinearLayout; +import com.android.internal.view.menu.MenuView.ItemView; + /** * @hide */ diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index d00108edefd0..bac602509148 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -20,7 +20,7 @@ import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StyleRes; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java index c5df8ad6edc6..35b8fefe75ab 100644 --- a/core/java/com/android/internal/view/menu/MenuPresenter.java +++ b/core/java/com/android/internal/view/menu/MenuPresenter.java @@ -18,7 +18,7 @@ package com.android.internal.view.menu; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Parcelable; import android.view.ViewGroup; diff --git a/core/java/com/android/internal/view/menu/MenuView.java b/core/java/com/android/internal/view/menu/MenuView.java index 67a55308938d..a31c820cd2a6 100644 --- a/core/java/com/android/internal/view/menu/MenuView.java +++ b/core/java/com/android/internal/view/menu/MenuView.java @@ -16,10 +16,7 @@ package com.android.internal.view.menu; -import com.android.internal.view.menu.MenuBuilder; -import com.android.internal.view.menu.MenuItemImpl; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.drawable.Drawable; /** diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java index cf6d9746bb93..6eb215e94093 100644 --- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java +++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java @@ -16,7 +16,7 @@ package com.android.internal.view.menu; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.drawable.Drawable; import android.view.Menu; diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java index 9ccee7fc32ff..0f0c1a3de3a2 100644 --- a/core/java/com/android/internal/widget/AbsActionBarView.java +++ b/core/java/com/android/internal/widget/AbsActionBarView.java @@ -15,26 +15,25 @@ */ package com.android.internal.widget; -import com.android.internal.R; - -import android.util.TypedValue; -import android.view.ContextThemeWrapper; -import android.view.MotionEvent; -import android.widget.ActionMenuPresenter; -import android.widget.ActionMenuView; - import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; +import android.widget.ActionMenuPresenter; +import android.widget.ActionMenuView; + +import com.android.internal.R; public abstract class AbsActionBarView extends ViewGroup { private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator(); diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 78ed53fa918c..051526ef2da7 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -15,13 +15,7 @@ */ package com.android.internal.widget; -import com.android.internal.R; - -import android.widget.ActionMenuPresenter; -import android.widget.ActionMenuView; -import com.android.internal.view.menu.MenuBuilder; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; @@ -32,9 +26,14 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; +import android.widget.ActionMenuPresenter; +import android.widget.ActionMenuView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.internal.R; +import com.android.internal.view.menu.MenuBuilder; + /** * @hide */ diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index e9e3cdab7a10..aca0b713686f 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -18,7 +18,7 @@ package com.android.internal.widget; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -41,6 +41,7 @@ import android.view.Window; import android.view.WindowInsets; import android.widget.OverScroller; import android.widget.Toolbar; + import com.android.internal.view.menu.MenuPresenter; /** diff --git a/core/java/com/android/internal/widget/AlertDialogLayout.java b/core/java/com/android/internal/widget/AlertDialogLayout.java index 7a0174946671..d879b6d569f3 100644 --- a/core/java/com/android/internal/widget/AlertDialogLayout.java +++ b/core/java/com/android/internal/widget/AlertDialogLayout.java @@ -18,8 +18,8 @@ package com.android.internal.widget; import android.annotation.AttrRes; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.annotation.StyleRes; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java index 0ca67438c5c3..ff131071efef 100644 --- a/core/java/com/android/internal/widget/ButtonBarLayout.java +++ b/core/java/com/android/internal/widget/ButtonBarLayout.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java index 35bff6d7c430..74ad81566ef4 100644 --- a/core/java/com/android/internal/widget/CachingIconView.java +++ b/core/java/com/android/internal/widget/CachingIconView.java @@ -18,7 +18,7 @@ package com.android.internal.widget; import android.annotation.DrawableRes; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.graphics.Bitmap; diff --git a/core/java/com/android/internal/widget/DialogTitle.java b/core/java/com/android/internal/widget/DialogTitle.java index 405436c53ff0..0bfd684317fd 100644 --- a/core/java/com/android/internal/widget/DialogTitle.java +++ b/core/java/com/android/internal/widget/DialogTitle.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; import android.text.Layout; diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 2b648e90f7dd..ff3543c837eb 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.text.Editable; import android.text.method.KeyListener; diff --git a/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java b/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java index cc7911da0b96..9ef9f697c46c 100644 --- a/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java +++ b/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java @@ -16,12 +16,12 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.view.View; import android.view.MotionEvent; +import android.view.View; import android.widget.LinearLayout; diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 540c43892935..f37a46811b81 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -25,11 +25,11 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.app.admin.DevicePolicyManager; import android.app.admin.PasswordMetrics; import android.app.trust.IStrongAuthTracker; import android.app.trust.TrustManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -55,10 +55,10 @@ import android.util.SparseLongArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; -import com.google.android.collect.Lists; - import libcore.util.HexEncoding; +import com.google.android.collect.Lists; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.MessageDigest; diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 74a0aa37dafb..4ddc782aacb4 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -19,7 +19,7 @@ package com.android.internal.widget; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java index d2156704a655..c8f901133be6 100644 --- a/core/java/com/android/internal/widget/NumericTextView.java +++ b/core/java/com/android/internal/widget/NumericTextView.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 37046afaaa85..dc8d57ab709e 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; diff --git a/core/java/com/android/internal/widget/PreferenceImageView.java b/core/java/com/android/internal/widget/PreferenceImageView.java index 02a0b8d436b9..43b6b5a169c5 100644 --- a/core/java/com/android/internal/widget/PreferenceImageView.java +++ b/core/java/com/android/internal/widget/PreferenceImageView.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.util.AttributeSet; import android.widget.ImageView; diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java index b66a7b44f05d..43a227a32346 100644 --- a/core/java/com/android/internal/widget/RecyclerView.java +++ b/core/java/com/android/internal/widget/RecyclerView.java @@ -20,7 +20,7 @@ import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; import android.database.Observable; diff --git a/core/java/com/android/internal/widget/ScrollBarUtils.java b/core/java/com/android/internal/widget/ScrollBarUtils.java index 982e3152fc7c..3e9d697a0ace 100644 --- a/core/java/com/android/internal/widget/ScrollBarUtils.java +++ b/core/java/com/android/internal/widget/ScrollBarUtils.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; public class ScrollBarUtils { diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index 5d48ab910439..aa0b0bbd4c19 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -15,13 +15,11 @@ */ package com.android.internal.widget; -import com.android.internal.view.ActionBarPolicy; - import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.annotation.UnsupportedAppUsage; import android.app.ActionBar; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; @@ -42,6 +40,8 @@ import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; +import com.android.internal.view.ActionBarPolicy; + /** * This widget implements the dynamic action bar tab behavior that can change * across different configurations or circumstances. diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java index 4b5d62467af0..5e6f3a46de7d 100644 --- a/core/java/com/android/internal/widget/SlidingTab.java +++ b/core/java/com/android/internal/widget/SlidingTab.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -34,12 +34,12 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; import android.view.animation.LinearInterpolator; import android.view.animation.TranslateAnimation; -import android.view.animation.Animation.AnimationListener; import android.widget.ImageView; -import android.widget.TextView; import android.widget.ImageView.ScaleType; +import android.widget.TextView; import com.android.internal.R; diff --git a/core/java/com/android/internal/widget/TextViewInputDisabler.java b/core/java/com/android/internal/widget/TextViewInputDisabler.java index 8d8f0fe52d64..57806eb21dcf 100644 --- a/core/java/com/android/internal/widget/TextViewInputDisabler.java +++ b/core/java/com/android/internal/widget/TextViewInputDisabler.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.text.InputFilter; import android.text.Spanned; import android.widget.TextView; diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java index 7d36b02d4157..c8a86d108134 100644 --- a/core/java/com/android/internal/widget/ViewPager.java +++ b/core/java/com/android/internal/widget/ViewPager.java @@ -18,7 +18,7 @@ package com.android.internal.widget; import android.annotation.DrawableRes; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; diff --git a/core/java/com/android/server/ResettableTimeout.java b/core/java/com/android/server/ResettableTimeout.java index 64083f72aff5..511af941bf76 100644 --- a/core/java/com/android/server/ResettableTimeout.java +++ b/core/java/com/android/server/ResettableTimeout.java @@ -16,10 +16,9 @@ package com.android.server; -import android.os.SystemClock; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ConditionVariable; +import android.os.SystemClock; /** * Utility class that you can call on with a timeout, and get called back diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 8a59c998dacb..74b481c938c3 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -218,6 +218,7 @@ public class SystemConfig { final ArrayMap<String, ArraySet<String>> mAllowedAssociations = new ArrayMap<>(); private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>(); + private final ArraySet<String> mAppDataIsolationWhitelistedApps = new ArraySet<>(); // Map of packagesNames to userTypes. Stored temporarily until cleared by UserManagerService(). private ArrayMap<String, Set<String>> mPackageToUserTypeWhitelist = new ArrayMap<>(); @@ -389,6 +390,10 @@ public class SystemConfig { return mRollbackWhitelistedPackages; } + public ArraySet<String> getAppDataIsolationWhitelistedApps() { + return mAppDataIsolationWhitelistedApps; + } + /** * Gets map of packagesNames to userTypes, dictating on which user types each package should be * initially installed, and then removes this map from SystemConfig. @@ -1045,6 +1050,16 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "app-data-isolation-whitelisted-app": { + String pkgname = parser.getAttributeValue(null, "package"); + if (pkgname == null) { + Slog.w(TAG, "<" + name + "> without package in " + permFile + + " at " + parser.getPositionDescription()); + } else { + mAppDataIsolationWhitelistedApps.add(pkgname); + } + XmlUtils.skipCurrentTag(parser); + } break; case "bugreport-whitelisted": { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java index e1a10a5805f5..2a9c0b44b45e 100644 --- a/core/java/com/android/server/net/BaseNetworkObserver.java +++ b/core/java/com/android/server/net/BaseNetworkObserver.java @@ -16,7 +16,7 @@ package com.android.server.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.INetworkManagementEventObserver; import android.net.LinkAddress; import android.net.RouteInfo; diff --git a/core/java/com/android/server/net/NetlinkTracker.java b/core/java/com/android/server/net/NetlinkTracker.java index 647fb5b9d079..b57397f46a7e 100644 --- a/core/java/com/android/server/net/NetlinkTracker.java +++ b/core/java/com/android/server/net/NetlinkTracker.java @@ -16,7 +16,7 @@ package com.android.server.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; diff --git a/core/java/com/google/android/collect/Lists.java b/core/java/com/google/android/collect/Lists.java index 8f6594aefb0a..585847da566c 100644 --- a/core/java/com/google/android/collect/Lists.java +++ b/core/java/com/google/android/collect/Lists.java @@ -16,7 +16,8 @@ package com.google.android.collect; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.util.ArrayList; import java.util.Collections; diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java index 6ba33207631a..cd4c1280545e 100644 --- a/core/java/com/google/android/collect/Maps.java +++ b/core/java/com/google/android/collect/Maps.java @@ -16,7 +16,7 @@ package com.google.android.collect; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.util.ArrayMap; import java.util.HashMap; diff --git a/core/java/com/google/android/collect/Sets.java b/core/java/com/google/android/collect/Sets.java index 09b5e51ae2c6..c67a88a19080 100644 --- a/core/java/com/google/android/collect/Sets.java +++ b/core/java/com/google/android/collect/Sets.java @@ -16,7 +16,7 @@ package com.google.android.collect; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.util.ArraySet; import java.util.Collections; diff --git a/core/java/com/google/android/util/AbstractMessageParser.java b/core/java/com/google/android/util/AbstractMessageParser.java index f11e6b2342b7..0da7607cba6e 100644 --- a/core/java/com/google/android/util/AbstractMessageParser.java +++ b/core/java/com/google/android/util/AbstractMessageParser.java @@ -16,7 +16,7 @@ package com.google.android.util; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.util.ArrayList; import java.util.HashMap; diff --git a/core/java/org/apache/http/conn/ssl/AbstractVerifier.java b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java index 36d6e22ca847..2848ad7796af 100644 --- a/core/java/org/apache/http/conn/ssl/AbstractVerifier.java +++ b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java @@ -31,7 +31,7 @@ package org.apache.http.conn.ssl; -import java.util.regex.Pattern; +import android.compat.annotation.UnsupportedAppUsage; import java.io.IOException; import java.security.cert.Certificate; @@ -43,10 +43,10 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; -import java.util.logging.Logger; import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; -import android.annotation.UnsupportedAppUsage; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java index b2e8b5e7af05..ffae7570ea79 100644 --- a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java +++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java @@ -31,20 +31,14 @@ package org.apache.http.conn.ssl; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; + import org.apache.http.conn.scheme.HostNameResolver; import org.apache.http.conn.scheme.LayeredSocketFactory; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; -import android.annotation.UnsupportedAppUsage; -import android.os.Build; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -57,6 +51,14 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + /** * Layered socket factory for TLS/SSL connections, based on JSSE. *. diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp index 90cc98699827..0f56779135b8 100644 --- a/core/jni/android/graphics/apex/android_bitmap.cpp +++ b/core/jni/android/graphics/apex/android_bitmap.cpp @@ -122,6 +122,99 @@ AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) { return getInfo(bitmap->info(), bitmap->rowBytes()); } +static bool nearlyEqual(float a, float b) { + // By trial and error, this is close enough to match for the ADataSpaces we + // compare for. + return ::fabs(a-b) < .002f; +} + +static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) { + return nearlyEqual(x.g, y.g) + && nearlyEqual(x.a, y.a) + && nearlyEqual(x.b, y.b) + && nearlyEqual(x.c, y.c) + && nearlyEqual(x.d, y.d) + && nearlyEqual(x.e, y.e) + && nearlyEqual(x.f, y.f); +} + +static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false; + } + } + return true; +} + +static constexpr skcms_TransferFunction k2Dot6 = + { 2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + +// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut +// matches the white point used by ColorSpace.Named.DCIP3. +static constexpr skcms_Matrix3x3 kDCIP3 = {{ + { 0.486143, 0.323835, 0.154234 }, + { 0.226676, 0.710327, 0.0629966 }, + { 0.000800549, 0.0432385, 0.78275 }, +}}; + +ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + const SkImageInfo& info = bitmap->info(); + SkColorSpace* colorSpace = info.colorSpace(); + if (!colorSpace) { + return ADATASPACE_UNKNOWN; + } + + if (colorSpace->isSRGB()) { + if (info.colorType() == kRGBA_F16_SkColorType) { + return ADATASPACE_SCRGB; + } + return ADATASPACE_SRGB; + } + + skcms_TransferFunction fn; + LOG_ALWAYS_FATAL_IF(!colorSpace->isNumericalTransferFn(&fn)); + + skcms_Matrix3x3 gamut; + LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut)); + + if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) { + if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) { + // Skia doesn't differentiate amongst the RANGES. In Java, we associate + // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs. + // Make the same association here. + if (info.colorType() == kRGBA_F16_SkColorType) { + return ADATASPACE_SCRGB_LINEAR; + } + return ADATASPACE_SRGB_LINEAR; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) { + return ADATASPACE_BT709; + } + } + + if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) { + return ADATASPACE_DISPLAY_P3; + } + + if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) { + return ADATASPACE_ADOBE_RGB; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) + && nearlyEqual(gamut, SkNamedGamut::kRec2020)) { + return ADATASPACE_BT2020; + } + + if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) { + return ADATASPACE_DCI_P3; + } + + return ADATASPACE_UNKNOWN; +} + AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) { uint32_t rowBytes = 0; SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes); diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h index f231eeddb7e2..32b8a450e147 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h +++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h @@ -17,6 +17,7 @@ #define ANDROID_GRAPHICS_BITMAP_H #include <android/bitmap.h> +#include <android/data_space.h> #include <jni.h> #include <sys/cdefs.h> @@ -49,6 +50,7 @@ void ABitmap_acquireRef(ABitmap* bitmap); void ABitmap_releaseRef(ABitmap* bitmap); AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap); +ADataSpace ABitmap_getDataSpace(ABitmap* bitmap); void* ABitmap_getPixels(ABitmap* bitmap); void ABitmap_notifyPixelsChanged(ABitmap* bitmap); @@ -106,6 +108,7 @@ namespace graphics { ABitmap* get() const { return mBitmap; } AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); } + ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); } void* getPixels() const { return ABitmap_getPixels(mBitmap); } void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); } @@ -119,4 +122,4 @@ namespace graphics { }; // namespace android #endif // __cplusplus -#endif // ANDROID_GRAPHICS_BITMAP_H
\ No newline at end of file +#endif // ANDROID_GRAPHICS_BITMAP_H diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index d17d0a4ebbe2..5039213954d7 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -319,7 +319,8 @@ enum MountExternalKind { MOUNT_EXTERNAL_INSTALLER = 5, MOUNT_EXTERNAL_FULL = 6, MOUNT_EXTERNAL_PASS_THROUGH = 7, - MOUNT_EXTERNAL_COUNT = 8 + MOUNT_EXTERNAL_ANDROID_WRITABLE = 8, + MOUNT_EXTERNAL_COUNT = 9 }; // The order of entries here must be kept in sync with MountExternalKind enum values. @@ -331,6 +332,8 @@ static const std::array<const std::string, MOUNT_EXTERNAL_COUNT> ExternalStorage "/mnt/runtime/write", // MOUNT_EXTERNAL_LEGACY "/mnt/runtime/write", // MOUNT_EXTERNAL_INSTALLER "/mnt/runtime/full", // MOUNT_EXTERNAL_FULL + "/mnt/runtime/full", // MOUNT_EXTERNAL_PASS_THROUGH (only used w/ FUSE) + "/mnt/runtime/full", // MOUNT_EXTERNAL_ANDROID_WRITABLE (only used w/ FUSE) }; // Must match values in com.android.internal.os.Zygote. @@ -755,12 +758,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn); if (isFuse) { - if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH || mount_mode == - MOUNT_EXTERNAL_INSTALLER || mount_mode == MOUNT_EXTERNAL_FULL) { - // For now, MediaProvider, installers and "full" get the pass_through mount - // view, which is currently identical to the sdcardfs write view. - // - // TODO(b/146189163): scope down MOUNT_EXTERNAL_INSTALLER + if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) { BindMount(pass_through_source, "/storage", fail_fn); } else { BindMount(user_source, "/storage", fail_fn); diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 5624f457a9b2..082a2892e34b 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -33,27 +33,27 @@ // Static whitelist of open paths that the zygote is allowed to keep open. static const char* kPathWhitelist[] = { - "/apex/com.android.appsearch/javalib/framework-appsearch.jar", - "/apex/com.android.conscrypt/javalib/conscrypt.jar", - "/apex/com.android.ipsec/javalib/ike.jar", - "/apex/com.android.media/javalib/updatable-media.jar", - "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar", - "/apex/com.android.os.statsd/javalib/framework-statsd.jar", - "/apex/com.android.sdkext/javalib/framework-sdkext.jar", - "/apex/com.android.wifi/javalib/framework-wifi.jar", - "/apex/com.android.tethering/javalib/framework-tethering.jar", - "/dev/null", - "/dev/socket/zygote", - "/dev/socket/zygote_secondary", - "/dev/socket/usap_pool_primary", - "/dev/socket/usap_pool_secondary", - "/dev/socket/webview_zygote", - "/dev/socket/heapprofd", - "/sys/kernel/debug/tracing/trace_marker", - "/system/framework/framework-res.apk", - "/dev/urandom", - "/dev/ion", - "/dev/dri/renderD129", // Fixes b/31172436 + "/apex/com.android.appsearch/javalib/framework-appsearch.jar", + "/apex/com.android.conscrypt/javalib/conscrypt.jar", + "/apex/com.android.ipsec/javalib/ike.jar", + "/apex/com.android.media/javalib/updatable-media.jar", + "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar", + "/apex/com.android.os.statsd/javalib/framework-statsd.jar", + "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar", + "/apex/com.android.wifi/javalib/framework-wifi.jar", + "/apex/com.android.tethering/javalib/framework-tethering.jar", + "/dev/null", + "/dev/socket/zygote", + "/dev/socket/zygote_secondary", + "/dev/socket/usap_pool_primary", + "/dev/socket/usap_pool_secondary", + "/dev/socket/webview_zygote", + "/dev/socket/heapprofd", + "/sys/kernel/debug/tracing/trace_marker", + "/system/framework/framework-res.apk", + "/dev/urandom", + "/dev/ion", + "/dev/dri/renderD129", // Fixes b/31172436 }; static const char kFdPath[] = "/proc/self/fd"; diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index d5a3b5e91151..b83b31c1303a 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -702,6 +702,12 @@ enum Action { // CATEGORY: SETTINGS // OS: R ACTION_DASHBOARD_VISIBLE_TIME = 1729; + + // ACTION: Allow "Access all files" for an app + APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_ALLOW = 1730; + + // ACTION: Deny "Access all files" for an app + APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY = 1731; } /** @@ -2542,4 +2548,9 @@ enum PageId { // OS: R FUELGAUGE_BATTERY_SHARE = 1821; + // OPEN: Settings -> Apps & Notifications -> Special App Access + // CATEGORY: SETTINGS + // OS: R + MANAGE_EXTERNAL_STORAGE = 1822; + } diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto index 70627edf2cb3..c6925f448a58 100644 --- a/core/proto/android/server/animationadapter.proto +++ b/core/proto/android/server/animationadapter.proto @@ -50,6 +50,7 @@ message AnimationSpecProto { optional WindowAnimationSpecProto window = 1; optional MoveAnimationSpecProto move = 2; optional AlphaAnimationSpecProto alpha = 3; + optional RotationAnimationSpecProto rotate = 4; } /* represents WindowAnimationSpec */ @@ -76,3 +77,12 @@ message AlphaAnimationSpecProto { optional float to = 2; optional int64 duration_ms = 3; } + +/* represents RotationAnimationSpec */ +message RotationAnimationSpecProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional float start_luma = 1; + optional float end_luma = 2; + optional int64 duration_ms = 3; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 31faff6bd314..03d34b595e4a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -456,7 +456,6 @@ <protected-broadcast android:name="android.intent.action.internal_sim_state_changed" /> <protected-broadcast android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> <protected-broadcast android:name="android.intent.action.PRECISE_CALL_STATE" /> - <protected-broadcast android:name="android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.intent.action.SUBSCRIPTION_PHONE_STATE" /> <protected-broadcast android:name="android.intent.action.USER_INFO_CHANGED" /> <protected-broadcast android:name="android.intent.action.USER_UNLOCKED" /> @@ -934,11 +933,10 @@ <!-- Allows an application a broad access to external storage in scoped storage. Intended to be used by few apps that need to manage files on behalf of the users. - <p>Protection level: signature|appop - <p>This protection level is temporary and will most likely be changed to |preinstalled --> + <p>Protection level: signature|appop|preinstalled --> <permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:permissionGroup="android.permission-group.UNDEFINED" - android:protectionLevel="signature|appop" /> + android:protectionLevel="signature|appop|preinstalled" /> <!-- ====================================================================== --> <!-- Permissions for accessing the device location --> @@ -1656,6 +1654,7 @@ <!-- Allows holder to request bluetooth/wifi scan bypassing global "use location" setting and location permissions. <p>Not for use by third-party or privileged applications. + @SystemApi @hide --> <permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION" @@ -4757,6 +4756,10 @@ <!-- Allows input events to be monitored. Very dangerous! @hide --> <permission android:name="android.permission.MONITOR_INPUT" android:protectionLevel="signature" /> + <!-- Allows the caller to change the associations between input devices and displays. + Very dangerous! @hide --> + <permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT" + android:protectionLevel="signature" /> <!-- Allows query of any normal app on the device, regardless of manifest declarations. --> <permission android:name="android.permission.QUERY_ALL_PACKAGES" diff --git a/core/res/res/anim/screen_rotate_0_enter.xml b/core/res/res/anim/screen_rotate_0_enter.xml index 93cf3652d185..629be7ea2d30 100644 --- a/core/res/res/anim/screen_rotate_0_enter.xml +++ b/core/res/res/anim/screen_rotate_0_enter.xml @@ -1,25 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* -** Copyright 2010, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> + ~ 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. + --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:interpolator="@interpolator/decelerate_quint" - android:duration="@android:integer/config_shortAnimTime" /> + android:shareInterpolator="false"> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:interpolator="@interpolator/screen_rotation_alpha_in" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:duration="@android:integer/config_screen_rotation_fade_in" /> </set> diff --git a/core/res/res/anim/screen_rotate_0_exit.xml b/core/res/res/anim/screen_rotate_0_exit.xml index 37d5a4115621..fa046a036855 100644 --- a/core/res/res/anim/screen_rotate_0_exit.xml +++ b/core/res/res/anim/screen_rotate_0_exit.xml @@ -1,22 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* -** Copyright 2010, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> + ~ 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. + --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> + android:shareInterpolator="false"> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:interpolator="@interpolator/screen_rotation_alpha_out" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:duration="@android:integer/config_screen_rotation_fade_out" /> </set> diff --git a/core/res/res/anim/screen_rotate_0_frame.xml b/core/res/res/anim/screen_rotate_0_frame.xml deleted file mode 100644 index 5ea9bf8205e3..000000000000 --- a/core/res/res/anim/screen_rotate_0_frame.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2012, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:interpolator="@interpolator/decelerate_quint" - android:duration="@android:integer/config_shortAnimTime" /> -</set> diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml index 688a8d5bb2aa..889a615e07f4 100644 --- a/core/res/res/anim/screen_rotate_180_enter.xml +++ b/core/res/res/anim/screen_rotate_180_enter.xml @@ -18,11 +18,11 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> + android:shareInterpolator="false"> <rotate android:fromDegrees="180" android:toDegrees="0" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> + android:pivotX="50%" android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/fast_out_slow_in" + android:duration="@android:integer/config_screen_rotation_total_180" /> </set> diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml index 58a1868bd398..766fcfae1f91 100644 --- a/core/res/res/anim/screen_rotate_180_exit.xml +++ b/core/res/res/anim/screen_rotate_180_exit.xml @@ -18,11 +18,11 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> + android:shareInterpolator="false"> <rotate android:fromDegrees="0" android:toDegrees="-180" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> + android:pivotX="50%" android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/fast_out_slow_in" + android:duration="@android:integer/config_screen_rotation_total_180" /> </set>
\ No newline at end of file diff --git a/core/res/res/anim/screen_rotate_alpha.xml b/core/res/res/anim/screen_rotate_alpha.xml index c49ef9cafd39..2cac982e24b4 100644 --- a/core/res/res/anim/screen_rotate_alpha.xml +++ b/core/res/res/anim/screen_rotate_alpha.xml @@ -20,8 +20,8 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:interpolator="@interpolator/decelerate_quint" + android:interpolator="@interpolator/screen_rotation_alpha_out" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> + android:duration="@android:integer/config_screen_rotation_fade_out" /> </set> diff --git a/core/res/res/anim/screen_rotate_minus_90_enter.xml b/core/res/res/anim/screen_rotate_minus_90_enter.xml index b16d5fc761ee..87fd25ea4603 100644 --- a/core/res/res/anim/screen_rotate_minus_90_enter.xml +++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml @@ -18,19 +18,17 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> - <!-- Version for two-phase anim + android:shareInterpolator="false"> <rotate android:fromDegrees="-90" android:toDegrees="0" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_longAnimTime" /> - --> - <rotate android:fromDegrees="-90" android:toDegrees="0" - android:pivotX="50%" android:pivotY="50%" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:duration="@android:integer/config_mediumAnimTime" /> + android:pivotX="50%" android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/fast_out_slow_in" + android:duration="@android:integer/config_screen_rotation_total_90" /> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/screen_rotation_alpha_in" + android:startOffset="@android:integer/config_screen_rotation_fade_in_delay" + android:duration="@android:integer/config_screen_rotation_fade_in" /> </set> diff --git a/core/res/res/anim/screen_rotate_minus_90_exit.xml b/core/res/res/anim/screen_rotate_minus_90_exit.xml index 0927dd30ceb3..c3aee14dc235 100644 --- a/core/res/res/anim/screen_rotate_minus_90_exit.xml +++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml @@ -18,26 +18,16 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> - <!-- Version for two-phase animation + android:shareInterpolator="false"> <rotate android:fromDegrees="0" android:toDegrees="90" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_longAnimTime" /> - --> - <scale android:fromXScale="100%" android:toXScale="100%p" - android:fromYScale="100%" android:toYScale="100%p" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> - <rotate android:fromDegrees="0" android:toDegrees="90" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> + android:pivotX="50%" android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/fast_out_slow_in" + android:duration="@android:integer/config_screen_rotation_total_90" /> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/screen_rotation_alpha_out" + android:duration="@android:integer/config_screen_rotation_fade_out" /> </set> diff --git a/core/res/res/anim/screen_rotate_plus_90_enter.xml b/core/res/res/anim/screen_rotate_plus_90_enter.xml index 86a8d24cbbcc..8849db421e75 100644 --- a/core/res/res/anim/screen_rotate_plus_90_enter.xml +++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml @@ -18,19 +18,16 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> - <!-- Version for two-phase animation + android:shareInterpolator="false"> <rotate android:fromDegrees="90" android:toDegrees="0" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_longAnimTime" /> - --> - <rotate android:fromDegrees="90" android:toDegrees="0" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> + android:pivotX="50%" android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/fast_out_slow_in" + android:duration="@android:integer/config_screen_rotation_total_90" /> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:fillEnabled="true" + android:interpolator="@interpolator/screen_rotation_alpha_in" + android:startOffset="@android:integer/config_screen_rotation_fade_in_delay" + android:duration="@android:integer/config_screen_rotation_fade_in" /> </set> diff --git a/core/res/res/anim/screen_rotate_plus_90_exit.xml b/core/res/res/anim/screen_rotate_plus_90_exit.xml index fd786f9afce0..de84c3bd08fc 100644 --- a/core/res/res/anim/screen_rotate_plus_90_exit.xml +++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml @@ -18,26 +18,16 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false"> - <!-- Version for two-phase animation + android:shareInterpolator="false"> <rotate android:fromDegrees="0" android:toDegrees="-90" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_longAnimTime" /> - --> - <scale android:fromXScale="100%" android:toXScale="100%p" - android:fromYScale="100%" android:toYScale="100%p" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> - <rotate android:fromDegrees="0" android:toDegrees="-90" - android:pivotX="50%" android:pivotY="50%" - android:interpolator="@interpolator/decelerate_quint" - android:fillEnabled="true" - android:fillBefore="true" android:fillAfter="true" - android:duration="@android:integer/config_mediumAnimTime" /> + android:pivotX="50%" android:pivotY="50%" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@interpolator/fast_out_slow_in" + android:duration="@android:integer/config_screen_rotation_total_90" /> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:interpolator="@interpolator/screen_rotation_alpha_out" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:duration="@android:integer/config_screen_rotation_fade_out" /> </set> diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/res/res/interpolator/screen_rotation_alpha_in.xml new file mode 100644 index 000000000000..9c566a7c8f23 --- /dev/null +++ b/core/res/res/interpolator/screen_rotation_alpha_in.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0.15" + android:controlY1="0.45" + android:controlX2="0.33" + android:controlY2="1"/> diff --git a/core/res/res/interpolator/screen_rotation_alpha_out.xml b/core/res/res/interpolator/screen_rotation_alpha_out.xml new file mode 100644 index 000000000000..73a37d4f1aa5 --- /dev/null +++ b/core/res/res/interpolator/screen_rotation_alpha_out.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0.57" + android:controlY1="0" + android:controlX2="0.71" + android:controlY2=".43"/> diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml index 1edd2cdfcfe3..d19e313055ae 100644 --- a/core/res/res/layout/accessibility_button_chooser_item.xml +++ b/core/res/res/layout/accessibility_button_chooser_item.xml @@ -36,7 +36,7 @@ android:id="@+id/accessibility_button_target_label" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="8dp" + android:layout_marginStart="14dp" android:layout_weight="1" android:textColor="?attr/textColorPrimary"/> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6691d4c5955b..245aed1484f7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -147,6 +147,24 @@ <integer name="config_activityShortDur">150</integer> <integer name="config_activityDefaultDur">220</integer> + <!-- Fade out time for screen rotation --> + <integer name="config_screen_rotation_fade_out">116</integer> + + <!-- Fade in time for screen rotation --> + <integer name="config_screen_rotation_fade_in">233</integer> + + <!-- Fade in delay time for screen rotation --> + <integer name="config_screen_rotation_fade_in_delay">100</integer> + + <!-- Total time for 90 degree screen rotation animations --> + <integer name="config_screen_rotation_total_90">333</integer> + + <!-- Total time for 180 degree screen rotation animation --> + <integer name="config_screen_rotation_total_180">433</integer> + + <!-- Total time for the rotation background color transition --> + <integer name="config_screen_rotation_color_transition">200</integer> + <!-- The duration (in milliseconds) of the tooltip show/hide animations. --> <integer name="config_tooltipAnimTime">150</integer> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 66267d136e10..a0e40646ead9 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4358,10 +4358,7 @@ <string name="accessibility_shortcut_menu_button">Empty</string> <!-- Text in button that edit the accessibility shortcut menu. [CHAR LIMIT=100] --> - <string name="edit_accessibility_shortcut_menu_button">Edit</string> - - <!-- Text in button that save the accessibility shortcut menu changed status. [CHAR LIMIT=100] --> - <string name="save_accessibility_shortcut_menu_button">Save</string> + <string name="edit_accessibility_shortcut_menu_button">Edit shortcuts</string> <!-- Text in button that cancel the accessibility shortcut menu changed status. [CHAR LIMIT=100] --> <string name="cancel_accessibility_shortcut_menu_button">Cancel</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 01bd510dcf27..973d5f6392f4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1805,7 +1805,6 @@ <!-- From services --> <java-symbol type="anim" name="screen_rotate_0_enter" /> <java-symbol type="anim" name="screen_rotate_0_exit" /> - <java-symbol type="anim" name="screen_rotate_0_frame" /> <java-symbol type="anim" name="screen_rotate_180_enter" /> <java-symbol type="anim" name="screen_rotate_180_exit" /> <java-symbol type="anim" name="screen_rotate_180_frame" /> @@ -1981,6 +1980,7 @@ <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" /> <java-symbol type="integer" name="config_brightness_ramp_rate_fast" /> <java-symbol type="integer" name="config_brightness_ramp_rate_slow" /> + <java-symbol type="integer" name="config_screen_rotation_color_transition" /> <java-symbol type="layout" name="am_compat_mode_dialog" /> <java-symbol type="layout" name="launch_warning" /> <java-symbol type="layout" name="safe_mode" /> @@ -3212,7 +3212,6 @@ <java-symbol type="id" name="accessibility_button_target_switch_item" /> <java-symbol type="string" name="accessibility_magnification_chooser_text" /> <java-symbol type="string" name="edit_accessibility_shortcut_menu_button" /> - <java-symbol type="string" name="save_accessibility_shortcut_menu_button" /> <java-symbol type="string" name="cancel_accessibility_shortcut_menu_button" /> <java-symbol type="drawable" name="ic_accessibility_magnification" /> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java index f0a83678f70b..a296ca27e268 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java @@ -16,6 +16,7 @@ package com.android.connectivitymanagertest; +import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; import android.net.LinkAddress; @@ -136,7 +137,7 @@ public class WifiConfigurationHelper { config.enterpriseConfig.setPhase2Method(phase2); config.enterpriseConfig.setIdentity(identity); config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity); - config.enterpriseConfig.setCaCertificateAlias(caCert); + config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert}); config.enterpriseConfig.setClientCertificateAlias(clientCert); return config; } @@ -147,8 +148,12 @@ public class WifiConfigurationHelper { private static WifiConfiguration createGenericConfig(String ssid) { WifiConfiguration config = new WifiConfiguration(); config.SSID = quotedString(ssid); - config.setIpAssignment(IpAssignment.DHCP); - config.setProxySettings(ProxySettings.NONE); + + IpConfiguration ipConfiguration = config.getIpConfiguration(); + ipConfiguration.setIpAssignment(IpAssignment.DHCP); + ipConfiguration.setProxySettings(ProxySettings.NONE); + config.setIpConfiguration(ipConfiguration); + return config; } @@ -237,6 +242,7 @@ public class WifiConfigurationHelper { throw new IllegalArgumentException(); } + IpConfiguration ipConfiguration = config.getIpConfiguration(); if (jsonConfig.has("ip")) { StaticIpConfiguration staticIpConfig = new StaticIpConfiguration(); @@ -247,13 +253,14 @@ public class WifiConfigurationHelper { staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns1"))); staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns2"))); - config.setIpAssignment(IpAssignment.STATIC); - config.setStaticIpConfiguration(staticIpConfig); + ipConfiguration.setIpAssignment(IpAssignment.STATIC); + ipConfiguration.setStaticIpConfiguration(staticIpConfig); } else { - config.setIpAssignment(IpAssignment.DHCP); + ipConfiguration.setIpAssignment(IpAssignment.DHCP); } + ipConfiguration.setProxySettings(ProxySettings.NONE); + config.setIpConfiguration(ipConfiguration); - config.setProxySettings(ProxySettings.NONE); return config; } diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java index 2989df83866c..5d42915fc82b 100644 --- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java +++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java @@ -268,7 +268,7 @@ public class BandwidthTest extends InstrumentationTestCase { File snd_stat = new File (root_filepath + "tcp_snd"); int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat); NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); - stats.addValues(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT, + stats.addEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT, NetworkStats.TAG_NONE, rx, 0, tx, 0, 0); return stats; } diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java index 707d7b30e09b..239f971664e9 100644 --- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java +++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java @@ -54,7 +54,7 @@ public class NetworkStatsBenchmark { recycle.txBytes = 150000; recycle.txPackets = 1500; recycle.operations = 0; - mNetworkStats.addValues(recycle); + mNetworkStats.addEntry(recycle); if (recycle.set == 1) { uid++; } @@ -70,7 +70,7 @@ public class NetworkStatsBenchmark { recycle.txBytes = 180000 * mSize; recycle.txPackets = 1200 * mSize; recycle.operations = 0; - mNetworkStats.addValues(recycle); + mNetworkStats.addEntry(recycle); } } diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java index 68b9b0079761..f4709ff0bc00 100644 --- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java @@ -33,14 +33,15 @@ import android.os.ParcelFileDescriptor.AutoCloseInputStream; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.support.test.uiautomator.UiDevice; import android.test.InstrumentationTestCase; import android.util.Log; -import libcore.io.Streams; - import com.google.mockwebserver.MockResponse; import com.google.mockwebserver.MockWebServer; +import libcore.io.Streams; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; @@ -63,6 +64,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { private static final String TAG = "DownloadManagerBaseTest"; protected DownloadManager mDownloadManager = null; private MockWebServer mServer = null; + private UiDevice mUiDevice = null; protected String mFileType = "text/plain"; protected Context mContext = null; protected MultipleDownloadsCompletedReceiver mReceiver = null; @@ -234,6 +236,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { @Override public void setUp() throws Exception { mContext = getInstrumentation().getContext(); + mUiDevice = UiDevice.getInstance(getInstrumentation()); mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE); mServer = new MockWebServer(); mServer.play(); @@ -512,7 +515,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase { Log.i(LOG_TAG, "Setting WiFi State to: " + enable); WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); - manager.setWifiEnabled(enable); + mUiDevice.executeShellCommand("svc wifi " + (enable ? "enable" : "disable")); String timeoutMessage = "Timed out waiting for Wifi to be " + (enable ? "enabled!" : "disabled!"); diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index beaaa373b1a0..d8b527c8a11a 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -438,8 +438,7 @@ public class ActivityThreadTest { } private static ClientTransaction newStopTransaction(Activity activity) { - final StopActivityItem stopStateRequest = - StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */); + final StopActivityItem stopStateRequest = StopActivityItem.obtain(0 /* configChanges */); final ClientTransaction transaction = newTransaction(activity); transaction.setLifecycleStateRequest(stopStateRequest); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 37d21f0928be..4b29d59de332 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -274,30 +274,15 @@ public class ObjectPoolTests { @Test public void testRecycleStopItem() { - StopActivityItem emptyItem = StopActivityItem.obtain(false, 0); - StopActivityItem item = StopActivityItem.obtain(true, 4); + StopActivityItem emptyItem = StopActivityItem.obtain(0); + StopActivityItem item = StopActivityItem.obtain(4); assertNotSame(item, emptyItem); assertFalse(item.equals(emptyItem)); item.recycle(); assertEquals(item, emptyItem); - StopActivityItem item2 = StopActivityItem.obtain(true, 3); - assertSame(item, item2); - assertFalse(item2.equals(emptyItem)); - } - - @Test - public void testRecycleWindowVisibleItem() { - WindowVisibilityItem emptyItem = WindowVisibilityItem.obtain(false); - WindowVisibilityItem item = WindowVisibilityItem.obtain(true); - assertNotSame(item, emptyItem); - assertFalse(item.equals(emptyItem)); - - item.recycle(); - assertEquals(item, emptyItem); - - WindowVisibilityItem item2 = WindowVisibilityItem.obtain(true); + StopActivityItem item2 = StopActivityItem.obtain(3); assertSame(item, item2); assertFalse(item2.equals(emptyItem)); } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 39bf7421b15e..ecea9011e704 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -61,6 +61,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -180,31 +181,6 @@ public class TransactionParcelTests { } @Test - public void testWindowVisibilityChange() { - // Write to parcel - WindowVisibilityItem item = WindowVisibilityItem.obtain(true /* showWindow */); - writeAndPrepareForReading(item); - - // Read from parcel and assert - WindowVisibilityItem result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel); - - assertEquals(item.hashCode(), result.hashCode()); - assertTrue(item.equals(result)); - - // Check different value - item = WindowVisibilityItem.obtain(false); - - mParcel = Parcel.obtain(); - writeAndPrepareForReading(item); - - // Read from parcel and assert - result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel); - - assertEquals(item.hashCode(), result.hashCode()); - assertTrue(item.equals(result)); - } - - @Test public void testDestroy() { DestroyActivityItem item = DestroyActivityItem.obtain(true /* finished */, 135 /* configChanges */); @@ -299,8 +275,7 @@ public class TransactionParcelTests { @Test public void testStop() { // Write to parcel - StopActivityItem item = StopActivityItem.obtain(true /* showWindow */, - 14 /* configChanges */); + StopActivityItem item = StopActivityItem.obtain(14 /* configChanges */); writeAndPrepareForReading(item); // Read from parcel and assert @@ -311,14 +286,26 @@ public class TransactionParcelTests { } @Test + public void testStart() { + // Write to parcel + StartActivityItem item = StartActivityItem.obtain(); + writeAndPrepareForReading(item); + + // Read from parcel and assert + StartActivityItem result = StartActivityItem.CREATOR.createFromParcel(mParcel); + + assertEquals(item.hashCode(), result.hashCode()); + assertEquals(item, result); + } + + @Test public void testClientTransaction() { // Write to parcel - WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true); + NewIntentItem callback1 = NewIntentItem.obtain(new ArrayList<>(), true); ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( config()); - StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */, - 78 /* configChanges */); + StopActivityItem lifecycleRequest = StopActivityItem.obtain(78 /* configChanges */); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); @@ -340,7 +327,7 @@ public class TransactionParcelTests { @Test public void testClientTransactionCallbacksOnly() { // Write to parcel - WindowVisibilityItem callback1 = WindowVisibilityItem.obtain(true); + NewIntentItem callback1 = NewIntentItem.obtain(new ArrayList<>(), true); ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( config()); @@ -363,8 +350,7 @@ public class TransactionParcelTests { @Test public void testClientTransactionLifecycleOnly() { // Write to parcel - StopActivityItem lifecycleRequest = StopActivityItem.obtain(true /* showWindow */, - 78 /* configChanges */); + StopActivityItem lifecycleRequest = StopActivityItem.obtain(78 /* configChanges */); IApplicationThread appThread = new StubAppThread(); Binder activityToken = new Binder(); diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java index de6f8f7231fa..750ffa1c9a54 100644 --- a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java @@ -22,7 +22,7 @@ import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcel import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import org.junit.Test; diff --git a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java index 9b3d0c9eaff6..b88c36f20bc6 100644 --- a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java @@ -22,7 +22,7 @@ import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcel import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import org.junit.Test; diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java index bee270e5f5c9..ba29a97b55ab 100644 --- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java @@ -22,7 +22,7 @@ import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcel import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import org.junit.Test; diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/os/TimestampedValueTest.java index 6fc2400316c2..f36d9e6b1eff 100644 --- a/core/tests/coretests/src/android/util/TimestampedValueTest.java +++ b/core/tests/coretests/src/android/os/TimestampedValueTest.java @@ -14,14 +14,12 @@ * limitations under the License. */ -package android.util; +package android.os; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; -import android.os.Parcel; - import androidx.test.runner.AndroidJUnit4; import org.junit.Test; diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index 0e19ca84d433..d0fd92a838c9 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -90,12 +90,12 @@ public class ImeInsetsSourceConsumerTest { mImeConsumer.onWindowFocusGained(); mImeConsumer.applyImeVisibility(true); mController.cancelExistingAnimation(); - assertTrue(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); // test if setVisibility can hide IME mImeConsumer.applyImeVisibility(false); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); }); } diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 179929f2aae0..fa61a0a0250b 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -16,11 +16,11 @@ package android.view; +import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowInsets.Type.systemBars; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -39,8 +39,6 @@ import android.view.SurfaceControl.Transaction; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.test.InsetsModeSession; -import androidx.test.runner.AndroidJUnit4; - import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -52,6 +50,8 @@ import org.mockito.MockitoAnnotations; import java.util.List; +import androidx.test.runner.AndroidJUnit4; + /** * Tests for {@link InsetsAnimationControlImpl}. * @@ -116,7 +116,7 @@ public class InsetsAnimationControlImplTest { mController = new InsetsAnimationControlImpl(controls, new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(), mMockController, 10 /* durationMs */, - false /* fade */); + false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN); } @Test diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index a89fc1e6315f..1db96b15f83a 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -68,6 +68,7 @@ public class InsetsControllerTest { private InsetsController mController; private SurfaceSession mSession = new SurfaceSession(); private SurfaceControl mLeash; + private ViewRootImpl mViewRoot; @Before public void setup() { @@ -77,13 +78,13 @@ public class InsetsControllerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { Context context = InstrumentationRegistry.getTargetContext(); // cannot mock ViewRootImpl since it's final. - ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay()); + mViewRoot = new ViewRootImpl(context, context.getDisplay()); try { - viewRootImpl.setView(new TextView(context), new LayoutParams(), null); + mViewRoot.setView(new TextView(context), new LayoutParams(), null); } catch (BadTokenException e) { // activity isn't running, we will ignore BadTokenException. } - mController = new InsetsController(viewRootImpl); + mController = new InsetsController(mViewRoot); final Rect rect = new Rect(5, 5, 5, 5); mController.calculateInsets( false, @@ -117,16 +118,22 @@ public class InsetsControllerTest { @Test public void testControlsRevoked_duringAnim() { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); - mController.onControlsChanged(new InsetsSourceControl[] { control }); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + InsetsSourceControl control = + new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); + mController.onControlsChanged(new InsetsSourceControl[] { control }); - WindowInsetsAnimationControlListener mockListener = - mock(WindowInsetsAnimationControlListener.class); - mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */, mockListener); - verify(mockListener).onReady(any(), anyInt()); - mController.onControlsChanged(new InsetsSourceControl[0]); - verify(mockListener).onCancelled(); + WindowInsetsAnimationControlListener mockListener = + mock(WindowInsetsAnimationControlListener.class); + mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */, + mockListener); + + // Ready gets deferred until next predraw + mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); + verify(mockListener).onReady(any(), anyInt()); + mController.onControlsChanged(new InsetsSourceControl[0]); + verify(mockListener).onCancelled(); + }); } @Test @@ -154,16 +161,16 @@ public class InsetsControllerTest { mController.show(Type.all()); // quickly jump to final state by cancelling it. mController.cancelExistingAnimation(); - assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); mController.applyImeVisibility(false /* setVisible */); mController.hide(Type.all()); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost(); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -180,10 +187,10 @@ public class InsetsControllerTest { mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(); mController.applyImeVisibility(true); mController.cancelExistingAnimation(); - assertTrue(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); mController.applyImeVisibility(false); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost(); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -201,16 +208,16 @@ public class InsetsControllerTest { // test show select types. mController.show(types); mController.cancelExistingAnimation(); - assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); // test hide all mController.hide(types); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @@ -227,29 +234,29 @@ public class InsetsControllerTest { // test show select types. mController.show(types); mController.cancelExistingAnimation(); - assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); // test hide all mController.hide(Type.all()); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); // test single show mController.show(Type.navigationBars()); mController.cancelExistingAnimation(); - assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); // test single hide mController.hide(Type.navigationBars()); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -267,31 +274,31 @@ public class InsetsControllerTest { mController.show(Type.navigationBars()); mController.show(Type.systemBars()); mController.cancelExistingAnimation(); - assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); mController.hide(Type.navigationBars()); mController.hide(Type.systemBars()); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); int types = Type.navigationBars() | Type.systemBars(); // show two at a time and hide one by one. mController.show(types); mController.hide(Type.navigationBars()); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); mController.hide(Type.systemBars()); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @@ -309,15 +316,15 @@ public class InsetsControllerTest { mController.show(types); mController.hide(Type.navigationBars()); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); mController.hide(Type.systemBars()); mController.cancelExistingAnimation(); - assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible()); - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible()); + assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @@ -336,12 +343,16 @@ public class InsetsControllerTest { ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor = ArgumentCaptor.forClass(WindowInsetsAnimationController.class); + + // Ready gets deferred until next predraw + mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); + verify(mockListener).onReady(controllerCaptor.capture(), anyInt()); controllerCaptor.getValue().finish(false /* shown */); }); waitUntilNextFrame(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isVisible()); + assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible()); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 7af833bfcba4..492c03653990 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -96,7 +96,7 @@ public class InsetsSourceConsumerTest { @Test public void testHide() { mConsumer.hide(); - assertFalse("Consumer should not be visible", mConsumer.isVisible()); + assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible()); verify(mSpyInsetsSource).setVisible(eq(false)); } @@ -106,7 +106,7 @@ public class InsetsSourceConsumerTest { // Insets source starts out visible mConsumer.hide(); mConsumer.show(); - assertTrue("Consumer should be visible", mConsumer.isVisible()); + assertTrue("Consumer should be visible", mConsumer.isRequestedVisible()); verify(mSpyInsetsSource).setVisible(eq(false)); verify(mSpyInsetsSource).setVisible(eq(true)); } diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 60620881b6f2..fa2ffccaaa63 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -23,6 +23,7 @@ import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.WindowInsets.Type.ime; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static org.junit.Assert.assertEquals; @@ -116,14 +117,18 @@ public class InsetsStateTest { @Test public void testCalculateInsets_imeIgnoredWithoutAdjustResize() { - mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); - mState.getSource(ITYPE_STATUS_BAR).setVisible(true); - mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); - mState.getSource(ITYPE_IME).setVisible(true); - WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT, null, null, 0, null); - assertEquals(0, insets.getSystemWindowInsetBottom()); - assertTrue(insets.isVisible(ime())); + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(true); + mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(ITYPE_IME).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, null); + assertEquals(0, insets.getSystemWindowInsetBottom()); + assertEquals(100, insets.getInsets(ime()).bottom); + assertTrue(insets.isVisible(ime())); + } } @Test diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java index 8c7b28af2e78..e5a4f6d5b3be 100644 --- a/core/tests/coretests/src/android/view/WindowInsetsTest.java +++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java @@ -63,7 +63,8 @@ public class WindowInsetsTest { b.setInsets(navigationBars(), Insets.of(0, 0, 0, 100)); b.setInsets(ime(), Insets.of(0, 0, 0, 300)); WindowInsets insets = b.build(); - assertEquals(300, insets.getSystemWindowInsets().bottom); + assertEquals(100, insets.getSystemWindowInsets().bottom); + assertEquals(300, insets.getInsets(ime()).bottom); } // TODO: Move this to CTS once API made public diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java index b9b6d55b52d6..e23c51e66a02 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.accessibility; +package android.view.accessibility; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertSame; @@ -29,16 +29,17 @@ import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Instrumentation; +import android.app.PendingIntent; +import android.app.RemoteAction; +import android.content.Intent; +import android.graphics.drawable.Icon; import android.os.UserHandle; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.IAccessibilityManager; -import android.view.accessibility.IAccessibilityManagerClient; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.IntPair; +import com.android.server.accessibility.test.MessageCapturingHandler; import org.junit.After; import org.junit.Before; @@ -57,6 +58,16 @@ import java.util.List; public class AccessibilityManagerTest { private static final boolean WITH_A11Y_ENABLED = true; private static final boolean WITH_A11Y_DISABLED = false; + private static final String LABEL = "label"; + private static final String INTENT_ACTION = "TESTACTION"; + private static final String DESCRIPTION = "description"; + private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast( + InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0); + private static final RemoteAction TEST_ACTION = new RemoteAction( + Icon.createWithContentUri("content://test"), + LABEL, + DESCRIPTION, + TEST_PENDING_INTENT); @Mock private IAccessibilityManager mMockService; private MessageCapturingHandler mHandler; @@ -122,6 +133,29 @@ public class AccessibilityManagerTest { } @Test + public void testRegisterSystemAction() throws Exception { + AccessibilityManager manager = createManager(WITH_A11Y_ENABLED); + RemoteAction action = new RemoteAction( + Icon.createWithContentUri("content://test"), + LABEL, + DESCRIPTION, + TEST_PENDING_INTENT); + final int actionId = 0; + manager.registerSystemAction(TEST_ACTION, actionId); + + verify(mMockService).registerSystemAction(TEST_ACTION, actionId); + } + + @Test + public void testUnregisterSystemAction() throws Exception { + AccessibilityManager manager = createManager(WITH_A11Y_ENABLED); + final int actionId = 0; + manager.unregisterSystemAction(actionId); + + verify(mMockService).unregisterSystemAction(actionId); + } + + @Test public void testIsEnabled() throws Exception { // Create manager with a11y enabled AccessibilityManager manager = createManager(WITH_A11Y_ENABLED); diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index f497db2256dd..89c237498e5c 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -226,6 +226,61 @@ public class EditorCursorDragTest { } @Test + public void testEditor_onTouchEvent_quickTapAfterDrag() throws Throwable { + String text = "Hi world!"; + onView(withId(R.id.textview)).perform(replaceText(text)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0)); + + TextView tv = mActivity.findViewById(R.id.textview); + Editor editor = tv.getEditorForTesting(); + + // Simulate a tap-and-drag gesture. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, 5f, 10f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); + assertFalse(editor.getInsertionController().isCursorBeingModified()); + assertFalse(editor.getSelectionController().isCursorBeingModified()); + + long event2Time = 1002; + MotionEvent event2 = moveEvent(event1Time, event2Time, 50f, 10f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2)); + assertTrue(editor.getInsertionController().isCursorBeingModified()); + assertFalse(editor.getSelectionController().isCursorBeingModified()); + + long event3Time = 1003; + MotionEvent event3 = moveEvent(event1Time, event3Time, 100f, 10f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3)); + assertTrue(editor.getInsertionController().isCursorBeingModified()); + assertFalse(editor.getSelectionController().isCursorBeingModified()); + + long event4Time = 2004; + MotionEvent event4 = upEvent(event1Time, event4Time, 100f, 10f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4)); + assertFalse(editor.getInsertionController().isCursorBeingModified()); + assertFalse(editor.getSelectionController().isCursorBeingModified()); + + // Simulate a quick tap after the drag, near the location where the drag ended. + long event5Time = 2005; + MotionEvent event5 = downEvent(event5Time, event5Time, 90f, 10f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5)); + assertFalse(editor.getInsertionController().isCursorBeingModified()); + assertFalse(editor.getSelectionController().isCursorBeingModified()); + + long event6Time = 2006; + MotionEvent event6 = upEvent(event5Time, event6Time, 90f, 10f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event6)); + assertFalse(editor.getInsertionController().isCursorBeingModified()); + assertFalse(editor.getSelectionController().isCursorBeingModified()); + + // Simulate another quick tap in the same location; now selection should be triggered. + long event7Time = 2007; + MotionEvent event7 = downEvent(event7Time, event7Time, 90f, 10f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event7)); + assertFalse(editor.getInsertionController().isCursorBeingModified()); + assertTrue(editor.getSelectionController().isCursorBeingModified()); + } + + @Test public void testEditor_onTouchEvent_cursorDrag() throws Throwable { String text = "testEditor_onTouchEvent_cursorDrag"; onView(withId(R.id.textview)).perform(replaceText(text)); @@ -237,29 +292,25 @@ public class EditorCursorDragTest { // Simulate a tap-and-drag gesture. This should trigger a cursor drag. long event1Time = 1001; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event1)); - mInstrumentation.waitForIdleSync(); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event2Time = 1002; MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event2)); - mInstrumentation.waitForIdleSync(); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event3Time = 1003; - MotionEvent event3 = moveEvent(event3Time, event3Time, 120f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event3)); - mInstrumentation.waitForIdleSync(); + MotionEvent event3 = moveEvent(event1Time, event3Time, 120f, 30f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3)); assertTrue(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event4Time = 1004; - MotionEvent event4 = upEvent(event3Time, event4Time, 120f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event4)); - mInstrumentation.waitForIdleSync(); + MotionEvent event4 = upEvent(event1Time, event4Time, 120f, 30f); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); } @@ -276,36 +327,31 @@ public class EditorCursorDragTest { // Simulate a double-tap followed by a drag. This should trigger a selection drag. long event1Time = 1001; MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event1)); - mInstrumentation.waitForIdleSync(); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event2Time = 1002; MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event2)); - mInstrumentation.waitForIdleSync(); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event3Time = 1003; MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event3)); - mInstrumentation.waitForIdleSync(); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertTrue(editor.getSelectionController().isCursorBeingModified()); long event4Time = 1004; MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event4)); - mInstrumentation.waitForIdleSync(); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertTrue(editor.getSelectionController().isCursorBeingModified()); long event5Time = 1005; MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f); - mActivity.runOnUiThread(() -> editor.onTouchEvent(event5)); - mInstrumentation.waitForIdleSync(); + mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); } diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index 6adb1b8fa0d6..215d0b800074 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -120,6 +120,60 @@ public class EditorTouchStateTest { } @Test + public void testUpdate_doubleTap_delayAfterFirstDownEvent() throws Exception { + // Simulate an ACTION_DOWN event. + long event1Time = 1000; + MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + mTouchState.update(event1, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + + // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout. + long event2Time = 1000 + ViewConfiguration.getDoubleTapTimeout() + 1; + MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); + mTouchState.update(event2, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false); + + // Generate an ACTION_DOWN event whose time is within the double-tap timeout when + // calculated from the last ACTION_UP event time. Even though the time between the last up + // and this down event is within the double-tap timeout, this should not be considered a + // double-tap (since the first down event had a longer delay). + long event3Time = event2Time + 1; + MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f); + mTouchState.update(event3, mConfig); + assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false); + } + + @Test + public void testUpdate_quickTapAfterDrag() throws Exception { + // Simulate an ACTION_DOWN event. + long event1Time = 1000; + MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + mTouchState.update(event1, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0, false); + + // Simulate an ACTION_MOVE event. + long event2Time = 1001; + MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f); + mTouchState.update(event2, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 0, 0, true); + + // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout. + long event3Time = 5000; + MotionEvent event3 = upEvent(event1Time, event3Time, 200f, 31f); + mTouchState.update(event3, mConfig); + assertSingleTap(mTouchState, 20f, 30f, 200f, 31f, false); + + // Generate an ACTION_DOWN event whose time is within the double-tap timeout when + // calculated from the last ACTION_UP event time. Even though the time between the last up + // and this down event is within the double-tap timeout, this should not be considered a + // double-tap (since the first down event had a longer delay). + long event4Time = event3Time + 1; + MotionEvent event4 = downEvent(event4Time, event4Time, 200f, 31f); + mTouchState.update(event4, mConfig); + assertSingleTap(mTouchState, 200f, 31f, 200f, 31f, false); + } + + @Test public void testUpdate_tripleClick_mouse() throws Exception { // Simulate an ACTION_DOWN event. long event1Time = 1000; diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java index ffc925ff82cd..f108eb8aeb0b 100644 --- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java +++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java @@ -121,7 +121,12 @@ public class AndroidFutureTest { AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel); ExecutionException executionException = expectThrows(ExecutionException.class, future2::get); - assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class); + + Throwable cause = executionException.getCause(); + String msg = cause.getMessage(); + assertThat(cause).isInstanceOf(UnsupportedOperationException.class); + assertThat(msg).contains(getClass().getName()); + assertThat(msg).contains("testWriteToParcel_Exception"); } @Test diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 66d84aaf35cd..9018320e479c 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -79,138 +79,6 @@ public class ActivityThreadClientTest { @Test @UiThreadTest - public void testWindowVisibilityChange_OnCreate() throws Exception { - try (ClientMockSession clientSession = new ClientMockSession()) { - ActivityClientRecord r = clientSession.stubActivityRecord(); - - clientSession.launchActivity(r); - assertEquals(ON_CREATE, r.getLifecycleState()); - - clientSession.changeVisibility(r, true); - assertEquals(ON_CREATE, r.getLifecycleState()); - - clientSession.changeVisibility(r, false); - assertEquals(ON_CREATE, r.getLifecycleState()); - } - } - - @Test - @UiThreadTest - public void testWindowVisibilityChange_OnCreate_Finished() throws Exception { - try (ClientMockSession clientSession = new ClientMockSession()) { - ActivityClientRecord r = clientSession.stubActivityRecord(); - - Activity activity = clientSession.launchActivity(r); - activity.finish(); - assertEquals(ON_CREATE, r.getLifecycleState()); - - clientSession.changeVisibility(r, true); - assertEquals(ON_CREATE, r.getLifecycleState()); - - clientSession.changeVisibility(r, false); - assertEquals(ON_CREATE, r.getLifecycleState()); - } - } - - @Test - @UiThreadTest - public void testWindowVisibilityChange_OnStart() throws Exception { - try (ClientMockSession clientSession = new ClientMockSession()) { - ActivityClientRecord r = clientSession.stubActivityRecord(); - - clientSession.launchActivity(r); - clientSession.startActivity(r); - assertEquals(ON_START, r.getLifecycleState()); - - clientSession.changeVisibility(r, false); - assertEquals(ON_STOP, r.getLifecycleState()); - - clientSession.changeVisibility(r, true); - assertEquals(ON_START, r.getLifecycleState()); - } - } - - @Test - @UiThreadTest - public void testWindowVisibilityChange_OnStart_Finished() throws Exception { - try (ClientMockSession clientSession = new ClientMockSession()) { - ActivityClientRecord r = clientSession.stubActivityRecord(); - - Activity activity = clientSession.launchActivity(r); - clientSession.startActivity(r); - activity.finish(); - assertEquals(ON_START, r.getLifecycleState()); - - clientSession.changeVisibility(r, false); - assertEquals(ON_STOP, r.getLifecycleState()); - - clientSession.changeVisibility(r, true); - assertEquals(ON_START, r.getLifecycleState()); - } - } - - @Test - @UiThreadTest - public void testWindowVisibilityChange_OnResume() throws Exception { - try (ClientMockSession clientSession = new ClientMockSession()) { - ActivityClientRecord r = clientSession.stubActivityRecord(); - - clientSession.launchActivity(r); - clientSession.startActivity(r); - clientSession.resumeActivity(r); - assertEquals(ON_RESUME, r.getLifecycleState()); - - clientSession.changeVisibility(r, false); - assertEquals(ON_STOP, r.getLifecycleState()); - - clientSession.changeVisibility(r, true); - assertEquals(ON_START, r.getLifecycleState()); - } - } - - @Test - @UiThreadTest - public void testWindowVisibilityChange_OnPause() throws Exception { - try (ClientMockSession clientSession = new ClientMockSession()) { - ActivityClientRecord r = clientSession.stubActivityRecord(); - - clientSession.launchActivity(r); - clientSession.startActivity(r); - clientSession.resumeActivity(r); - clientSession.pauseActivity(r); - assertEquals(ON_PAUSE, r.getLifecycleState()); - - clientSession.changeVisibility(r, false); - assertEquals(ON_STOP, r.getLifecycleState()); - - clientSession.changeVisibility(r, true); - assertEquals(ON_START, r.getLifecycleState()); - } - } - - @Test - @UiThreadTest - public void testWindowVisibilityChange_OnStop() throws Exception { - try (ClientMockSession clientSession = new ClientMockSession()) { - ActivityClientRecord r = clientSession.stubActivityRecord(); - - clientSession.launchActivity(r); - clientSession.startActivity(r); - clientSession.resumeActivity(r); - clientSession.pauseActivity(r); - clientSession.stopActivity(r); - assertEquals(ON_STOP, r.getLifecycleState()); - - clientSession.changeVisibility(r, true); - assertEquals(ON_START, r.getLifecycleState()); - - clientSession.changeVisibility(r, false); - assertEquals(ON_STOP, r.getLifecycleState()); - } - } - - @Test - @UiThreadTest public void testLifecycleAfterFinished_OnCreate() throws Exception { try (ClientMockSession clientSession = new ClientMockSession()) { ActivityClientRecord r = clientSession.stubActivityRecord(); @@ -308,7 +176,7 @@ public class ActivityThreadClientTest { } private void startActivity(ActivityClientRecord r) { - mThread.handleStartActivity(r, null /* pendingActions */); + mThread.handleStartActivity(r.token, null /* pendingActions */); } private void resumeActivity(ActivityClientRecord r) { @@ -323,7 +191,7 @@ public class ActivityThreadClientTest { } private void stopActivity(ActivityClientRecord r) { - mThread.handleStopActivity(r.token, false /* show */, 0 /* configChanges */, + mThread.handleStopActivity(r.token, 0 /* configChanges */, new PendingTransactionActions(), false /* finalStateRequest */, "test"); } @@ -332,10 +200,6 @@ public class ActivityThreadClientTest { false /* getNonConfigInstance */, "test"); } - private void changeVisibility(ActivityClientRecord r, boolean show) { - mThread.handleWindowVisibility(r.token, show); - } - private ActivityClientRecord stubActivityRecord() { ComponentName component = new ComponentName( InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class); diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd index cc01a31224bc..543504764ee3 100644 --- a/core/xsd/permission.xsd +++ b/core/xsd/permission.xsd @@ -46,6 +46,7 @@ <xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app"/> <xs:element name="allow-association" type="allow-association"/> <xs:element name="bugreport-whitelisted" type="bugreport-whitelisted"/> + <xs:element name="app-data-isolation-whitelisted-app" type="app-data-isolation-whitelisted-app"/> </xs:choice> </xs:complexType> </xs:element> @@ -161,6 +162,9 @@ <xs:attribute name="target" type="xs:string"/> <xs:attribute name="allowed" type="xs:string"/> </xs:complexType> + <xs:complexType name="app-data-isolation-whitelisted-app"> + <xs:attribute name="package" type="xs:string"/> + </xs:complexType> <xs:complexType name="bugreport-whitelisted"> <xs:attribute name="package" type="xs:string"/> </xs:complexType> diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt index 771c1dffb909..c36c422a852d 100644 --- a/core/xsd/schema/current.txt +++ b/core/xsd/schema/current.txt @@ -45,6 +45,12 @@ package com.android.xml.permission.configfile { method public void set_package(String); } + public class AppDataIsolationWhitelistedApp { + ctor public AppDataIsolationWhitelistedApp(); + method public String get_package(); + method public void set_package(String); + } + public class AppLink { ctor public AppLink(); method public String get_package(); @@ -160,6 +166,7 @@ package com.android.xml.permission.configfile { method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSaveExceptIdle> getAllowInPowerSaveExceptIdle_optional(); method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSave> getAllowInPowerSave_optional(); method public java.util.List<com.android.xml.permission.configfile.AllowUnthrottledLocation> getAllowUnthrottledLocation_optional(); + method public java.util.List<com.android.xml.permission.configfile.AppDataIsolationWhitelistedApp> getAppDataIsolationWhitelistedApp_optional(); method public java.util.List<com.android.xml.permission.configfile.AppLink> getAppLink_optional(); method public java.util.List<com.android.xml.permission.configfile.AssignPermission> getAssignPermission_optional(); method public java.util.List<com.android.xml.permission.configfile.BackupTransportWhitelistedService> getBackupTransportWhitelistedService_optional(); diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index eb1d1ab1089c..9930ea262b32 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -243,6 +243,7 @@ applications that come with the platform <permission name="android.permission.MANAGE_USB"/> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> + <permission name="android.permission.TETHER_PRIVILEGED"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> </privapp-permissions> diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index 6e7f286d19a7..bee8d5efc933 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -21,7 +21,7 @@ import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Canvas.VertexMode; import android.graphics.text.MeasuredText; import android.text.GraphicsOperations; diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index d900a42b1e66..ac094ba5d5d2 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -21,8 +21,8 @@ import android.annotation.ColorInt; import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.ResourcesImpl; import android.hardware.HardwareBuffer; import android.os.Build; diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 5623a8a49b35..bad487b47682 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -20,7 +20,7 @@ import static android.graphics.BitmapFactory.Options.validate; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.Trace; diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java index 629d8c131b68..34eba97819aa 100644 --- a/graphics/java/android/graphics/BitmapRegionDecoder.java +++ b/graphics/java/android/graphics/BitmapRegionDecoder.java @@ -15,7 +15,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.os.Build; diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java index 198d1e7bc956..edf53c491311 100644 --- a/graphics/java/android/graphics/BitmapShader.java +++ b/graphics/java/android/graphics/BitmapShader.java @@ -17,7 +17,7 @@ package android.graphics; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * Shader used to draw a bitmap as a texture. The bitmap can be repeated or diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java index cbd4eadca30a..80a3740d2f4e 100644 --- a/graphics/java/android/graphics/Camera.java +++ b/graphics/java/android/graphics/Camera.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * A camera instance can be used to compute 3D transformations and diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index a815f20293c5..9a0ca3e4ad9b 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -22,7 +22,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.text.MeasuredText; import android.os.Build; diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java index 1275e0827580..4263772c1c2c 100644 --- a/graphics/java/android/graphics/CanvasProperty.java +++ b/graphics/java/android/graphics/CanvasProperty.java @@ -16,7 +16,8 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import com.android.internal.util.VirtualRefBasePtr; /** diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java index 0f7980cc32e4..a8b18a9fcb1f 100644 --- a/graphics/java/android/graphics/ColorMatrixColorFilter.java +++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java @@ -18,7 +18,7 @@ package android.graphics; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * A color filter that transforms colors through a 4x5 color matrix. This filter diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index 5ad93f411393..ae90995573dc 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -17,7 +17,7 @@ package android.graphics; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.graphics.fonts.FontVariationAxis; import android.text.TextUtils; diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 21cc3757a40e..c146bbd4441b 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontVariationAxis; import android.text.FontConfig; import android.util.Xml; diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java index 3b1fc70397ea..99fa5eef7bbd 100644 --- a/graphics/java/android/graphics/GraphicBuffer.java +++ b/graphics/java/android/graphics/GraphicBuffer.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index bcb313e1c227..83432c362672 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -28,8 +28,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; import android.annotation.WorkerThread; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java index 62a890ff4f0b..221dfa192795 100644 --- a/graphics/java/android/graphics/LightingColorFilter.java +++ b/graphics/java/android/graphics/LightingColorFilter.java @@ -22,7 +22,7 @@ package android.graphics; import android.annotation.ColorInt; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * A color filter that can be used to simulate simple lighting effects. diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java index 12e63c09d76b..3f3ad967fe97 100644 --- a/graphics/java/android/graphics/LinearGradient.java +++ b/graphics/java/android/graphics/LinearGradient.java @@ -20,7 +20,7 @@ import android.annotation.ColorInt; import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; public class LinearGradient extends Shader { diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java index 22b6401fdc2e..cf914c2c3eae 100644 --- a/graphics/java/android/graphics/Matrix.java +++ b/graphics/java/android/graphics/Matrix.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java index 6f030ffac2df..4b3924f0d55f 100644 --- a/graphics/java/android/graphics/Movie.java +++ b/graphics/java/android/graphics/Movie.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.os.Build; diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java index c4c1eaceb4fc..ff3239348240 100644 --- a/graphics/java/android/graphics/NinePatch.java +++ b/graphics/java/android/graphics/NinePatch.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * The NinePatch class permits drawing a bitmap in nine or more sections. diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java index 1fc056c3652f..91a60c327bf0 100644 --- a/graphics/java/android/graphics/Outline.java +++ b/graphics/java/android/graphics/Outline.java @@ -19,7 +19,7 @@ package android.graphics; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.drawable.Drawable; import java.lang.annotation.Retention; diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 109d8631284d..3b586242e5b1 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -24,7 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.Size; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontVariationAxis; import android.os.Build; import android.os.LocaleList; diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index 7282d52d6e23..1362fd864d29 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -20,7 +20,7 @@ import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index 8d12cbffc793..390d3d414346 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -17,7 +17,7 @@ package android.graphics; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.io.InputStream; import java.io.OutputStream; diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java index bc1f66fdd5c0..1275cb9ca4f9 100644 --- a/graphics/java/android/graphics/PorterDuff.java +++ b/graphics/java/android/graphics/PorterDuff.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * <p>This class contains the list of alpha compositing and blending modes diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java index cc2d3a8969fc..50ecb62e7fcc 100644 --- a/graphics/java/android/graphics/PorterDuffColorFilter.java +++ b/graphics/java/android/graphics/PorterDuffColorFilter.java @@ -18,7 +18,7 @@ package android.graphics; import android.annotation.ColorInt; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * A color filter that can be used to tint the source pixels using a single diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java index acbe3da75247..96b7b9a78ba8 100644 --- a/graphics/java/android/graphics/RadialGradient.java +++ b/graphics/java/android/graphics/RadialGradient.java @@ -20,7 +20,7 @@ import android.annotation.ColorInt; import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; public class RadialGradient extends Shader { @UnsupportedAppUsage diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 9e1946c557d2..081b851d1333 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -19,7 +19,7 @@ package android.graphics; import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java index ec7f7a05b685..d8d96413a93d 100644 --- a/graphics/java/android/graphics/Region.java +++ b/graphics/java/android/graphics/Region.java @@ -17,7 +17,7 @@ package android.graphics; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.util.Pools.SynchronizedPool; diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java index 3050d1dae5e4..5335aa4725ad 100644 --- a/graphics/java/android/graphics/Shader.java +++ b/graphics/java/android/graphics/Shader.java @@ -20,7 +20,7 @@ import android.annotation.ColorInt; import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import libcore.util.NativeAllocationRegistry; diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index 99f440d599cb..697daa8b7b70 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -17,7 +17,7 @@ package android.graphics; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.Looper; import android.os.Message; diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java index 667f45afe500..08520048b787 100644 --- a/graphics/java/android/graphics/SweepGradient.java +++ b/graphics/java/android/graphics/SweepGradient.java @@ -20,7 +20,7 @@ import android.annotation.ColorInt; import android.annotation.ColorLong; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; public class SweepGradient extends Shader { @UnsupportedAppUsage diff --git a/graphics/java/android/graphics/TableMaskFilter.java b/graphics/java/android/graphics/TableMaskFilter.java index d81c491e07e0..204f9705852a 100644 --- a/graphics/java/android/graphics/TableMaskFilter.java +++ b/graphics/java/android/graphics/TableMaskFilter.java @@ -16,7 +16,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java index 0ae2c703c21c..ef3f7f704e0d 100644 --- a/graphics/java/android/graphics/TemporaryBuffer.java +++ b/graphics/java/android/graphics/TemporaryBuffer.java @@ -16,7 +16,8 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import com.android.internal.util.ArrayUtils; /** diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 6d20ec32cdc4..a2dd9a8322b6 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -25,7 +25,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.graphics.fonts.Font; import android.graphics.fonts.FontFamily; diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java index 6f4adfde7ff9..e79fb76d806e 100644 --- a/graphics/java/android/graphics/Xfermode.java +++ b/graphics/java/android/graphics/Xfermode.java @@ -21,7 +21,7 @@ package android.graphics; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * Xfermode is the base class for objects that are called to implement custom diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index 82f587086428..d8946009483c 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -19,7 +19,7 @@ package android.graphics.drawable; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.content.res.Resources.Theme; diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java index b29fd4db5803..686f146e9c18 100644 --- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -18,23 +18,23 @@ package android.graphics.drawable; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; -import android.graphics.Canvas; -import android.graphics.Rect; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; -import android.content.res.TypedArray; import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.os.SystemClock; import android.util.AttributeSet; import android.util.TypedValue; -import android.os.SystemClock; + +import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; -import com.android.internal.R; - /** * @hide */ diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java index 11a46c4ba9b9..06159d8a0558 100644 --- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java @@ -20,7 +20,7 @@ import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 66947da9166f..1acf6c512fbd 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -25,9 +25,9 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.app.Application; +import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo.Config; import android.content.res.ColorStateList; import android.content.res.Resources; diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java index 57764c2cb693..8c3fa441cbb0 100644 --- a/graphics/java/android/graphics/drawable/AnimationDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -16,21 +16,21 @@ package android.graphics.drawable; -import com.android.internal.R; - -import java.io.IOException; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; -import android.content.res.TypedArray; import android.content.res.Resources.Theme; +import android.content.res.TypedArray; import android.os.SystemClock; import android.util.AttributeSet; +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + /** * An object used to create frame-by-frame animations, defined by a series of * Drawable objects, which can be used as a View object's background. diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index e4aa774fd434..4e768c9eddfb 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -17,7 +17,7 @@ package android.graphics.drawable; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo.Config; import android.content.res.ColorStateList; import android.content.res.Resources; diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java index 31fdb025bbc5..69ed9b423d48 100644 --- a/graphics/java/android/graphics/drawable/ClipDrawable.java +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -16,20 +16,22 @@ package android.graphics.drawable; -import com.android.internal.R; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; -import android.content.res.TypedArray; import android.content.res.Resources.Theme; -import android.graphics.*; -import android.view.Gravity; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.graphics.Rect; import android.util.AttributeSet; +import android.view.Gravity; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index e53f3db08538..6d4a0c6421d9 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -52,8 +52,8 @@ void DeviceInfo::setMaxTextureSize(int maxTextureSize) { DeviceInfo::get()->mMaxTextureSize = maxTextureSize; } -void DeviceInfo::onDisplayConfigChanged() { - updateDisplayInfo(); +void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) { + mVsyncPeriod = vsyncPeriod; } void DeviceInfo::updateDisplayInfo() { @@ -113,10 +113,11 @@ void DeviceInfo::updateDisplayInfo() { ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig); LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status); + mWidth = ADisplayConfig_getWidth(mCurrentConfig); mHeight = ADisplayConfig_getHeight(mCurrentConfig); mDensity = ADisplayConfig_getDensity(mCurrentConfig); - mRefreshRate = ADisplayConfig_getFps(mCurrentConfig); + mVsyncPeriod = static_cast<int64_t>(1000000000 / ADisplayConfig_getFps(mCurrentConfig)); mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig); mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig); } diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index a4207460883e..16a22f4706f5 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -37,7 +37,7 @@ public: static int32_t getWidth() { return get()->mWidth; } static int32_t getHeight() { return get()->mHeight; } static float getDensity() { return get()->mDensity; } - static float getRefreshRate() { return get()->mRefreshRate; } + static int64_t getVsyncPeriod() { return get()->mVsyncPeriod; } static int64_t getCompositorOffset() { return get()->mCompositorOffset; } static int64_t getAppOffset() { return get()->mAppOffset; } @@ -47,7 +47,8 @@ public: sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; } SkColorType getWideColorType() const { return mWideColorType; } - void onDisplayConfigChanged(); + // This method should be called whenever the display refresh rate changes. + void onRefreshRateChanged(int64_t vsyncPeriod); private: friend class renderthread::RenderThread; @@ -68,7 +69,7 @@ private: int32_t mWidth = 1080; int32_t mHeight = 1920; float mDensity = 2.0; - float mRefreshRate = 60.0; + int64_t mVsyncPeriod = 16666666; int64_t mCompositorOffset = 0; int64_t mAppOffset = 0; }; diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 10e7160e7069..d25fc4b0b03e 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -81,7 +81,7 @@ static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync; JankTracker::JankTracker(ProfileDataContainer* globalData) { mGlobalData = globalData; - nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / DeviceInfo::getRefreshRate()); + nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod(); nsecs_t sfOffset = DeviceInfo::getCompositorOffset(); nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset(); // There are two different offset cases. If the offsetDelta is positive diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index f4149b9b4d7d..84549e8ce6e4 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -152,7 +152,9 @@ sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp<SkColorS AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace); - const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride; + // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer) + const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width; + const size_t rowBytes = info.bytesPerPixel() * bufferStride; return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette)); } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 11dc013af6bc..35a885f46919 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -457,7 +457,10 @@ void SkiaPipeline::renderFrameImpl(const SkRect& clip, const Rect& contentDrawBounds, SkCanvas* canvas, const SkMatrix& preTransform) { SkAutoCanvasRestore saver(canvas, true); - canvas->androidFramework_setDeviceClipRestriction(preTransform.mapRect(clip).roundOut()); + auto clipRestriction = preTransform.mapRect(clip).roundOut(); + canvas->androidFramework_setDeviceClipRestriction(clipRestriction); + canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction", + nullptr); canvas->concat(preTransform); // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index a446858ca565..d78f641d45b9 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -34,7 +34,6 @@ #include <GrContextOptions.h> #include <gl/GrGLInterface.h> -#include <gui/DisplayEventReceiver.h> #include <sys/resource.h> #include <utils/Condition.h> #include <utils/Log.h> @@ -45,53 +44,43 @@ namespace android { namespace uirenderer { namespace renderthread { -// Number of events to read at a time from the DisplayEventReceiver pipe. -// The value should be large enough that we can quickly drain the pipe -// using just a few large reads. -static const size_t EVENT_BUFFER_SIZE = 100; - static bool gHasRenderThreadInstance = false; static JVMAttachHook gOnStartHook = nullptr; -class DisplayEventReceiverWrapper : public VsyncSource { +void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) { + RenderThread* rt = reinterpret_cast<RenderThread*>(data); + rt->mVsyncRequested = false; + if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) { + ATRACE_NAME("queue mFrameCallbackTask"); + rt->mFrameCallbackTaskPending = true; + nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay); + rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); }); + } +} + +void RenderThread::refreshRateCallback(int64_t vsyncPeriod, void* data) { + ATRACE_NAME("refreshRateCallback"); + RenderThread* rt = reinterpret_cast<RenderThread*>(data); + DeviceInfo::get()->onRefreshRateChanged(vsyncPeriod); + rt->setupFrameInterval(); +} + +class ChoreographerSource : public VsyncSource { public: - DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver, - const std::function<void()>& onDisplayConfigChanged) - : mDisplayEventReceiver(std::move(receiver)) - , mOnDisplayConfigChanged(onDisplayConfigChanged) {} + ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {} virtual void requestNextVsync() override { - status_t status = mDisplayEventReceiver->requestNextVsync(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status); + AChoreographer_postFrameCallback64(mRenderThread->mChoreographer, + RenderThread::frameCallback, mRenderThread); } - virtual nsecs_t latestVsyncEvent() override { - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - nsecs_t latest = 0; - ssize_t n; - while ((n = mDisplayEventReceiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - for (ssize_t i = 0; i < n; i++) { - const DisplayEventReceiver::Event& ev = buf[i]; - switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - latest = ev.header.timestamp; - break; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - mOnDisplayConfigChanged(); - break; - } - } - } - if (n < 0) { - ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); - } - return latest; + virtual void drainPendingEvents() override { + AChoreographer_handlePendingEvents(mRenderThread->mChoreographer, mRenderThread); } private: - std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver; - std::function<void()> mOnDisplayConfigChanged; + RenderThread* mRenderThread; }; class DummyVsyncSource : public VsyncSource { @@ -99,11 +88,14 @@ public: DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {} virtual void requestNextVsync() override { - mRenderThread->queue().postDelayed(16_ms, - [this]() { mRenderThread->drainDisplayEventQueue(); }); + mRenderThread->queue().postDelayed(16_ms, [this]() { + RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread); + }); } - virtual nsecs_t latestVsyncEvent() override { return systemTime(SYSTEM_TIME_MONOTONIC); } + virtual void drainPendingEvents() override { + RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread); + } private: RenderThread* mRenderThread; @@ -145,29 +137,24 @@ RenderThread::RenderThread() } RenderThread::~RenderThread() { + // Note that if this fatal assertion is removed then member variables must + // be properly destroyed. LOG_ALWAYS_FATAL("Can't destroy the render thread"); } -void RenderThread::initializeDisplayEventReceiver() { - LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?"); +void RenderThread::initializeChoreographer() { + LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?"); if (!Properties::isolatedProcess) { - auto receiver = std::make_unique<DisplayEventReceiver>( - ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::eConfigChangedDispatch); - status_t status = receiver->initCheck(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, - "Initialization of DisplayEventReceiver " - "failed with status: %d", - status); + mChoreographer = AChoreographer_create(); + LOG_ALWAYS_FATAL_IF(mChoreographer == nullptr, "Initialization of Choreographer failed"); + AChoreographer_registerRefreshRateCallback(mChoreographer, + RenderThread::refreshRateCallback, this); // Register the FD - mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT, - RenderThread::displayEventReceiverCallback, this); - mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] { - DeviceInfo::get()->onDisplayConfigChanged(); - setupFrameInterval(); - }); + mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT, + RenderThread::choreographerCallback, this); + mVsyncSource = new ChoreographerSource(this); } else { mVsyncSource = new DummyVsyncSource(this); } @@ -175,7 +162,7 @@ void RenderThread::initializeDisplayEventReceiver() { void RenderThread::initThreadLocals() { setupFrameInterval(); - initializeDisplayEventReceiver(); + initializeChoreographer(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); mVkManager = new VulkanManager(); @@ -183,7 +170,7 @@ void RenderThread::initThreadLocals() { } void RenderThread::setupFrameInterval() { - nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / DeviceInfo::getRefreshRate()); + nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod(); mTimeLord.setFrameInterval(frameIntervalNanos); mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f); } @@ -288,7 +275,7 @@ void RenderThread::setGrContext(sk_sp<GrContext> context) { } } -int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { +int RenderThread::choreographerCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " "events=0x%x", @@ -302,24 +289,10 @@ int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { events); return 1; // keep the callback } + RenderThread* rt = reinterpret_cast<RenderThread*>(data); + AChoreographer_handlePendingEvents(rt->mChoreographer, data); - reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue(); - - return 1; // keep the callback -} - -void RenderThread::drainDisplayEventQueue() { - ATRACE_CALL(); - nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent(); - if (vsyncEvent > 0) { - mVsyncRequested = false; - if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) { - ATRACE_NAME("queue mFrameCallbackTask"); - mFrameCallbackTaskPending = true; - nsecs_t runAt = (vsyncEvent + mDispatchFrameDelay); - queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); }); - } - } + return 1; } void RenderThread::dispatchFrameCallbacks() { @@ -360,7 +333,7 @@ bool RenderThread::threadLoop() { processQueue(); if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) { - drainDisplayEventQueue(); + mVsyncSource->drainPendingEvents(); mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end()); mPendingRegistrationFrameCallbacks.clear(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index da79e97a6ceb..8be46a6d16e1 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -19,6 +19,7 @@ #include <GrContext.h> #include <SkBitmap.h> +#include <apex/choreographer.h> #include <cutils/compiler.h> #include <thread/ThreadBase.h> #include <utils/Looper.h> @@ -73,10 +74,11 @@ protected: struct VsyncSource { virtual void requestNextVsync() = 0; - virtual nsecs_t latestVsyncEvent() = 0; + virtual void drainPendingEvents() = 0; virtual ~VsyncSource() {} }; +class ChoreographerSource; class DummyVsyncSource; typedef void (*JVMAttachHook)(const char* name); @@ -136,6 +138,7 @@ private: friend class DispatchFrameCallbacks; friend class RenderProxy; friend class DummyVsyncSource; + friend class ChoreographerSource; friend class android::uirenderer::AutoBackendTextureRelease; friend class android::uirenderer::TestUtils; friend class android::uirenderer::WebViewFunctor; @@ -149,13 +152,21 @@ private: static RenderThread& getInstance(); void initThreadLocals(); - void initializeDisplayEventReceiver(); + void initializeChoreographer(); void setupFrameInterval(); - static int displayEventReceiverCallback(int fd, int events, void* data); + // Callbacks for choreographer events: + // choreographerCallback will call AChoreograper_handleEvent to call the + // corresponding callbacks for each display event type + static int choreographerCallback(int fd, int events, void* data); + // Callback that will be run on vsync ticks. + static void frameCallback(int64_t frameTimeNanos, void* data); + // Callback that will be run whenver there is a refresh rate change. + static void refreshRateCallback(int64_t vsyncPeriod, void* data); void drainDisplayEventQueue(); void dispatchFrameCallbacks(); void requestVsync(); + AChoreographer* mChoreographer; VsyncSource* mVsyncSource; bool mVsyncRequested; std::set<IFrameCallback*> mFrameCallbacks; diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java index 1370b1095ae1..26f73f784879 100644 --- a/location/java/android/location/Criteria.java +++ b/location/java/android/location/Criteria.java @@ -16,9 +16,16 @@ package android.location; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A class indicating the application criteria for selecting a * location provider. Providers may be ordered according to accuracy, @@ -26,6 +33,25 @@ import android.os.Parcelable; * cost. */ public class Criteria implements Parcelable { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, POWER_HIGH}) + public @interface PowerRequirement { + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({NO_REQUIREMENT, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH}) + public @interface AccuracyRequirement { + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({NO_REQUIREMENT, ACCURACY_FINE, ACCURACY_COARSE}) + public @interface LocationAccuracyRequirement { + } + /** * A constant indicating that the application does not choose to * place requirement on a particular feature. @@ -81,15 +107,15 @@ public class Criteria implements Parcelable { */ public static final int ACCURACY_HIGH = 3; - private int mHorizontalAccuracy = NO_REQUIREMENT; - private int mVerticalAccuracy = NO_REQUIREMENT; - private int mSpeedAccuracy = NO_REQUIREMENT; - private int mBearingAccuracy = NO_REQUIREMENT; - private int mPowerRequirement = NO_REQUIREMENT; - private boolean mAltitudeRequired = false; - private boolean mBearingRequired = false; - private boolean mSpeedRequired = false; - private boolean mCostAllowed = false; + private int mHorizontalAccuracy = NO_REQUIREMENT; + private int mVerticalAccuracy = NO_REQUIREMENT; + private int mSpeedAccuracy = NO_REQUIREMENT; + private int mBearingAccuracy = NO_REQUIREMENT; + private int mPowerRequirement = NO_REQUIREMENT; + private boolean mAltitudeRequired = false; + private boolean mBearingRequired = false; + private boolean mSpeedRequired = false; + private boolean mCostAllowed = false; /** * Constructs a new Criteria object. The new object will have no @@ -97,7 +123,8 @@ public class Criteria implements Parcelable { * require altitude, speed, or bearing; and will not allow monetary * cost. */ - public Criteria() {} + public Criteria() { + } /** * Constructs a new Criteria object that is a copy of the given criteria. @@ -115,125 +142,121 @@ public class Criteria implements Parcelable { } /** - * Indicates the desired horizontal accuracy (latitude and longitude). - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM}, - * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}. - * More accurate location may consume more power and may take longer. + * Indicates the desired horizontal accuracy (latitude and longitude). Accuracy may be + * {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH} or + * {@link #NO_REQUIREMENT}. More accurate location may consume more power and may take longer. * * @throws IllegalArgumentException if accuracy is not one of the supported constants */ - public void setHorizontalAccuracy(int accuracy) { - if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) { - throw new IllegalArgumentException("accuracy=" + accuracy); - } - mHorizontalAccuracy = accuracy; + public void setHorizontalAccuracy(@AccuracyRequirement int accuracy) { + mHorizontalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, + ACCURACY_HIGH, "accuracy"); } /** * Returns a constant indicating the desired horizontal accuracy (latitude and longitude). - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM}, - * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}. + * + * @see #setHorizontalAccuracy(int) */ + @AccuracyRequirement public int getHorizontalAccuracy() { return mHorizontalAccuracy; } /** - * Indicates the desired vertical accuracy (altitude). - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM}, - * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}. - * More accurate location may consume more power and may take longer. + * Indicates the desired vertical accuracy (altitude). Accuracy may be {@link #ACCURACY_LOW}, + * {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}. More accurate + * location may consume more power and may take longer. * * @throws IllegalArgumentException if accuracy is not one of the supported constants */ - public void setVerticalAccuracy(int accuracy) { - if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) { - throw new IllegalArgumentException("accuracy=" + accuracy); - } - mVerticalAccuracy = accuracy; + public void setVerticalAccuracy(@AccuracyRequirement int accuracy) { + mVerticalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, + ACCURACY_HIGH, "accuracy"); } /** * Returns a constant indicating the desired vertical accuracy (altitude). - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH}, - * or {@link #NO_REQUIREMENT}. + * + * @see #setVerticalAccuracy(int) */ + @AccuracyRequirement public int getVerticalAccuracy() { return mVerticalAccuracy; } /** - * Indicates the desired speed accuracy. - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH}, - * or {@link #NO_REQUIREMENT}. - * More accurate location may consume more power and may take longer. + * Indicates the desired speed accuracy. Accuracy may be {@link #ACCURACY_LOW}, + * {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH}, or {@link #NO_REQUIREMENT}. More accurate + * location may consume more power and may take longer. * * @throws IllegalArgumentException if accuracy is not one of the supported constants */ - public void setSpeedAccuracy(int accuracy) { - if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) { - throw new IllegalArgumentException("accuracy=" + accuracy); - } - mSpeedAccuracy = accuracy; + public void setSpeedAccuracy(@AccuracyRequirement int accuracy) { + mSpeedAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_HIGH, + "accuracy"); } /** - * Returns a constant indicating the desired speed accuracy - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH}, - * or {@link #NO_REQUIREMENT}. + * Returns a constant indicating the desired speed accuracy. + * + * @see #setSpeedAccuracy(int) */ + @AccuracyRequirement public int getSpeedAccuracy() { return mSpeedAccuracy; } /** - * Indicates the desired bearing accuracy. - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH}, - * or {@link #NO_REQUIREMENT}. - * More accurate location may consume more power and may take longer. + * Indicates the desired bearing accuracy. Accuracy may be {@link #ACCURACY_LOW}, + * {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH}, or {@link #NO_REQUIREMENT}. More accurate + * location may consume more power and may take longer. * * @throws IllegalArgumentException if accuracy is not one of the supported constants */ - public void setBearingAccuracy(int accuracy) { - if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) { - throw new IllegalArgumentException("accuracy=" + accuracy); - } - mBearingAccuracy = accuracy; + public void setBearingAccuracy(@AccuracyRequirement int accuracy) { + mBearingAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, + ACCURACY_HIGH, "accuracy"); } /** * Returns a constant indicating the desired bearing accuracy. - * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH}, - * or {@link #NO_REQUIREMENT}. + * + * @see #setBearingAccuracy(int) */ + @AccuracyRequirement public int getBearingAccuracy() { return mBearingAccuracy; } /** - * Indicates the desired accuracy for latitude and longitude. Accuracy - * may be {@link #ACCURACY_FINE} if desired location - * is fine, else it can be {@link #ACCURACY_COARSE}. - * More accurate location may consume more power and may take longer. + * Indicates the desired accuracy for latitude and longitude. Accuracy may be + * {@link #ACCURACY_FINE} or {@link #ACCURACY_COARSE}. More accurate location may consume more + * power and may take longer. * * @throws IllegalArgumentException if accuracy is not one of the supported constants */ - public void setAccuracy(int accuracy) { - if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_COARSE) { - throw new IllegalArgumentException("accuracy=" + accuracy); - } - if (accuracy == ACCURACY_FINE) { - mHorizontalAccuracy = ACCURACY_HIGH; - } else { - mHorizontalAccuracy = ACCURACY_LOW; + public void setAccuracy(@LocationAccuracyRequirement int accuracy) { + Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_COARSE, "accuracy"); + switch (accuracy) { + case NO_REQUIREMENT: + setHorizontalAccuracy(NO_REQUIREMENT); + break; + case ACCURACY_FINE: + setHorizontalAccuracy(ACCURACY_HIGH); + break; + case ACCURACY_COARSE: + setHorizontalAccuracy(ACCURACY_LOW); + break; } } /** - * Returns a constant indicating desired accuracy of location - * Accuracy may be {@link #ACCURACY_FINE} if desired location - * is fine, else it can be {@link #ACCURACY_COARSE}. + * Returns a constant indicating desired accuracy of location. + * + * @see #setAccuracy(int) */ + @LocationAccuracyRequirement public int getAccuracy() { if (mHorizontalAccuracy >= ACCURACY_HIGH) { return ACCURACY_FINE; @@ -243,21 +266,20 @@ public class Criteria implements Parcelable { } /** - * Indicates the desired maximum power level. The level parameter - * must be one of NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, or - * POWER_HIGH. + * Indicates the desired maximum power requirement. The power requirement parameter may be + * {@link #NO_REQUIREMENT}, {@link #POWER_LOW}, {@link #POWER_MEDIUM}, or {@link #POWER_HIGH}. */ - public void setPowerRequirement(int level) { - if (level < NO_REQUIREMENT || level > POWER_HIGH) { - throw new IllegalArgumentException("level=" + level); - } - mPowerRequirement = level; + public void setPowerRequirement(@PowerRequirement int powerRequirement) { + mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, NO_REQUIREMENT, + POWER_HIGH, "powerRequirement"); } /** - * Returns a constant indicating the desired power requirement. The - * returned + * Returns a constant indicating the desired maximum power requirement. + * + * @see #setPowerRequirement(int) */ + @PowerRequirement public int getPowerRequirement() { return mPowerRequirement; } @@ -277,8 +299,8 @@ public class Criteria implements Parcelable { } /** - * Indicates whether the provider must provide altitude information. - * Not all fixes are guaranteed to contain such information. + * Indicates whether the provider must provide altitude information. Not all fixes are + * guaranteed to contain such information. */ public void setAltitudeRequired(boolean altitudeRequired) { mAltitudeRequired = altitudeRequired; @@ -286,15 +308,16 @@ public class Criteria implements Parcelable { /** * Returns whether the provider must provide altitude information. - * Not all fixes are guaranteed to contain such information. + * + * @see #setAltitudeRequired(boolean) */ public boolean isAltitudeRequired() { return mAltitudeRequired; } /** - * Indicates whether the provider must provide speed information. - * Not all fixes are guaranteed to contain such information. + * Indicates whether the provider must provide speed information. Not all fixes are guaranteed + * to contain such information. */ public void setSpeedRequired(boolean speedRequired) { mSpeedRequired = speedRequired; @@ -302,15 +325,16 @@ public class Criteria implements Parcelable { /** * Returns whether the provider must provide speed information. - * Not all fixes are guaranteed to contain such information. + * + * @see #setSpeedRequired(boolean) */ public boolean isSpeedRequired() { return mSpeedRequired; } /** - * Indicates whether the provider must provide bearing information. - * Not all fixes are guaranteed to contain such information. + * Indicates whether the provider must provide bearing information. Not all fixes are guaranteed + * to contain such information. */ public void setBearingRequired(boolean bearingRequired) { mBearingRequired = bearingRequired; @@ -318,34 +342,36 @@ public class Criteria implements Parcelable { /** * Returns whether the provider must provide bearing information. - * Not all fixes are guaranteed to contain such information. + * + * @see #setBearingRequired(boolean) */ public boolean isBearingRequired() { return mBearingRequired; } - public static final @android.annotation.NonNull Parcelable.Creator<Criteria> CREATOR = - new Parcelable.Creator<Criteria>() { - @Override - public Criteria createFromParcel(Parcel in) { - Criteria c = new Criteria(); - c.mHorizontalAccuracy = in.readInt(); - c.mVerticalAccuracy = in.readInt(); - c.mSpeedAccuracy = in.readInt(); - c.mBearingAccuracy = in.readInt(); - c.mPowerRequirement = in.readInt(); - c.mAltitudeRequired = in.readInt() != 0; - c.mBearingRequired = in.readInt() != 0; - c.mSpeedRequired = in.readInt() != 0; - c.mCostAllowed = in.readInt() != 0; - return c; - } - - @Override - public Criteria[] newArray(int size) { - return new Criteria[size]; - } - }; + @NonNull + public static final Parcelable.Creator<Criteria> CREATOR = + new Parcelable.Creator<Criteria>() { + @Override + public Criteria createFromParcel(Parcel in) { + Criteria c = new Criteria(); + c.mHorizontalAccuracy = in.readInt(); + c.mVerticalAccuracy = in.readInt(); + c.mSpeedAccuracy = in.readInt(); + c.mBearingAccuracy = in.readInt(); + c.mPowerRequirement = in.readInt(); + c.mAltitudeRequired = in.readInt() != 0; + c.mBearingRequired = in.readInt() != 0; + c.mSpeedRequired = in.readInt() != 0; + c.mCostAllowed = in.readInt() != 0; + return c; + } + + @Override + public Criteria[] newArray(int size) { + return new Criteria[size]; + } + }; @Override public int describeContents() { @@ -365,42 +391,57 @@ public class Criteria implements Parcelable { parcel.writeInt(mCostAllowed ? 1 : 0); } - private static String powerToString(int power) { + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + s.append("Criteria["); + s.append("power=").append(requirementToString(mPowerRequirement)).append(", "); + s.append("accuracy=").append(requirementToString(mHorizontalAccuracy)); + if (mVerticalAccuracy != NO_REQUIREMENT) { + s.append(", verticalAccuracy=").append(requirementToString(mVerticalAccuracy)); + } + if (mSpeedAccuracy != NO_REQUIREMENT) { + s.append(", speedAccuracy=").append(requirementToString(mSpeedAccuracy)); + } + if (mBearingAccuracy != NO_REQUIREMENT) { + s.append(", bearingAccuracy=").append(requirementToString(mBearingAccuracy)); + } + if (mAltitudeRequired || mBearingRequired || mSpeedRequired) { + s.append(", required=["); + if (mAltitudeRequired) { + s.append("altitude, "); + } + if (mBearingRequired) { + s.append("bearing, "); + } + if (mSpeedRequired) { + s.append("speed, "); + } + s.setLength(s.length() - 2); + s.append("]"); + } + if (mCostAllowed) { + s.append(", costAllowed"); + } + s.append(']'); + return s.toString(); + } + + private static String requirementToString(int power) { switch (power) { case NO_REQUIREMENT: - return "NO_REQ"; + return "None"; + //case ACCURACY_LOW: case POWER_LOW: - return "LOW"; + return "Low"; + //case ACCURACY_MEDIUM: case POWER_MEDIUM: - return "MEDIUM"; + return "Medium"; + //case ACCURACY_HIGH: case POWER_HIGH: - return "HIGH"; + return "High"; default: return "???"; } } - - private static String accuracyToString(int accuracy) { - switch (accuracy) { - case NO_REQUIREMENT: - return "---"; - case ACCURACY_HIGH: - return "HIGH"; - case ACCURACY_MEDIUM: - return "MEDIUM"; - case ACCURACY_LOW: - return "LOW"; - default: - return "???"; - } - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - s.append("Criteria[power=").append(powerToString(mPowerRequirement)); - s.append(" acc=").append(accuracyToString(mHorizontalAccuracy)); - s.append(']'); - return s.toString(); - } } diff --git a/location/java/com/android/internal/location/ProviderProperties.java b/location/java/com/android/internal/location/ProviderProperties.java index def96f0fb674..68f9ec3c530b 100644 --- a/location/java/com/android/internal/location/ProviderProperties.java +++ b/location/java/com/android/internal/location/ProviderProperties.java @@ -16,15 +16,36 @@ package com.android.internal.location; +import android.annotation.IntDef; +import android.location.Criteria; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A Parcelable containing (legacy) location provider properties. * This object is just used inside the framework and system services. + * * @hide */ public final class ProviderProperties implements Parcelable { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({Criteria.POWER_LOW, Criteria.POWER_MEDIUM, Criteria.POWER_HIGH}) + public @interface PowerRequirement { + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({Criteria.ACCURACY_FINE, Criteria.ACCURACY_COARSE}) + public @interface Accuracy { + } + /** * True if provider requires access to a * data network (e.g., the Internet), false otherwise. @@ -79,58 +100,58 @@ public final class ProviderProperties implements Parcelable { /** * Power requirement for this provider. - * - * @return the power requirement for this provider, as one of the - * constants Criteria.POWER_*. */ + @PowerRequirement public final int mPowerRequirement; /** * Constant describing the horizontal accuracy returned * by this provider. - * - * @return the horizontal accuracy for this provider, as one of the - * constants Criteria.ACCURACY_COARSE or Criteria.ACCURACY_FINE */ + @Accuracy public final int mAccuracy; - public ProviderProperties(boolean mRequiresNetwork, - boolean mRequiresSatellite, boolean mRequiresCell, boolean mHasMonetaryCost, - boolean mSupportsAltitude, boolean mSupportsSpeed, boolean mSupportsBearing, - int mPowerRequirement, int mAccuracy) { - this.mRequiresNetwork = mRequiresNetwork; - this.mRequiresSatellite = mRequiresSatellite; - this.mRequiresCell = mRequiresCell; - this.mHasMonetaryCost = mHasMonetaryCost; - this.mSupportsAltitude = mSupportsAltitude; - this.mSupportsSpeed = mSupportsSpeed; - this.mSupportsBearing = mSupportsBearing; - this.mPowerRequirement = mPowerRequirement; - this.mAccuracy = mAccuracy; + public ProviderProperties(boolean requiresNetwork, boolean requiresSatellite, + boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, + boolean supportsSpeed, boolean supportsBearing, @PowerRequirement int powerRequirement, + @Accuracy int accuracy) { + mRequiresNetwork = requiresNetwork; + mRequiresSatellite = requiresSatellite; + mRequiresCell = requiresCell; + mHasMonetaryCost = hasMonetaryCost; + mSupportsAltitude = supportsAltitude; + mSupportsSpeed = supportsSpeed; + mSupportsBearing = supportsBearing; + mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, Criteria.POWER_LOW, + Criteria.POWER_HIGH, "powerRequirement"); + mAccuracy = Preconditions.checkArgumentInRange(accuracy, Criteria.ACCURACY_FINE, + Criteria.ACCURACY_COARSE, "accuracy"); } public static final Parcelable.Creator<ProviderProperties> CREATOR = new Parcelable.Creator<ProviderProperties>() { - @Override - public ProviderProperties createFromParcel(Parcel in) { - boolean requiresNetwork = in.readInt() == 1; - boolean requiresSatellite = in.readInt() == 1; - boolean requiresCell = in.readInt() == 1; - boolean hasMonetaryCost = in.readInt() == 1; - boolean supportsAltitude = in.readInt() == 1; - boolean supportsSpeed = in.readInt() == 1; - boolean supportsBearing = in.readInt() == 1; - int powerRequirement = in.readInt(); - int accuracy = in.readInt(); - return new ProviderProperties(requiresNetwork, requiresSatellite, - requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, - powerRequirement, accuracy); - } - @Override - public ProviderProperties[] newArray(int size) { - return new ProviderProperties[size]; - } - }; + @Override + public ProviderProperties createFromParcel(Parcel in) { + boolean requiresNetwork = in.readInt() == 1; + boolean requiresSatellite = in.readInt() == 1; + boolean requiresCell = in.readInt() == 1; + boolean hasMonetaryCost = in.readInt() == 1; + boolean supportsAltitude = in.readInt() == 1; + boolean supportsSpeed = in.readInt() == 1; + boolean supportsBearing = in.readInt() == 1; + int powerRequirement = in.readInt(); + int accuracy = in.readInt(); + return new ProviderProperties(requiresNetwork, requiresSatellite, + requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, + supportsBearing, + powerRequirement, accuracy); + } + + @Override + public ProviderProperties[] newArray(int size) { + return new ProviderProperties[size]; + } + }; @Override public int describeContents() { @@ -149,4 +170,67 @@ public final class ProviderProperties implements Parcelable { parcel.writeInt(mPowerRequirement); parcel.writeInt(mAccuracy); } + + @Override + public String toString() { + StringBuilder b = new StringBuilder("ProviderProperties["); + b.append("power=").append(powerToString(mPowerRequirement)).append(", "); + b.append("accuracy=").append(accuracyToString(mAccuracy)); + if (mRequiresNetwork || mRequiresSatellite || mRequiresCell) { + b.append(", requires="); + if (mRequiresNetwork) { + b.append("network,"); + } + if (mRequiresSatellite) { + b.append("satellite,"); + } + if (mRequiresCell) { + b.append("cell,"); + } + b.setLength(b.length() - 1); + } + if (mHasMonetaryCost) { + b.append(", hasMonetaryCost"); + } + if (mSupportsBearing || mSupportsSpeed || mSupportsAltitude) { + b.append(", supports=["); + if (mSupportsBearing) { + b.append("bearing, "); + } + if (mSupportsSpeed) { + b.append("speed, "); + } + if (mSupportsAltitude) { + b.append("altitude, "); + } + b.setLength(b.length() - 2); + b.append("]"); + } + b.append("]"); + return b.toString(); + } + + private static String powerToString(@PowerRequirement int power) { + switch (power) { + case Criteria.POWER_LOW: + return "Low"; + case Criteria.POWER_MEDIUM: + return "Medium"; + case Criteria.POWER_HIGH: + return "High"; + default: + return "???"; + } + } + + private static String accuracyToString(@Accuracy int accuracy) { + switch (accuracy) { + case Criteria.ACCURACY_COARSE: + return "Coarse"; + case Criteria.ACCURACY_FINE: + return "Fine"; + default: + return "???"; + } + } } diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java index c23f49976799..8d8df4533ebe 100644 --- a/location/java/com/android/internal/location/ProviderRequest.java +++ b/location/java/com/android/internal/location/ProviderRequest.java @@ -20,33 +20,42 @@ import android.compat.annotation.UnsupportedAppUsage; import android.location.LocationRequest; import android.os.Parcel; import android.os.Parcelable; +import android.os.WorkSource; import android.util.TimeUtils; +import com.android.internal.util.Preconditions; + import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @hide */ public final class ProviderRequest implements Parcelable { + + public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(false, Long.MAX_VALUE, + false, false, + Collections.emptyList(), new WorkSource()); + /** Location reporting is requested (true) */ @UnsupportedAppUsage - public boolean reportLocation = false; + public final boolean reportLocation; /** The smallest requested interval */ @UnsupportedAppUsage - public long interval = Long.MAX_VALUE; + public final long interval; /** - * When this flag is true, providers should ignore all location settings, user consents, power - * restrictions or any other restricting factors and always satisfy this request to the best of - * their ability. This flag should only be used in event of an emergency. + * Whether provider shall make stronger than normal tradeoffs to substantially restrict power + * use. */ - public boolean locationSettingsIgnored = false; + public final boolean lowPowerMode; /** - * Whether provider shall make stronger than normal tradeoffs to substantially restrict power - * use. + * When this flag is true, providers should ignore all location settings, user consents, power + * restrictions or any other restricting factors and always satisfy this request to the best of + * their ability. This flag should only be used in event of an emergency. */ - public boolean lowPowerMode = false; + public final boolean locationSettingsIgnored; /** * A more detailed set of requests. @@ -56,26 +65,37 @@ public final class ProviderRequest implements Parcelable { * low power fast interval request. */ @UnsupportedAppUsage - public final List<LocationRequest> locationRequests = new ArrayList<>(); + public final List<LocationRequest> locationRequests; - @UnsupportedAppUsage - public ProviderRequest() { + public final WorkSource workSource; + + private ProviderRequest(boolean reportLocation, long interval, boolean lowPowerMode, + boolean locationSettingsIgnored, List<LocationRequest> locationRequests, + WorkSource workSource) { + this.reportLocation = reportLocation; + this.interval = interval; + this.lowPowerMode = lowPowerMode; + this.locationSettingsIgnored = locationSettingsIgnored; + this.locationRequests = Preconditions.checkNotNull(locationRequests); + this.workSource = Preconditions.checkNotNull(workSource); } public static final Parcelable.Creator<ProviderRequest> CREATOR = new Parcelable.Creator<ProviderRequest>() { @Override public ProviderRequest createFromParcel(Parcel in) { - ProviderRequest request = new ProviderRequest(); - request.reportLocation = in.readInt() == 1; - request.interval = in.readLong(); - request.lowPowerMode = in.readBoolean(); - request.locationSettingsIgnored = in.readBoolean(); + boolean reportLocation = in.readInt() == 1; + long interval = in.readLong(); + boolean lowPowerMode = in.readBoolean(); + boolean locationSettingsIgnored = in.readBoolean(); int count = in.readInt(); + ArrayList<LocationRequest> locationRequests = new ArrayList<>(count); for (int i = 0; i < count; i++) { - request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in)); + locationRequests.add(LocationRequest.CREATOR.createFromParcel(in)); } - return request; + WorkSource workSource = in.readParcelable(null); + return new ProviderRequest(reportLocation, interval, lowPowerMode, + locationSettingsIgnored, locationRequests, workSource); } @Override @@ -106,14 +126,13 @@ public final class ProviderRequest implements Parcelable { StringBuilder s = new StringBuilder(); s.append("ProviderRequest["); if (reportLocation) { - s.append("ON"); - s.append(" interval="); + s.append("interval="); TimeUtils.formatDuration(interval, s); if (lowPowerMode) { - s.append(" lowPowerMode"); + s.append(", lowPowerMode"); } if (locationSettingsIgnored) { - s.append(" locationSettingsIgnored"); + s.append(", locationSettingsIgnored"); } } else { s.append("OFF"); @@ -121,4 +140,67 @@ public final class ProviderRequest implements Parcelable { s.append(']'); return s.toString(); } + + /** + * A Builder for {@link ProviderRequest}s. + */ + public static class Builder { + private long mInterval = Long.MAX_VALUE; + private boolean mLowPowerMode; + private boolean mLocationSettingsIgnored; + private List<LocationRequest> mLocationRequests = Collections.emptyList(); + private WorkSource mWorkSource = new WorkSource(); + + public long getInterval() { + return mInterval; + } + + public void setInterval(long interval) { + this.mInterval = interval; + } + + public boolean isLowPowerMode() { + return mLowPowerMode; + } + + public void setLowPowerMode(boolean lowPowerMode) { + this.mLowPowerMode = lowPowerMode; + } + + public boolean isLocationSettingsIgnored() { + return mLocationSettingsIgnored; + } + + public void setLocationSettingsIgnored(boolean locationSettingsIgnored) { + this.mLocationSettingsIgnored = locationSettingsIgnored; + } + + public List<LocationRequest> getLocationRequests() { + return mLocationRequests; + } + + public void setLocationRequests(List<LocationRequest> locationRequests) { + this.mLocationRequests = Preconditions.checkNotNull(locationRequests); + } + + public WorkSource getWorkSource() { + return mWorkSource; + } + + public void setWorkSource(WorkSource workSource) { + mWorkSource = Preconditions.checkNotNull(workSource); + } + + /** + * Builds a ProviderRequest object with the set information. + */ + public ProviderRequest build() { + if (mInterval == Long.MAX_VALUE) { + return EMPTY_REQUEST; + } else { + return new ProviderRequest(true, mInterval, mLowPowerMode, + mLocationSettingsIgnored, mLocationRequests, mWorkSource); + } + } + } } diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl index 02a381669893..28bf84d6e079 100644 --- a/media/java/android/media/IMediaRoute2Provider.aidl +++ b/media/java/android/media/IMediaRoute2Provider.aidl @@ -24,13 +24,12 @@ import android.media.IMediaRoute2ProviderClient; */ oneway interface IMediaRoute2Provider { void setClient(IMediaRoute2ProviderClient client); - void requestCreateSession(String packageName, String routeId, - String controlCategory, long requestId); - void releaseSession(int sessionId); + void requestCreateSession(String packageName, String routeId, String routeType, long requestId); + void releaseSession(String sessionId); - void selectRoute(int sessionId, String routeId); - void deselectRoute(int sessionId, String routeId); - void transferToRoute(int sessionId, String routeId); + void selectRoute(String sessionId, String routeId); + void deselectRoute(String sessionId, String routeId); + void transferToRoute(String sessionId, String routeId); void notifyControlRequestSent(String id, in Intent request); void requestSetVolume(String id, int volume); diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl index b7cb7059ce3d..e8af21e59cc1 100644 --- a/media/java/android/media/IMediaRouter2Manager.aidl +++ b/media/java/android/media/IMediaRouter2Manager.aidl @@ -24,7 +24,7 @@ import android.media.MediaRoute2Info; */ oneway interface IMediaRouter2Manager { void notifyRouteSelected(String packageName, in MediaRoute2Info route); - void notifyControlCategoriesChanged(String packageName, in List<String> categories); + void notifyRouteTypesChanged(String packageName, in List<String> routeTypes); void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); void notifyRoutesChanged(in List<MediaRoute2Info> routes); diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index e5b62ff10aa6..b573f64da51d 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -22,6 +22,7 @@ import android.media.IMediaRouter2Manager; import android.media.IMediaRouterClient; import android.media.MediaRoute2Info; import android.media.MediaRouterClientState; +import android.media.RouteDiscoveryRequest; import android.media.RouteSessionInfo; /** @@ -51,8 +52,8 @@ interface IMediaRouterService { void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction); void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, - String controlCategory, int requestId); - void setControlCategories(IMediaRouter2Client client, in List<String> categories); + String routeType, int requestId); + void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryRequest request); void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route); void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route); void transferToRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route); diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 7fca03cdfd57..4cd581b6628c 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; @@ -354,8 +355,8 @@ public class MediaMetadataRetriever implements AutoCloseable { * is less than or equal to 0. * @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)} */ - public @Nullable Bitmap getScaledFrameAtTime( - long timeUs, @Option int option, int dstWidth, int dstHeight) { + public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option, + @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight) { validate(option, dstWidth, dstHeight); return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, null); } @@ -400,7 +401,8 @@ public class MediaMetadataRetriever implements AutoCloseable { * @see {@link #getScaledFrameAtTime(long, int, int, int)} */ public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option, - int dstWidth, int dstHeight, @NonNull BitmapParams params) { + @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight, + @NonNull BitmapParams params) { validate(option, dstWidth, dstHeight); return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, params); } diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 506d6165bbfa..1ed53d942b63 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -16,6 +16,8 @@ package android.media; +import static android.media.MediaRouter2Utils.toUniqueId; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -83,12 +85,14 @@ public final class MediaRoute2Info implements Parcelable { * controlled from this object. An example of fixed playback volume is a remote player, * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather * than attenuate at the source. + * * @see #getVolumeHandling() */ public static final int PLAYBACK_VOLUME_FIXED = 0; /** * Playback information indicating the playback volume is variable and can be controlled * from this object. + * * @see #getVolumeHandling() */ public static final int PLAYBACK_VOLUME_VARIABLE = 1; @@ -148,7 +152,7 @@ public final class MediaRoute2Info implements Parcelable { @Nullable final String mClientPackageName; @NonNull - final List<String> mSupportedCategories; + final List<String> mRouteTypes; final int mVolume; final int mVolumeMax; final int mVolumeHandling; @@ -156,8 +160,6 @@ public final class MediaRoute2Info implements Parcelable { @Nullable final Bundle mExtras; - private final String mUniqueId; - MediaRoute2Info(@NonNull Builder builder) { mId = builder.mId; mProviderId = builder.mProviderId; @@ -166,13 +168,12 @@ public final class MediaRoute2Info implements Parcelable { mConnectionState = builder.mConnectionState; mIconUri = builder.mIconUri; mClientPackageName = builder.mClientPackageName; - mSupportedCategories = builder.mSupportedCategories; + mRouteTypes = builder.mRouteTypes; mVolume = builder.mVolume; mVolumeMax = builder.mVolumeMax; mVolumeHandling = builder.mVolumeHandling; mDeviceType = builder.mDeviceType; mExtras = builder.mExtras; - mUniqueId = createUniqueId(); } MediaRoute2Info(@NonNull Parcel in) { @@ -183,25 +184,12 @@ public final class MediaRoute2Info implements Parcelable { mConnectionState = in.readInt(); mIconUri = in.readParcelable(null); mClientPackageName = in.readString(); - mSupportedCategories = in.createStringArrayList(); + mRouteTypes = in.createStringArrayList(); mVolume = in.readInt(); mVolumeMax = in.readInt(); mVolumeHandling = in.readInt(); mDeviceType = in.readInt(); mExtras = in.readBundle(); - mUniqueId = createUniqueId(); - } - - private String createUniqueId() { - String uniqueId = null; - if (mProviderId != null) { - uniqueId = toUniqueId(mProviderId, mId); - } - return uniqueId; - } - - static String toUniqueId(String providerId, String routeId) { - return providerId + ":" + routeId; } /** @@ -235,7 +223,7 @@ public final class MediaRoute2Info implements Parcelable { && (mConnectionState == other.mConnectionState) && Objects.equals(mIconUri, other.mIconUri) && Objects.equals(mClientPackageName, other.mClientPackageName) - && Objects.equals(mSupportedCategories, other.mSupportedCategories) + && Objects.equals(mRouteTypes, other.mRouteTypes) && (mVolume == other.mVolume) && (mVolumeMax == other.mVolumeMax) && (mVolumeHandling == other.mVolumeHandling) @@ -247,29 +235,34 @@ public final class MediaRoute2Info implements Parcelable { @Override public int hashCode() { return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri, - mSupportedCategories, mVolume, mVolumeMax, mVolumeHandling, mDeviceType); + mRouteTypes, mVolume, mVolumeMax, mVolumeHandling, mDeviceType); } /** - * Gets the id of the route. - * Use {@link #getUniqueId()} if you need a unique identifier. + * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have + * unique IDs. + * <p> + * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method + * can be different from what was set in {@link MediaRoute2ProviderService}. * - * @see #getUniqueId() + * @see Builder#setId(String) */ @NonNull public String getId() { - return mId; + if (mProviderId != null) { + return toUniqueId(mProviderId, mId); + } else { + return mId; + } } /** - * Gets the unique id of the route. A route obtained from - * {@link com.android.server.media.MediaRouterService} always has a unique id. - * - * @return unique id of the route or null if it has no unique id. + * Gets the original id set by {@link Builder#setId(String)}. + * @hide */ - @Nullable - public String getUniqueId() { - return mUniqueId; + @NonNull + public String getOriginalId() { + return mId; } /** @@ -331,8 +324,8 @@ public final class MediaRoute2Info implements Parcelable { * Gets the supported categories of the route. */ @NonNull - public List<String> getSupportedCategories() { - return mSupportedCategories; + public List<String> getRouteTypes() { + return mRouteTypes; } //TODO: once device types are confirmed, reflect those into the comment. @@ -376,32 +369,15 @@ public final class MediaRoute2Info implements Parcelable { } /** - * Returns if the route supports the specified control category + * Returns if the route contains at least one of the specified route types. * - * @param controlCategory control category to consider - * @return true if the route supports at the category + * @param routeTypes the list of route types to consider + * @return true if the route contains at least one type in the list */ - public boolean supportsControlCategory(@NonNull String controlCategory) { - Objects.requireNonNull(controlCategory, "control category must not be null"); - for (String supportedCategory : getSupportedCategories()) { - if (TextUtils.equals(controlCategory, supportedCategory)) { - return true; - } - } - return false; - } - - //TODO: Move this if we re-define control category / selector things. - /** - * Returns if the route supports at least one of the specified control categories - * - * @param controlCategories the list of control categories to consider - * @return true if the route supports at least one category - */ - public boolean supportsControlCategories(@NonNull Collection<String> controlCategories) { - Objects.requireNonNull(controlCategories, "control categories must not be null"); - for (String controlCategory : controlCategories) { - if (supportsControlCategory(controlCategory)) { + public boolean containsRouteTypes(@NonNull Collection<String> routeTypes) { + Objects.requireNonNull(routeTypes, "routeTypes must not be null"); + for (String routeType : routeTypes) { + if (getRouteTypes().contains(routeType)) { return true; } } @@ -422,7 +398,7 @@ public final class MediaRoute2Info implements Parcelable { dest.writeInt(mConnectionState); dest.writeParcelable(mIconUri, flags); dest.writeString(mClientPackageName); - dest.writeStringList(mSupportedCategories); + dest.writeStringList(mRouteTypes); dest.writeInt(mVolume); dest.writeInt(mVolumeMax); dest.writeInt(mVolumeHandling); @@ -460,7 +436,7 @@ public final class MediaRoute2Info implements Parcelable { int mConnectionState; Uri mIconUri; String mClientPackageName; - List<String> mSupportedCategories; + List<String> mRouteTypes; int mVolume; int mVolumeMax; int mVolumeHandling = PLAYBACK_VOLUME_FIXED; @@ -471,7 +447,7 @@ public final class MediaRoute2Info implements Parcelable { public Builder(@NonNull String id, @NonNull CharSequence name) { setId(id); setName(name); - mSupportedCategories = new ArrayList<>(); + mRouteTypes = new ArrayList<>(); } public Builder(@NonNull MediaRoute2Info routeInfo) { @@ -488,7 +464,7 @@ public final class MediaRoute2Info implements Parcelable { mConnectionState = routeInfo.mConnectionState; mIconUri = routeInfo.mIconUri; setClientPackageName(routeInfo.mClientPackageName); - setSupportedCategories(routeInfo.mSupportedCategories); + setRouteTypes(routeInfo.mRouteTypes); setVolume(routeInfo.mVolume); setVolumeMax(routeInfo.mVolumeMax); setVolumeHandling(routeInfo.mVolumeHandling); @@ -499,7 +475,15 @@ public final class MediaRoute2Info implements Parcelable { } /** - * Sets the unique id of the route. + * Sets the unique id of the route. The value given here must be unique for each of your + * route. + * <p> + * In order to ensure uniqueness in {@link MediaRouter2} side, the value of + * {@link MediaRoute2Info#getId()} can be different from what was set in + * {@link MediaRoute2ProviderService}. + * </p> + * + * @see MediaRoute2Info#getId() */ @NonNull public Builder setId(@NonNull String id) { @@ -585,35 +569,35 @@ public final class MediaRoute2Info implements Parcelable { } /** - * Sets the supported categories of the route. + * Sets the types of the route. */ @NonNull - public Builder setSupportedCategories(@NonNull Collection<String> categories) { - mSupportedCategories = new ArrayList<>(); - return addSupportedCategories(categories); + public Builder setRouteTypes(@NonNull Collection<String> routeTypes) { + mRouteTypes = new ArrayList<>(); + return addRouteTypes(routeTypes); } /** - * Adds supported categories for the route. + * Adds types for the route. */ @NonNull - public Builder addSupportedCategories(@NonNull Collection<String> categories) { - Objects.requireNonNull(categories, "categories must not be null"); - for (String category: categories) { - addSupportedCategory(category); + public Builder addRouteTypes(@NonNull Collection<String> routeTypes) { + Objects.requireNonNull(routeTypes, "routeTypes must not be null"); + for (String routeType: routeTypes) { + addRouteType(routeType); } return this; } /** - * Add a supported category for the route. + * Add a type for the route. */ @NonNull - public Builder addSupportedCategory(@NonNull String category) { - if (TextUtils.isEmpty(category)) { - throw new IllegalArgumentException("category must not be null or empty"); + public Builder addRouteType(@NonNull String routeType) { + if (TextUtils.isEmpty(routeType)) { + throw new IllegalArgumentException("routeType must not be null or empty"); } - mSupportedCategories.add(category); + mRouteTypes.add(routeType); return this; } diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java index 7078d4a0b568..e2f246cae8c2 100644 --- a/media/java/android/media/MediaRoute2ProviderInfo.java +++ b/media/java/android/media/MediaRoute2ProviderInfo.java @@ -25,6 +25,7 @@ import android.util.ArrayMap; import java.util.Arrays; import java.util.Collection; +import java.util.Map; import java.util.Objects; /** @@ -161,14 +162,17 @@ public final class MediaRoute2ProviderInfo implements Parcelable { return this; } mUniqueId = uniqueId; - final int count = mRoutes.size(); - for (int i = 0; i < count; i++) { - MediaRoute2Info route = mRoutes.valueAt(i); - mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route) + + final ArrayMap<String, MediaRoute2Info> newRoutes = new ArrayMap<>(); + for (Map.Entry<String, MediaRoute2Info> entry : mRoutes.entrySet()) { + MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue()) .setProviderId(mUniqueId) - .build()); + .build(); + newRoutes.put(routeWithProviderId.getId(), routeWithProviderId); } + mRoutes.clear(); + mRoutes.putAll(newRoutes); return this; } diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 99bd1dcde3bb..24b65baebcbd 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -29,6 +29,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -55,7 +56,7 @@ public abstract class MediaRoute2ProviderService extends Service { private MediaRoute2ProviderInfo mProviderInfo; @GuardedBy("mSessionLock") - private ArrayMap<Integer, RouteSessionInfo> mSessionInfo = new ArrayMap<>(); + private ArrayMap<String, RouteSessionInfo> mSessionInfo = new ArrayMap<>(); public MediaRoute2ProviderService() { mHandler = new Handler(Looper.getMainLooper()); @@ -106,7 +107,10 @@ public abstract class MediaRoute2ProviderService extends Service { * null if the session is destroyed or id is not valid. */ @Nullable - public final RouteSessionInfo getSessionInfo(int sessionId) { + public final RouteSessionInfo getSessionInfo(@NonNull String sessionId) { + if (TextUtils.isEmpty(sessionId)) { + throw new IllegalArgumentException("sessionId must not be empty"); + } synchronized (mSessionLock) { return mSessionInfo.get(sessionId); } @@ -134,7 +138,7 @@ public abstract class MediaRoute2ProviderService extends Service { */ public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - int sessionId = sessionInfo.getSessionId(); + String sessionId = sessionInfo.getId(); if (sessionInfo.getSelectedRoutes().isEmpty()) { releaseSession(sessionId); return; @@ -160,7 +164,7 @@ public abstract class MediaRoute2ProviderService extends Service { public final void notifySessionInfoChanged(@NonNull RouteSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - int sessionId = sessionInfo.getSessionId(); + String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { mSessionInfo.put(sessionId, sessionInfo); @@ -185,7 +189,7 @@ public abstract class MediaRoute2ProviderService extends Service { * controlled, pass a {@link Bundle} that contains how to control it. * * @param sessionInfo information of the new session. - * The {@link RouteSessionInfo#getSessionId() id} of the session must be + * The {@link RouteSessionInfo#getId() id} of the session must be * unique. Pass {@code null} to reject the request or inform clients that * session creation is failed. * @param requestId id of the previous request to create this session @@ -194,13 +198,13 @@ public abstract class MediaRoute2ProviderService extends Service { // TODO: Maybe better to create notifySessionCreationFailed? public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) { if (sessionInfo != null) { - int sessionId = sessionInfo.getSessionId(); + String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { Log.w(TAG, "Ignoring duplicate session id."); return; } - mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo); + mSessionInfo.put(sessionInfo.getId(), sessionInfo); } schedulePublishState(); } @@ -220,9 +224,12 @@ public abstract class MediaRoute2ProviderService extends Service { * {@link #onDestroySession} is called if the session is released. * * @param sessionId id of the session to be released - * @see #onDestroySession(int, RouteSessionInfo) + * @see #onDestroySession(String, RouteSessionInfo) */ - public final void releaseSession(int sessionId) { + public final void releaseSession(@NonNull String sessionId) { + if (TextUtils.isEmpty(sessionId)) { + throw new IllegalArgumentException("sessionId must not be empty"); + } //TODO: notify media router service of release. RouteSessionInfo sessionInfo; synchronized (mSessionLock) { @@ -246,11 +253,11 @@ public abstract class MediaRoute2ProviderService extends Service { * * @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 routeType the route type of the new session * @param requestId the id of this session creation request */ public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId, - @NonNull String controlCategory, long requestId); + @NonNull String routeType, long requestId); /** * Called when a session is about to be destroyed. @@ -259,9 +266,10 @@ public abstract class MediaRoute2ProviderService extends Service { * * @param sessionId id of the session being destroyed. * @param lastSessionInfo information of the session being destroyed. - * @see #releaseSession(int) + * @see #releaseSession(String) */ - public abstract void onDestroySession(int sessionId, @NonNull RouteSessionInfo lastSessionInfo); + public abstract void onDestroySession(@NonNull String sessionId, + @NonNull RouteSessionInfo lastSessionInfo); //TODO: make a way to reject the request /** @@ -274,7 +282,7 @@ public abstract class MediaRoute2ProviderService extends Service { * @param routeId id of the route * @see #updateSessionInfo(RouteSessionInfo) */ - public abstract void onSelectRoute(int sessionId, @NonNull String routeId); + public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId); //TODO: make a way to reject the request /** @@ -286,7 +294,7 @@ public abstract class MediaRoute2ProviderService extends Service { * @param sessionId id of the session * @param routeId id of the route */ - public abstract void onDeselectRoute(int sessionId, @NonNull String routeId); + public abstract void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId); //TODO: make a way to reject the request /** @@ -298,7 +306,26 @@ public abstract class MediaRoute2ProviderService extends Service { * @param sessionId id of the session * @param routeId id of the route */ - public abstract void onTransferToRoute(int sessionId, @NonNull String routeId); + public abstract void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId); + + /** + * Called when the {@link RouteDiscoveryRequest discovery request} has changed. + * <p> + * Whenever an application registers a {@link MediaRouter2.RouteCallback callback}, + * it also provides a discovery request to specify types of routes that it is interested in. + * The media router combines all of these discovery request into a single discovery request + * and notifies each provider. + * </p><p> + * The provider should examine {@link RouteDiscoveryRequest#getRouteTypes() route types} + * in the discovery request to determine what kind of routes it should try to discover + * and whether it should perform active or passive scans. In many cases, the provider may be + * able to save power by not performing any scans when the request doesn't have any matching + * route types. + * </p> + * + * @param request the new discovery request + */ + public void onDiscoveryRequestChanged(@NonNull RouteDiscoveryRequest request) {} /** * Updates provider info and publishes routes and session info. @@ -357,46 +384,62 @@ public abstract class MediaRoute2ProviderService extends Service { @Override public void requestCreateSession(String packageName, String routeId, - String controlCategory, long requestId) { + String routeType, long requestId) { if (!checkCallerisSystem()) { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession, - MediaRoute2ProviderService.this, packageName, routeId, controlCategory, + MediaRoute2ProviderService.this, packageName, routeId, routeType, requestId)); } @Override - public void releaseSession(int sessionId) { + public void releaseSession(@NonNull String sessionId) { if (!checkCallerisSystem()) { return; } + if (TextUtils.isEmpty(sessionId)) { + Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service."); + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession, MediaRoute2ProviderService.this, sessionId)); } @Override - public void selectRoute(int sessionId, String routeId) { + public void selectRoute(@NonNull String sessionId, String routeId) { if (!checkCallerisSystem()) { return; } + if (TextUtils.isEmpty(sessionId)) { + Log.w(TAG, "selectRoute: Ignoring empty sessionId from system service."); + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute, MediaRoute2ProviderService.this, sessionId, routeId)); } @Override - public void deselectRoute(int sessionId, String routeId) { + public void deselectRoute(@NonNull String sessionId, String routeId) { if (!checkCallerisSystem()) { return; } + if (TextUtils.isEmpty(sessionId)) { + Log.w(TAG, "deselectRoute: Ignoring empty sessionId from system service."); + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute, MediaRoute2ProviderService.this, sessionId, routeId)); } @Override - public void transferToRoute(int sessionId, String routeId) { + public void transferToRoute(@NonNull String sessionId, String routeId) { if (!checkCallerisSystem()) { return; } + if (TextUtils.isEmpty(sessionId)) { + Log.w(TAG, "transferToRoute: Ignoring empty sessionId from system service."); + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute, MediaRoute2ProviderService.this, sessionId, routeId)); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index f5cfde4b968b..8ebf6174cabf 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -39,7 +39,6 @@ import com.android.internal.annotations.GuardedBy; import java.lang.annotation.Retention; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -48,6 +47,7 @@ import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; /** * A new Media Router @@ -118,7 +118,7 @@ public class MediaRouter2 { final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); @GuardedBy("sLock") - private List<String> mControlCategories = Collections.emptyList(); + private RouteDiscoveryRequest mDiscoveryRequest = RouteDiscoveryRequest.EMPTY; // TODO: Make MediaRouter2 is always connected to the MediaRouterService. @GuardedBy("sLock") @@ -152,7 +152,6 @@ public class MediaRouter2 { mMediaRouterService = IMediaRouterService.Stub.asInterface( ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE)); mPackageName = mContext.getPackageName(); - //TODO: read control categories from the manifest mHandler = new Handler(Looper.getMainLooper()); List<MediaRoute2Info> currentSystemRoutes = null; @@ -177,9 +176,9 @@ public class MediaRouter2 { * @hide */ public static boolean checkRouteListContainsRouteId(@NonNull List<MediaRoute2Info> routeList, - @NonNull String uniqueRouteId) { + @NonNull String routeId) { for (MediaRoute2Info info : routeList) { - if (TextUtils.equals(uniqueRouteId, info.getUniqueId())) { + if (TextUtils.equals(routeId, info.getId())) { return true; } } @@ -188,24 +187,18 @@ public class MediaRouter2 { /** * Registers a callback to discover routes and to receive events when they change. - */ - public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull RouteCallback routeCallback) { - registerRouteCallback(executor, routeCallback, 0); - } - - /** - * Registers a callback to discover routes and to receive events when they change. * <p> * If you register the same callback twice or more, it will be ignored. * </p> */ public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull RouteCallback routeCallback, int flags) { + @NonNull RouteCallback routeCallback, + @NonNull RouteDiscoveryRequest request) { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(routeCallback, "callback must not be null"); + Objects.requireNonNull(request, "request must not be null"); - RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, flags); + RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, request); if (!mRouteCallbackRecords.addIfAbsent(record)) { Log.w(TAG, "Ignoring the same callback"); return; @@ -216,7 +209,8 @@ public class MediaRouter2 { Client2 client = new Client2(); try { mMediaRouterService.registerClient2(client, mPackageName); - mMediaRouterService.setControlCategories(client, mControlCategories); + updateDiscoveryRequestLocked(); + mMediaRouterService.setDiscoveryRequest2(client, mDiscoveryRequest); mClient = client; } catch (RemoteException ex) { Log.e(TAG, "Unable to register media router.", ex); @@ -238,7 +232,7 @@ public class MediaRouter2 { Objects.requireNonNull(routeCallback, "callback must not be null"); if (!mRouteCallbackRecords.remove( - new RouteCallbackRecord(null, routeCallback, 0))) { + new RouteCallbackRecord(null, routeCallback, null))) { Log.w(TAG, "Ignoring unknown callback"); return; } @@ -256,30 +250,10 @@ public class MediaRouter2 { } } - //TODO(b/139033746): Rename "Control Category" when it's finalized. - /** - * Sets the control categories of the application. - * Routes that support at least one of the given control categories are handled - * by the media router. - */ - public void setControlCategories(@NonNull Collection<String> controlCategories) { - Objects.requireNonNull(controlCategories, "control categories must not be null"); - - List<String> newControlCategories = new ArrayList<>(controlCategories); - - synchronized (sRouterLock) { - mShouldUpdateRoutes = true; - - // invoke callbacks due to control categories change - handleControlCategoriesChangedLocked(newControlCategories); - if (mClient != null) { - try { - mMediaRouterService.setControlCategories(mClient, mControlCategories); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to set control categories.", ex); - } - } - } + private void updateDiscoveryRequestLocked() { + mDiscoveryRequest = new RouteDiscoveryRequest.Builder( + mRouteCallbackRecords.stream().map(record -> record.mRequest).collect( + Collectors.toList())).build(); } /** @@ -287,8 +261,8 @@ public class MediaRouter2 { * known to the media router. * Please note that the list can be changed before callbacks are invoked. * - * @return the list of routes that support at least one of the control categories set by - * the application + * @return the list of routes that contains at least one of the route types in discovery + * requests registered by the application */ @NonNull public List<MediaRoute2Info> getRoutes() { @@ -298,7 +272,7 @@ public class MediaRouter2 { List<MediaRoute2Info> filteredRoutes = new ArrayList<>(); for (MediaRoute2Info route : mRoutes.values()) { - if (route.supportsControlCategories(mControlCategories)) { + if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) { filteredRoutes.add(route); } } @@ -350,26 +324,25 @@ public class MediaRouter2 { * Requests the media route provider service to create a session with the given route. * * @param route the route you want to create a session with. - * @param controlCategory the control category of the session. Should not be empty + * @param routeType the route type of the session. Should not be empty * * @see SessionCallback#onSessionCreated * @see SessionCallback#onSessionCreationFailed */ @NonNull public void requestCreateSession(@NonNull MediaRoute2Info route, - @NonNull String controlCategory) { + @NonNull String routeType) { Objects.requireNonNull(route, "route must not be null"); - if (TextUtils.isEmpty(controlCategory)) { - throw new IllegalArgumentException("controlCategory must not be empty"); + if (TextUtils.isEmpty(routeType)) { + throw new IllegalArgumentException("routeType must not be empty"); } // TODO: Check the given route exists - // TODO: Check the route supports the given controlCategory + // TODO: Check the route supports the given routeType final int requestId; requestId = mSessionCreationRequestCnt.getAndIncrement(); - SessionCreationRequest request = new SessionCreationRequest( - requestId, route, controlCategory); + SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeType); mSessionCreationRequests.add(request); Client2 client; @@ -378,8 +351,7 @@ public class MediaRouter2 { } if (client != null) { try { - mMediaRouterService.requestCreateSession( - client, route, controlCategory, requestId); + mMediaRouterService.requestCreateSession(client, route, routeType, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to request to create session.", ex); mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler, @@ -461,36 +433,6 @@ public class MediaRouter2 { } } - private void handleControlCategoriesChangedLocked(List<String> newControlCategories) { - List<MediaRoute2Info> addedRoutes = new ArrayList<>(); - List<MediaRoute2Info> removedRoutes = new ArrayList<>(); - - List<String> prevControlCategories = mControlCategories; - mControlCategories = newControlCategories; - - for (MediaRoute2Info route : mRoutes.values()) { - boolean preSupported = route.supportsControlCategories(prevControlCategories); - boolean postSupported = route.supportsControlCategories(newControlCategories); - if (preSupported == postSupported) { - continue; - } - if (preSupported) { - removedRoutes.add(route); - } else { - addedRoutes.add(route); - } - } - - if (removedRoutes.size() > 0) { - mHandler.sendMessage(obtainMessage(MediaRouter2::notifyRoutesRemoved, - MediaRouter2.this, removedRoutes)); - } - if (addedRoutes.size() > 0) { - mHandler.sendMessage(obtainMessage(MediaRouter2::notifyRoutesAdded, - MediaRouter2.this, addedRoutes)); - } - } - void addRoutesOnHandler(List<MediaRoute2Info> routes) { // TODO: When onRoutesAdded is first called, // 1) clear mRoutes before adding the routes @@ -499,8 +441,8 @@ public class MediaRouter2 { List<MediaRoute2Info> addedRoutes = new ArrayList<>(); synchronized (sRouterLock) { for (MediaRoute2Info route : routes) { - mRoutes.put(route.getUniqueId(), route); - if (route.supportsControlCategories(mControlCategories)) { + mRoutes.put(route.getId(), route); + if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) { addedRoutes.add(route); } } @@ -515,8 +457,8 @@ public class MediaRouter2 { List<MediaRoute2Info> removedRoutes = new ArrayList<>(); synchronized (sRouterLock) { for (MediaRoute2Info route : routes) { - mRoutes.remove(route.getUniqueId()); - if (route.supportsControlCategories(mControlCategories)) { + mRoutes.remove(route.getId()); + if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) { removedRoutes.add(route); } } @@ -531,8 +473,8 @@ public class MediaRouter2 { List<MediaRoute2Info> changedRoutes = new ArrayList<>(); synchronized (sRouterLock) { for (MediaRoute2Info route : routes) { - mRoutes.put(route.getUniqueId(), route); - if (route.supportsControlCategories(mControlCategories)) { + mRoutes.put(route.getId(), route); + if (route.containsRouteTypes(mDiscoveryRequest.getRouteTypes())) { changedRoutes.add(route); } } @@ -562,27 +504,27 @@ public class MediaRouter2 { mSessionCreationRequests.remove(matchingRequest); MediaRoute2Info requestedRoute = matchingRequest.mRoute; - String requestedControlCategory = matchingRequest.mControlCategory; + String requestedRouteType = matchingRequest.mRouteType; if (sessionInfo == null) { // TODO: We may need to distinguish between failure and rejection. // One way can be introducing 'reason'. - notifySessionCreationFailed(requestedRoute, requestedControlCategory); + notifySessionCreationFailed(requestedRoute, requestedRouteType); return; - } else if (!TextUtils.equals(requestedControlCategory, - sessionInfo.getControlCategory())) { - Log.w(TAG, "The session has different control category from what we requested. " - + "(requested=" + requestedControlCategory - + ", actual=" + sessionInfo.getControlCategory() + } else if (!TextUtils.equals(requestedRouteType, + sessionInfo.getRouteType())) { + Log.w(TAG, "The session has different route type from what we requested. " + + "(requested=" + requestedRouteType + + ", actual=" + sessionInfo.getRouteType() + ")"); - notifySessionCreationFailed(requestedRoute, requestedControlCategory); + notifySessionCreationFailed(requestedRoute, requestedRouteType); return; } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) { Log.w(TAG, "The session does not contain the requested route. " + "(requestedRouteId=" + requestedRoute.getId() + ", actualRoutes=" + sessionInfo.getSelectedRoutes() + ")"); - notifySessionCreationFailed(requestedRoute, requestedControlCategory); + notifySessionCreationFailed(requestedRoute, requestedRouteType); return; } else if (!TextUtils.equals(requestedRoute.getProviderId(), sessionInfo.getProviderId())) { @@ -590,7 +532,7 @@ public class MediaRouter2 { + "(requested route's providerId=" + requestedRoute.getProviderId() + ", actual providerId=" + sessionInfo.getProviderId() + ")"); - notifySessionCreationFailed(requestedRoute, requestedControlCategory); + notifySessionCreationFailed(requestedRoute, requestedRouteType); return; } } @@ -598,7 +540,7 @@ public class MediaRouter2 { if (sessionInfo != null) { RouteSessionController controller = new RouteSessionController(sessionInfo); synchronized (sRouterLock) { - mSessionControllers.put(controller.getUniqueSessionId(), controller); + mSessionControllers.put(controller.getSessionId(), controller); } notifySessionCreated(controller); } @@ -612,12 +554,12 @@ public class MediaRouter2 { RouteSessionController matchingController; synchronized (sRouterLock) { - matchingController = mSessionControllers.get(sessionInfo.getUniqueSessionId()); + matchingController = mSessionControllers.get(sessionInfo.getId()); } if (matchingController == null) { Log.w(TAG, "changeSessionInfoOnHandler: Matching controller not found. uniqueSessionId=" - + sessionInfo.getUniqueSessionId()); + + sessionInfo.getId()); return; } @@ -638,7 +580,7 @@ public class MediaRouter2 { return; } - final String uniqueSessionId = sessionInfo.getUniqueSessionId(); + final String uniqueSessionId = sessionInfo.getId(); RouteSessionController matchingController; synchronized (sRouterLock) { matchingController = mSessionControllers.get(uniqueSessionId); @@ -647,7 +589,7 @@ public class MediaRouter2 { if (matchingController == null) { if (DEBUG) { Log.d(TAG, "releaseControllerOnHandler: Matching controller not found. " - + "uniqueSessionId=" + sessionInfo.getUniqueSessionId()); + + "uniqueSessionId=" + sessionInfo.getId()); } return; } @@ -666,24 +608,41 @@ public class MediaRouter2 { notifyControllerReleased(matchingController); } + private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes, + RouteDiscoveryRequest discoveryRequest) { + return routes.stream() + .filter( + route -> route.containsRouteTypes(discoveryRequest.getRouteTypes())) + .collect(Collectors.toList()); + } + private void notifyRoutesAdded(List<MediaRoute2Info> routes) { for (RouteCallbackRecord record: mRouteCallbackRecords) { - record.mExecutor.execute( - () -> record.mRouteCallback.onRoutesAdded(routes)); + List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest); + if (!filteredRoutes.isEmpty()) { + record.mExecutor.execute( + () -> record.mRouteCallback.onRoutesAdded(filteredRoutes)); + } } } private void notifyRoutesRemoved(List<MediaRoute2Info> routes) { for (RouteCallbackRecord record: mRouteCallbackRecords) { - record.mExecutor.execute( - () -> record.mRouteCallback.onRoutesRemoved(routes)); + List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest); + if (!filteredRoutes.isEmpty()) { + record.mExecutor.execute( + () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes)); + } } } private void notifyRoutesChanged(List<MediaRoute2Info> routes) { for (RouteCallbackRecord record: mRouteCallbackRecords) { - record.mExecutor.execute( - () -> record.mRouteCallback.onRoutesChanged(routes)); + List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mRequest); + if (!filteredRoutes.isEmpty()) { + record.mExecutor.execute( + () -> record.mRouteCallback.onRoutesChanged(filteredRoutes)); + } } } @@ -694,10 +653,10 @@ public class MediaRouter2 { } } - private void notifySessionCreationFailed(MediaRoute2Info route, String controlCategory) { + private void notifySessionCreationFailed(MediaRoute2Info route, String routeType) { for (SessionCallbackRecord record: mSessionCallbackRecords) { record.mExecutor.execute( - () -> record.mSessionCallback.onSessionCreationFailed(route, controlCategory)); + () -> record.mSessionCallback.onSessionCreationFailed(route, routeType)); } } @@ -764,10 +723,10 @@ public class MediaRouter2 { * Called when the session creation request failed. * * @param requestedRoute the route info which was used for the request - * @param requestedControlCategory the control category which was used for the request + * @param requestedRouteType the route type which was used for the request */ public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute, - @NonNull String requestedControlCategory) {} + @NonNull String requestedRouteType) {} /** * Called when the session info has changed. @@ -822,30 +781,19 @@ public class MediaRouter2 { /** * @return the ID of the session */ - public int getSessionId() { + public String getSessionId() { synchronized (mControllerLock) { - return mSessionInfo.getSessionId(); + return mSessionInfo.getId(); } } /** - * @return the unique ID of the session - * @hide + * @return the type of routes that the session includes. */ @NonNull - public String getUniqueSessionId() { + public String getRouteType() { synchronized (mControllerLock) { - return mSessionInfo.getUniqueSessionId(); - } - } - - /** - * @return the category of routes that the session includes. - */ - @NonNull - public String getControlCategory() { - synchronized (mControllerLock) { - return mSessionInfo.getControlCategory(); + return mSessionInfo.getRouteType(); } } @@ -935,13 +883,13 @@ public class MediaRouter2 { } List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(); - if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) { + if (checkRouteListContainsRouteId(selectedRoutes, route.getId())) { Log.w(TAG, "Ignoring selecting a route that is already selected. route=" + route); return; } List<MediaRoute2Info> selectableRoutes = getSelectableRoutes(); - if (!checkRouteListContainsRouteId(selectableRoutes, route.getUniqueId())) { + if (!checkRouteListContainsRouteId(selectableRoutes, route.getId())) { Log.w(TAG, "Ignoring selecting a non-selectable route=" + route); return; } @@ -952,7 +900,7 @@ public class MediaRouter2 { } if (client != null) { try { - mMediaRouterService.selectRoute(client, getUniqueSessionId(), route); + mMediaRouterService.selectRoute(client, getSessionId(), route); } catch (RemoteException ex) { Log.e(TAG, "Unable to select route for session.", ex); } @@ -982,13 +930,13 @@ public class MediaRouter2 { } List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(); - if (!checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) { + if (!checkRouteListContainsRouteId(selectedRoutes, route.getId())) { Log.w(TAG, "Ignoring deselecting a route that is not selected. route=" + route); return; } List<MediaRoute2Info> deselectableRoutes = getDeselectableRoutes(); - if (!checkRouteListContainsRouteId(deselectableRoutes, route.getUniqueId())) { + if (!checkRouteListContainsRouteId(deselectableRoutes, route.getId())) { Log.w(TAG, "Ignoring deselecting a non-deselectable route=" + route); return; } @@ -999,7 +947,7 @@ public class MediaRouter2 { } if (client != null) { try { - mMediaRouterService.deselectRoute(client, getUniqueSessionId(), route); + mMediaRouterService.deselectRoute(client, getSessionId(), route); } catch (RemoteException ex) { Log.e(TAG, "Unable to remove route from session.", ex); } @@ -1029,14 +977,14 @@ public class MediaRouter2 { } List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(); - if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) { + if (checkRouteListContainsRouteId(selectedRoutes, route.getId())) { Log.w(TAG, "Ignoring transferring to a route that is already added. route=" + route); return; } List<MediaRoute2Info> transferrableRoutes = getTransferrableRoutes(); - if (!checkRouteListContainsRouteId(transferrableRoutes, route.getUniqueId())) { + if (!checkRouteListContainsRouteId(transferrableRoutes, route.getId())) { Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route); return; } @@ -1047,7 +995,7 @@ public class MediaRouter2 { } if (client != null) { try { - mMediaRouterService.transferToRoute(client, getUniqueSessionId(), route); + mMediaRouterService.transferToRoute(client, getSessionId(), route); } catch (RemoteException ex) { Log.e(TAG, "Unable to transfer to route for session.", ex); } @@ -1072,20 +1020,24 @@ public class MediaRouter2 { Client2 client; synchronized (sRouterLock) { - mSessionControllers.remove(getUniqueSessionId(), this); + mSessionControllers.remove(getSessionId(), this); client = mClient; } if (client != null) { try { - mMediaRouterService.releaseSession(client, getUniqueSessionId()); + mMediaRouterService.releaseSession(client, getSessionId()); } catch (RemoteException ex) { Log.e(TAG, "Unable to notify of controller release", ex); } } } + /** + * TODO: Change this to package private. (Hidden for debugging purposes) + * @hide + */ @NonNull - RouteSessionInfo getRouteSessionInfo() { + public RouteSessionInfo getRouteSessionInfo() { synchronized (mControllerLock) { return mSessionInfo; } @@ -1100,11 +1052,12 @@ public class MediaRouter2 { // TODO: This method uses two locks (mLock outside, sLock inside). // Check if there is any possiblity of deadlock. private List<MediaRoute2Info> getRoutesWithIdsLocked(List<String> routeIds) { + List<MediaRoute2Info> routes = new ArrayList<>(); synchronized (sRouterLock) { + // TODO: Maybe able to change using Collection.stream()? for (String routeId : routeIds) { - MediaRoute2Info route = mRoutes.get( - MediaRoute2Info.toUniqueId(mSessionInfo.mProviderId, routeId)); + MediaRoute2Info route = mRoutes.get(routeId); if (route != null) { routes.add(route); } @@ -1117,13 +1070,13 @@ public class MediaRouter2 { final class RouteCallbackRecord { public final Executor mExecutor; public final RouteCallback mRouteCallback; - public final int mFlags; + public final RouteDiscoveryRequest mRequest; RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback, - int flags) { + @Nullable RouteDiscoveryRequest request) { mRouteCallback = routeCallback; mExecutor = executor; - mFlags = flags; + mRequest = request; } @Override @@ -1172,13 +1125,13 @@ public class MediaRouter2 { final class SessionCreationRequest { public final MediaRoute2Info mRoute; - public final String mControlCategory; + public final String mRouteType; public final int mRequestId; SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route, - @NonNull String controlCategory) { + @NonNull String routeType) { mRoute = route; - mControlCategory = controlCategory; + mRouteType = routeType; mRequestId = requestId; } } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 2e68e42e714f..1e6ec51442d6 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -65,7 +65,7 @@ public class MediaRouter2Manager { @GuardedBy("mRoutesLock") private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); @NonNull - final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>(); + final ConcurrentMap<String, List<String>> mRouteTypeMap = new ConcurrentHashMap<>(); private AtomicInteger mNextRequestId = new AtomicInteger(1); @@ -144,7 +144,7 @@ public class MediaRouter2Manager { } //TODO: clear mRoutes? mClient = null; - mControlCategoryMap.clear(); + mRouteTypeMap.clear(); } } } @@ -160,14 +160,14 @@ public class MediaRouter2Manager { public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) { Objects.requireNonNull(packageName, "packageName must not be null"); - List<String> controlCategories = mControlCategoryMap.get(packageName); - if (controlCategories == null) { + List<String> routeTypes = mRouteTypeMap.get(packageName); + if (routeTypes == null) { return Collections.emptyList(); } List<MediaRoute2Info> routes = new ArrayList<>(); synchronized (mRoutesLock) { for (MediaRoute2Info route : mRoutes.values()) { - if (route.supportsControlCategories(controlCategories)) { + if (route.containsRouteTypes(routeTypes)) { routes.add(route); } } @@ -295,7 +295,7 @@ public class MediaRouter2Manager { void addRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { - mRoutes.put(route.getUniqueId(), route); + mRoutes.put(route.getId(), route); } } if (routes.size() > 0) { @@ -306,7 +306,7 @@ public class MediaRouter2Manager { void removeRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { - mRoutes.remove(route.getUniqueId()); + mRoutes.remove(route.getId()); } } if (routes.size() > 0) { @@ -317,7 +317,7 @@ public class MediaRouter2Manager { void changeRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { - mRoutes.put(route.getUniqueId(), route); + mRoutes.put(route.getId(), route); } } if (routes.size() > 0) { @@ -352,15 +352,15 @@ public class MediaRouter2Manager { } } - void updateControlCategories(String packageName, List<String> categories) { - List<String> prevCategories = mControlCategoryMap.put(packageName, categories); - if ((prevCategories == null && categories.size() == 0) - || Objects.equals(categories, prevCategories)) { + void updateRouteTypes(String packageName, List<String> routeTypes) { + List<String> prevTypes = mRouteTypeMap.put(packageName, routeTypes); + if ((prevTypes == null && routeTypes.size() == 0) + || Objects.equals(routeTypes, prevTypes)) { return; } for (CallbackRecord record : mCallbackRecords) { record.mExecutor.execute( - () -> record.mCallback.onControlCategoriesChanged(packageName, categories)); + () -> record.mCallback.onControlCategoriesChanged(packageName, routeTypes)); } } @@ -398,13 +398,13 @@ public class MediaRouter2Manager { /** - * Called when the control categories of an app is changed. + * Called when the route types of an app is changed. * * @param packageName the package name of the application - * @param controlCategories the list of control categories set by an application. + * @param routeTypes the list of route types set by an application. */ public void onControlCategoriesChanged(@NonNull String packageName, - @NonNull List<String> controlCategories) {} + @NonNull List<String> routeTypes) {} } final class CallbackRecord { @@ -440,10 +440,9 @@ public class MediaRouter2Manager { MediaRouter2Manager.this, packageName, route)); } - @Override - public void notifyControlCategoriesChanged(String packageName, List<String> categories) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateControlCategories, - MediaRouter2Manager.this, packageName, categories)); + public void notifyRouteTypesChanged(String packageName, List<String> routeTypes) { + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateRouteTypes, + MediaRouter2Manager.this, packageName, routeTypes)); } @Override diff --git a/media/java/android/media/MediaRouter2Utils.java b/media/java/android/media/MediaRouter2Utils.java new file mode 100644 index 000000000000..49045828dbe8 --- /dev/null +++ b/media/java/android/media/MediaRouter2Utils.java @@ -0,0 +1,100 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; + +/** + * @hide + */ +public class MediaRouter2Utils { + + static final String TAG = "MR2Utils"; + static final String SEPARATOR = ":"; + + /** + * @hide + */ + @NonNull + public static String toUniqueId(@NonNull String providerId, @NonNull String id) { + if (TextUtils.isEmpty(providerId)) { + Log.w(TAG, "toUniqueId: providerId shouldn't be empty"); + return null; + } + if (TextUtils.isEmpty(id)) { + Log.w(TAG, "toUniqueId: id shouldn't be null"); + return null; + } + + return providerId + SEPARATOR + id; + } + + /** + * Gets provider ID from unique ID. + * If the corresponding provider ID could not be generated, it will return null. + * + * @hide + */ + @Nullable + public static String getProviderId(@NonNull String uniqueId) { + if (TextUtils.isEmpty(uniqueId)) { + Log.w(TAG, "getProviderId: uniqueId shouldn't be empty"); + return null; + } + + int firstIndexOfSeparator = uniqueId.indexOf(SEPARATOR); + if (firstIndexOfSeparator == -1) { + return null; + } + + String providerId = uniqueId.substring(0, firstIndexOfSeparator); + if (TextUtils.isEmpty(providerId)) { + return null; + } + + return providerId; + } + + /** + * Gets the original ID (i.e. non-unique route/session ID) from unique ID. + * If the corresponding ID could not be generated, it will return null. + * + * @hide + */ + @Nullable + public static String getOriginalId(@NonNull String uniqueId) { + if (TextUtils.isEmpty(uniqueId)) { + Log.w(TAG, "getOriginalId: uniqueId shouldn't be empty"); + return null; + } + + int firstIndexOfSeparator = uniqueId.indexOf(SEPARATOR); + if (firstIndexOfSeparator == -1 || firstIndexOfSeparator + 1 >= uniqueId.length()) { + return null; + } + + String providerId = uniqueId.substring(firstIndexOfSeparator + 1); + if (TextUtils.isEmpty(providerId)) { + return null; + } + + return providerId; + } +} diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java index 40e90731f2a2..05fa511fc81a 100644 --- a/media/java/android/media/MediaScannerConnection.java +++ b/media/java/android/media/MediaScannerConnection.java @@ -16,7 +16,7 @@ package android.media; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentResolver; diff --git a/media/java/android/media/RouteDiscoveryRequest.aidl b/media/java/android/media/RouteDiscoveryRequest.aidl new file mode 100644 index 000000000000..744f6569325d --- /dev/null +++ b/media/java/android/media/RouteDiscoveryRequest.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable RouteDiscoveryRequest; diff --git a/media/java/android/media/RouteDiscoveryRequest.java b/media/java/android/media/RouteDiscoveryRequest.java new file mode 100644 index 000000000000..88b31fb30ffc --- /dev/null +++ b/media/java/android/media/RouteDiscoveryRequest.java @@ -0,0 +1,200 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + + +/** + * @hide + */ +public final class RouteDiscoveryRequest implements Parcelable { + @NonNull + public static final Creator<RouteDiscoveryRequest> CREATOR = + new Creator<RouteDiscoveryRequest>() { + @Override + public RouteDiscoveryRequest createFromParcel(Parcel in) { + return new RouteDiscoveryRequest(in); + } + + @Override + public RouteDiscoveryRequest[] newArray(int size) { + return new RouteDiscoveryRequest[size]; + } + }; + + @NonNull + private final List<String> mRouteTypes; + private final boolean mActiveScan; + @Nullable + private final Bundle mExtras; + + /** + * @hide + */ + public static final RouteDiscoveryRequest EMPTY = + new Builder(Collections.emptyList(), false).build(); + + RouteDiscoveryRequest(@NonNull Builder builder) { + mRouteTypes = builder.mRouteTypes; + mActiveScan = builder.mActiveScan; + mExtras = builder.mExtras; + } + + RouteDiscoveryRequest(@NonNull Parcel in) { + mRouteTypes = in.createStringArrayList(); + mActiveScan = in.readBoolean(); + mExtras = in.readBundle(); + } + + @NonNull + public List<String> getRouteTypes() { + return mRouteTypes; + } + + public boolean isActiveScan() { + return mActiveScan; + } + + /** + * @hide + */ + public Bundle getExtras() { + return mExtras; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStringList(mRouteTypes); + dest.writeBoolean(mActiveScan); + dest.writeBundle(mExtras); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder() + .append("RouteDiscoveryRequest{ ") + .append("routeTypes={") + .append(String.join(", ", mRouteTypes)) + .append("}") + .append(", activeScan=") + .append(mActiveScan) + .append(" }"); + + return result.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RouteDiscoveryRequest)) { + return false; + } + RouteDiscoveryRequest other = (RouteDiscoveryRequest) o; + return Objects.equals(mRouteTypes, other.mRouteTypes) + && mActiveScan == other.mActiveScan; + } + + /** + * Builder for {@link RouteDiscoveryRequest}. + */ + public static final class Builder { + List<String> mRouteTypes; + boolean mActiveScan; + Bundle mExtras; + + public Builder(@NonNull List<String> routeTypes, boolean activeScan) { + mRouteTypes = new ArrayList<>( + Objects.requireNonNull(routeTypes, "routeTypes must not be null")); + mActiveScan = activeScan; + } + + public Builder(@NonNull RouteDiscoveryRequest request) { + Objects.requireNonNull(request, "request must not be null"); + + mRouteTypes = request.getRouteTypes(); + mActiveScan = request.isActiveScan(); + mExtras = request.getExtras(); + } + + /** + * A constructor to combine all of the requests into a single request. + * It ignores extras of requests. + */ + Builder(@NonNull Collection<RouteDiscoveryRequest> requests) { + Set<String> routeTypeSet = new HashSet<>(); + mActiveScan = false; + for (RouteDiscoveryRequest request : requests) { + routeTypeSet.addAll(request.mRouteTypes); + mActiveScan |= request.mActiveScan; + } + mRouteTypes = new ArrayList<>(routeTypeSet); + } + + /** + * Sets route types to discover. + */ + public Builder setRouteTypes(@NonNull List<String> routeTypes) { + mRouteTypes = new ArrayList<>( + Objects.requireNonNull(routeTypes, "routeTypes must not be null")); + return this; + } + + /** + * Sets if active scanning should be performed. + */ + public Builder setActiveScan(boolean activeScan) { + mActiveScan = activeScan; + return this; + } + + /** + * Sets the extras of the route. + * @hide + */ + public Builder setExtras(@Nullable Bundle extras) { + mExtras = extras; + return this; + } + + /** + * Builds the {@link RouteDiscoveryRequest}. + */ + public RouteDiscoveryRequest build() { + return new RouteDiscoveryRequest(this); + } + } +} diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java index 2d7bc24ae7a2..5330630ef3a9 100644 --- a/media/java/android/media/RouteSessionInfo.java +++ b/media/java/android/media/RouteSessionInfo.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.Log; import java.util.ArrayList; import java.util.Collections; @@ -33,6 +34,7 @@ import java.util.Objects; * @hide */ public class RouteSessionInfo implements Parcelable { + @NonNull public static final Creator<RouteSessionInfo> CREATOR = new Creator<RouteSessionInfo>() { @@ -46,9 +48,11 @@ public class RouteSessionInfo implements Parcelable { } }; - final int mSessionId; - final String mPackageName; - final String mControlCategory; + public static final String TAG = "RouteSessionInfo"; + + final String mId; + final String mClientPackageName; + final String mRouteType; @Nullable final String mProviderId; final List<String> mSelectedRoutes; @@ -61,15 +65,19 @@ public class RouteSessionInfo implements Parcelable { RouteSessionInfo(@NonNull Builder builder) { Objects.requireNonNull(builder, "builder must not be null."); - mSessionId = builder.mSessionId; - mPackageName = builder.mPackageName; - mControlCategory = builder.mControlCategory; + mId = builder.mId; + mClientPackageName = builder.mClientPackageName; + mRouteType = builder.mRouteType; mProviderId = builder.mProviderId; - mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes); - mSelectableRoutes = Collections.unmodifiableList(builder.mSelectableRoutes); - mDeselectableRoutes = Collections.unmodifiableList(builder.mDeselectableRoutes); - mTransferrableRoutes = Collections.unmodifiableList(builder.mTransferrableRoutes); + mSelectedRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mSelectedRoutes)); + mSelectableRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mSelectableRoutes)); + mDeselectableRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mDeselectableRoutes)); + mTransferrableRoutes = Collections.unmodifiableList( + convertToUniqueRouteIds(builder.mTransferrableRoutes)); mControlHints = builder.mControlHints; } @@ -77,9 +85,9 @@ public class RouteSessionInfo implements Parcelable { RouteSessionInfo(@NonNull Parcel src) { Objects.requireNonNull(src, "src must not be null."); - mSessionId = src.readInt(); - mPackageName = ensureString(src.readString()); - mControlCategory = ensureString(src.readString()); + mId = ensureString(src.readString()); + mClientPackageName = ensureString(src.readString()); + mRouteType = ensureString(src.readString()); mProviderId = src.readString(); mSelectedRoutes = ensureList(src.createStringArrayList()); @@ -105,82 +113,59 @@ public class RouteSessionInfo implements Parcelable { } /** - * Gets non-unique session id (int) from unique session id (string). - * If the corresponding session id could not be generated, it will return null. - * @hide + * Returns whether the session info is valid or not + * + * TODO in this CL: Remove this method. */ - @Nullable - public static Integer getSessionId(@NonNull String uniqueSessionId) { - int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/"); - if (lastIndexOfSeparator == -1 || lastIndexOfSeparator + 1 >= uniqueSessionId.length()) { - return null; - } - - String integerString = uniqueSessionId.substring(lastIndexOfSeparator + 1); - if (TextUtils.isEmpty(integerString)) { - return null; - } - - try { - return Integer.parseInt(integerString); - } catch (NumberFormatException ex) { - return null; - } + public boolean isValid() { + return !TextUtils.isEmpty(mId) + && !TextUtils.isEmpty(mClientPackageName) + && !TextUtils.isEmpty(mRouteType) + && mSelectedRoutes.size() > 0; } /** - * Gets provider ID (string) from unique session id (string). - * If the corresponding provider ID could not be generated, it will return null. - * @hide + * Gets the id of the session. The sessions which are given by {@link MediaRouter2} will have + * unique IDs. + * <p> + * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method + * can be different from what was set in {@link MediaRoute2ProviderService}. * - * TODO: This logic seems error-prone. Consider to use long uniqueId. + * @see Builder#Builder(String, String, String) */ - @Nullable - public static String getProviderId(@NonNull String uniqueSessionId) { - int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/"); - if (lastIndexOfSeparator == -1) { - return null; - } - - String result = uniqueSessionId.substring(0, lastIndexOfSeparator); - if (TextUtils.isEmpty(result)) { - return null; + @NonNull + public String getId() { + if (mProviderId != null) { + return MediaRouter2Utils.toUniqueId(mProviderId, mId); + } else { + return mId; } - return result; } /** - * Returns whether the session info is valid or not - */ - public boolean isValid() { - return !TextUtils.isEmpty(mPackageName) - && !TextUtils.isEmpty(mControlCategory) - && mSelectedRoutes.size() > 0; - } - - /** - * Gets the id of the session + * Gets the original id set by {@link Builder#Builder(String, String, String)}. + * @hide */ @NonNull - public int getSessionId() { - return mSessionId; + public String getOriginalId() { + return mId; } /** * Gets the client package name of the session */ @NonNull - public String getPackageName() { - return mPackageName; + public String getClientPackageName() { + return mClientPackageName; } /** - * Gets the control category of the session. - * Routes that don't support the category can't be added to the session. + * Gets the route type of the session. + * Routes that don't have the type can't be added to the session. */ @NonNull - public String getControlCategory() { - return mControlCategory; + public String getRouteType() { + return mRouteType; } /** @@ -193,19 +178,6 @@ public class RouteSessionInfo implements Parcelable { } /** - * Gets the unique id of the session. - * @hide - */ - @NonNull - public String getUniqueSessionId() { - StringBuilder sessionIdBuilder = new StringBuilder() - .append(mProviderId) - .append("/") - .append(mSessionId); - return sessionIdBuilder.toString(); - } - - /** * Gets the list of ids of selected routes for the session. It shouldn't be empty. */ @NonNull @@ -252,9 +224,9 @@ public class RouteSessionInfo implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mSessionId); - dest.writeString(mPackageName); - dest.writeString(mControlCategory); + dest.writeString(mId); + dest.writeString(mClientPackageName); + dest.writeString(mRouteType); dest.writeString(mProviderId); dest.writeStringList(mSelectedRoutes); dest.writeStringList(mSelectableRoutes); @@ -267,8 +239,8 @@ public class RouteSessionInfo implements Parcelable { public String toString() { StringBuilder result = new StringBuilder() .append("RouteSessionInfo{ ") - .append("sessionId=").append(mSessionId) - .append(", controlCategory=").append(mControlCategory) + .append("sessionId=").append(mId) + .append(", routeType=").append(mRouteType) .append(", selectedRoutes={") .append(String.join(",", mSelectedRoutes)) .append("}") @@ -285,13 +257,31 @@ public class RouteSessionInfo implements Parcelable { return result.toString(); } + private List<String> convertToUniqueRouteIds(@NonNull List<String> routeIds) { + if (routeIds == null) { + Log.w(TAG, "routeIds is null. Returning an empty list"); + return Collections.emptyList(); + } + + // mProviderId can be null if not set. Return the original list for this case. + if (mProviderId == null) { + return routeIds; + } + + List<String> result = new ArrayList<>(); + for (String routeId : routeIds) { + result.add(MediaRouter2Utils.toUniqueId(mProviderId, routeId)); + } + return result; + } + /** * Builder class for {@link RouteSessionInfo}. */ public static final class Builder { - final String mPackageName; - final int mSessionId; - final String mControlCategory; + final String mId; + final String mClientPackageName; + final String mRouteType; String mProviderId; final List<String> mSelectedRoutes; final List<String> mSelectableRoutes; @@ -299,23 +289,43 @@ public class RouteSessionInfo implements Parcelable { final List<String> mTransferrableRoutes; Bundle mControlHints; - public Builder(int sessionId, @NonNull String packageName, - @NonNull String controlCategory) { - mSessionId = sessionId; - mPackageName = Objects.requireNonNull(packageName, "packageName must not be null"); - mControlCategory = Objects.requireNonNull(controlCategory, - "controlCategory must not be null"); - + /** + * Constructor for builder to create {@link RouteSessionInfo}. + * <p> + * In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of + * {@link RouteSessionInfo#getId()} can be different from what was set in + * {@link MediaRoute2ProviderService}. + * </p> + * + * @see MediaRoute2Info#getId() + */ + public Builder(@NonNull String id, @NonNull String clientPackageName, + @NonNull String routeType) { + if (TextUtils.isEmpty(id)) { + throw new IllegalArgumentException("id must not be empty"); + } + mId = id; + mClientPackageName = Objects.requireNonNull( + clientPackageName, "clientPackageName must not be null"); + mRouteType = Objects.requireNonNull(routeType, "routeType must not be null"); mSelectedRoutes = new ArrayList<>(); mSelectableRoutes = new ArrayList<>(); mDeselectableRoutes = new ArrayList<>(); mTransferrableRoutes = new ArrayList<>(); } - public Builder(RouteSessionInfo sessionInfo) { - mSessionId = sessionInfo.mSessionId; - mPackageName = sessionInfo.mPackageName; - mControlCategory = sessionInfo.mControlCategory; + /** + * Constructor for builder to create {@link RouteSessionInfo} with + * existing {@link RouteSessionInfo} instance. + * + * @param sessionInfo the existing instance to copy data from. + */ + public Builder(@NonNull RouteSessionInfo sessionInfo) { + Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); + + mId = sessionInfo.mId; + mClientPackageName = sessionInfo.mClientPackageName; + mRouteType = sessionInfo.mRouteType; mProviderId = sessionInfo.mProviderId; mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes); @@ -327,10 +337,17 @@ public class RouteSessionInfo implements Parcelable { } /** - * Sets the provider id of the session. + * Sets the provider ID of the session. + * Also, calling this method will make all type of route IDs be unique by adding + * {@code providerId:} as a prefix. So do NOT call this method twice on same instance. + * + * @hide */ @NonNull - public Builder setProviderId(String providerId) { + public Builder setProviderId(@NonNull String providerId) { + if (TextUtils.isEmpty(providerId)) { + throw new IllegalArgumentException("providerId must not be empty"); + } mProviderId = providerId; return this; } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index aff725734ee1..aece39d78694 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -940,16 +940,15 @@ public final class MediaSessionManager { /** * Called when a media key event is dispatched through the media session service. The * session token can be {@link null} if the framework has sent the media key event to the - * media button receiver to revive the media app's playback. - * - * the session is dead when , but the framework sent + * media button receiver to revive the media app's playback after the corresponding session + * is released. * * @param event Dispatched media key event. * @param packageName Package * @param sessionToken The media session's token. Can be {@code null}. */ default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName, - @NonNull MediaSession.Token sessionToken) { } + @Nullable MediaSession.Token sessionToken) { } } /** diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java index 56e5566df29c..77596a5de815 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java +++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java @@ -22,7 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java index 30a44ff32401..938ffcd5f731 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerManager.java +++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java @@ -23,7 +23,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.hardware.soundtrigger.ModelParams; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 1b9cac0c8c99..377b2bc19c6b 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -20,7 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.StringRes; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 5c11ed9bb7b4..7fbb3376d5fb 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -22,9 +22,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.app.Service; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java index ab7bbca1d01c..4318a0ae7d06 100644 --- a/media/java/android/media/tv/TvTrackInfo.java +++ b/media/java/android/media/tv/TvTrackInfo.java @@ -62,6 +62,8 @@ public final class TvTrackInfo implements Parcelable { private final int mAudioChannelCount; private final int mAudioSampleRate; private final boolean mAudioDescription; + private final boolean mHardOfHearing; + private final boolean mSpokenSubtitle; private final int mVideoWidth; private final int mVideoHeight; private final float mVideoFrameRate; @@ -72,8 +74,9 @@ public final class TvTrackInfo implements Parcelable { private TvTrackInfo(int type, String id, String language, CharSequence description, boolean encrypted, int audioChannelCount, int audioSampleRate, boolean audioDescription, - int videoWidth, int videoHeight, float videoFrameRate, float videoPixelAspectRatio, - byte videoActiveFormatDescription, Bundle extra) { + boolean hardOfHearing, boolean spokenSubtitle, int videoWidth, int videoHeight, + float videoFrameRate, float videoPixelAspectRatio, byte videoActiveFormatDescription, + Bundle extra) { mType = type; mId = id; mLanguage = language; @@ -82,6 +85,8 @@ public final class TvTrackInfo implements Parcelable { mAudioChannelCount = audioChannelCount; mAudioSampleRate = audioSampleRate; mAudioDescription = audioDescription; + mHardOfHearing = hardOfHearing; + mSpokenSubtitle = spokenSubtitle; mVideoWidth = videoWidth; mVideoHeight = videoHeight; mVideoFrameRate = videoFrameRate; @@ -99,6 +104,8 @@ public final class TvTrackInfo implements Parcelable { mAudioChannelCount = in.readInt(); mAudioSampleRate = in.readInt(); mAudioDescription = in.readInt() != 0; + mHardOfHearing = in.readInt() != 0; + mSpokenSubtitle = in.readInt() != 0; mVideoWidth = in.readInt(); mVideoHeight = in.readInt(); mVideoFrameRate = in.readFloat(); @@ -192,6 +199,39 @@ public final class TvTrackInfo implements Parcelable { } /** + * Returns {@code true} if the track is intended for people with hearing impairment, {@code + * false} otherwise. Valid only for {@link #TYPE_AUDIO} and {@link #TYPE_SUBTITLE} tracks. + * + * <p>For example of broadcast, hard of hearing information may be referred to broadcast + * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio Language + * Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN 300 468). + * + * @throws IllegalStateException if not called on an audio track or a subtitle track + */ + public boolean isHardOfHearing() { + if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) { + throw new IllegalStateException("Not an audio or a subtitle track"); + } + return mHardOfHearing; + } + + /** + * Returns {@code true} if the track is a spoken subtitle for people with visual impairment, + * {@code false} otherwise. Valid only for {@link #TYPE_AUDIO} tracks. + * + * <p>For example of broadcast, spoken subtitle information may be referred to broadcast + * standard (e.g. Supplementary Audio Language Descriptor of ETSI EN 300 468). + * + * @throws IllegalStateException if not called on an audio track + */ + public boolean isSpokenSubtitle() { + if (mType != TYPE_AUDIO) { + throw new IllegalStateException("Not an audio track"); + } + return mSpokenSubtitle; + } + + /** * Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO} * tracks. * @@ -287,6 +327,8 @@ public final class TvTrackInfo implements Parcelable { dest.writeInt(mAudioChannelCount); dest.writeInt(mAudioSampleRate); dest.writeInt(mAudioDescription ? 1 : 0); + dest.writeInt(mHardOfHearing ? 1 : 0); + dest.writeInt(mSpokenSubtitle ? 1 : 0); dest.writeInt(mVideoWidth); dest.writeInt(mVideoHeight); dest.writeFloat(mVideoFrameRate); @@ -310,6 +352,7 @@ public final class TvTrackInfo implements Parcelable { if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType || !TextUtils.equals(mLanguage, obj.mLanguage) || !TextUtils.equals(mDescription, obj.mDescription) + || mEncrypted != obj.mEncrypted || !Objects.equals(mExtra, obj.mExtra)) { return false; } @@ -318,7 +361,9 @@ public final class TvTrackInfo implements Parcelable { case TYPE_AUDIO: return mAudioChannelCount == obj.mAudioChannelCount && mAudioSampleRate == obj.mAudioSampleRate - && mAudioDescription == obj.mAudioDescription; + && mAudioDescription == obj.mAudioDescription + && mHardOfHearing == obj.mHardOfHearing + && mSpokenSubtitle == obj.mSpokenSubtitle; case TYPE_VIDEO: return mVideoWidth == obj.mVideoWidth @@ -326,6 +371,9 @@ public final class TvTrackInfo implements Parcelable { && mVideoFrameRate == obj.mVideoFrameRate && mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio && mVideoActiveFormatDescription == obj.mVideoActiveFormatDescription; + + case TYPE_SUBTITLE: + return mHardOfHearing == obj.mHardOfHearing; } return true; @@ -361,6 +409,8 @@ public final class TvTrackInfo implements Parcelable { private int mAudioChannelCount; private int mAudioSampleRate; private boolean mAudioDescription; + private boolean mHardOfHearing; + private boolean mSpokenSubtitle; private int mVideoWidth; private int mVideoHeight; private float mVideoFrameRate; @@ -474,6 +524,46 @@ public final class TvTrackInfo implements Parcelable { } /** + * Sets the hard of hearing attribute of the track. Valid only for {@link #TYPE_AUDIO} and + * {@link #TYPE_SUBTITLE} tracks. + * + * <p>For example of broadcast, hard of hearing information may be referred to broadcast + * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio + * Language Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN + * 300 468). + * + * @param hardOfHearing The hard of hearing attribute of the track. + * @throws IllegalStateException if not called on an audio track or a subtitle track + */ + @NonNull + public Builder setHardOfHearing(boolean hardOfHearing) { + if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) { + throw new IllegalStateException("Not an audio track or a subtitle track"); + } + mHardOfHearing = hardOfHearing; + return this; + } + + /** + * Sets the spoken subtitle attribute of the audio. Valid only for {@link #TYPE_AUDIO} + * tracks. + * + * <p>For example of broadcast, spoken subtitle information may be referred to broadcast + * standard (e.g. Supplementary Audio Language Descriptor of ETSI EN 300 468). + * + * @param spokenSubtitle The spoken subtitle attribute of the audio. + * @throws IllegalStateException if not called on an audio track + */ + @NonNull + public Builder setSpokenSubtitle(boolean spokenSubtitle) { + if (mType != TYPE_AUDIO) { + throw new IllegalStateException("Not an audio track"); + } + mSpokenSubtitle = spokenSubtitle; + return this; + } + + /** * Sets the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO} * tracks. * @@ -575,9 +665,9 @@ public final class TvTrackInfo implements Parcelable { */ public TvTrackInfo build() { return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted, - mAudioChannelCount, mAudioSampleRate, mAudioDescription, mVideoWidth, - mVideoHeight, mVideoFrameRate, mVideoPixelAspectRatio, - mVideoActiveFormatDescription, mExtra); + mAudioChannelCount, mAudioSampleRate, mAudioDescription, mHardOfHearing, + mSpokenSubtitle, mVideoWidth, mVideoHeight, mVideoFrameRate, + mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra); } } } diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java new file mode 100644 index 000000000000..f9f7a22c3de8 --- /dev/null +++ b/media/java/android/media/tv/tuner/Descrambler.java @@ -0,0 +1,103 @@ +/* + * 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.annotation.Nullable; +import android.media.tv.tuner.Tuner.Filter; +import android.media.tv.tuner.TunerConstants.DemuxPidType; + +/** + * This class is used to interact with descramblers. + * + * <p> Descrambler is a hardware component used to descramble data. + * + * <p> This class controls the TIS interaction with Tuner HAL. + * @hide + */ +public class Descrambler implements AutoCloseable { + private long mNativeContext; + + private native int nativeAddPid(int pidType, int pid, Filter filter); + private native int nativeRemovePid(int pidType, int pid, Filter filter); + private native int nativeSetKeyToken(byte[] keyToken); + private native int nativeClose(); + + private Descrambler() {} + + /** + * Add packets' PID to the descrambler for descrambling. + * + * The descrambler will start descrambling packets with this PID. Multiple PIDs can be added + * into one descrambler instance because descambling can happen simultaneously on packets + * from different PIDs. + * + * If the Descrambler previously contained a filter for the PID, the old filter is replaced + * by the specified filter. + * + * @param pidType the type of the PID. + * @param pid the PID of packets to start to be descrambled. + * @param filter an optional filter instance to identify upper stream. + * @return result status of the operation. + * + * @hide + */ + public int addPid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) { + return nativeAddPid(pidType, pid, filter); + } + + /** + * Remove packets' PID from the descrambler + * + * The descrambler will stop descrambling packets with this PID. + * + * @param pidType the type of the PID. + * @param pid the PID of packets to stop to be descrambled. + * @param filter an optional filter instance to identify upper stream. + * @return result status of the operation. + * + * @hide + */ + public int removePid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) { + return nativeRemovePid(pidType, pid, filter); + } + + /** + * Set a key token to link descrambler to a key slot + * + * A descrambler instance can have only one key slot to link, but a key slot can hold a few + * keys for different purposes. + * + * @param keyToken the token to be used to link the key slot. + * @return result status of the operation. + * + * @hide + */ + public int setKeyToken(byte[] keyToken) { + return nativeSetKeyToken(keyToken); + } + + /** + * Release the descrambler instance. + * + * @hide + */ + @Override + public void close() { + nativeClose(); + } + +} diff --git a/media/java/android/media/tv/tuner/Dvr.java b/media/java/android/media/tv/tuner/Dvr.java new file mode 100644 index 000000000000..0bfba8f9d4f3 --- /dev/null +++ b/media/java/android/media/tv/tuner/Dvr.java @@ -0,0 +1,152 @@ +/* + * 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.annotation.NonNull; +import android.media.tv.tuner.Tuner.DvrCallback; +import android.media.tv.tuner.Tuner.Filter; +import android.os.ParcelFileDescriptor; + +/** @hide */ +public class Dvr { + private long mNativeContext; + private DvrCallback mCallback; + + private native int nativeAttachFilter(Filter filter); + private native int nativeDetachFilter(Filter filter); + private native int nativeConfigureDvr(DvrSettings settings); + private native int nativeStartDvr(); + private native int nativeStopDvr(); + private native int nativeFlushDvr(); + private native int nativeClose(); + private native void nativeSetFileDescriptor(int fd); + private native int nativeRead(int size); + private native int nativeRead(byte[] bytes, int offset, int size); + private native int nativeWrite(int size); + private native int nativeWrite(byte[] bytes, int offset, int size); + + private Dvr() {} + + /** + * Attaches a filter to DVR interface for recording. + * + * @param filter the filter to be attached. + * @return result status of the operation. + */ + public int attachFilter(Filter filter) { + return nativeAttachFilter(filter); + } + + /** + * Detaches a filter from DVR interface. + * + * @param filter the filter to be detached. + * @return result status of the operation. + */ + public int detachFilter(Filter filter) { + return nativeDetachFilter(filter); + } + + /** + * Configures the DVR. + * + * @param settings the settings of the DVR interface. + * @return result status of the operation. + */ + public int configure(DvrSettings settings) { + return nativeConfigureDvr(settings); + } + + /** + * Starts DVR. + * + * Starts consuming playback data or producing data for recording. + * + * @return result status of the operation. + */ + public int start() { + return nativeStartDvr(); + } + + /** + * Stops DVR. + * + * Stops consuming playback data or producing data for recording. + * + * @return result status of the operation. + */ + public int stop() { + return nativeStopDvr(); + } + + /** + * Flushed DVR data. + * + * @return result status of the operation. + */ + public int flush() { + return nativeFlushDvr(); + } + + /** + * closes the DVR instance to release resources. + * + * @return result status of the operation. + */ + public int close() { + return nativeClose(); + } + + /** + * Sets file descriptor to read/write data. + */ + public void setFileDescriptor(ParcelFileDescriptor fd) { + nativeSetFileDescriptor(fd.getFd()); + } + + /** + * Reads data from the file for DVR playback. + */ + public int read(int size) { + return nativeRead(size); + } + + /** + * Reads data from the buffer for DVR playback. + */ + public int read(@NonNull byte[] bytes, int offset, int size) { + if (size + offset > bytes.length) { + throw new ArrayIndexOutOfBoundsException( + "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size); + } + return nativeRead(bytes, offset, size); + } + + /** + * Writes recording data to file. + */ + public int write(int size) { + return nativeWrite(size); + } + + /** + * Writes recording data to buffer. + */ + public int write(@NonNull byte[] bytes, int offset, int size) { + return nativeWrite(bytes, offset, size); + } +} diff --git a/media/java/android/media/tv/tuner/Filter.java b/media/java/android/media/tv/tuner/Filter.java new file mode 100644 index 000000000000..db3b97afb1da --- /dev/null +++ b/media/java/android/media/tv/tuner/Filter.java @@ -0,0 +1,142 @@ +/* + * 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.annotation.NonNull; +import android.annotation.Nullable; +import android.media.tv.tuner.Tuner.FilterCallback; +import android.media.tv.tuner.filter.FilterConfiguration; +import android.media.tv.tuner.filter.Settings; + +/** + * Tuner data filter. + * + * <p> This class is used to filter wanted data according to the filter's configuration. + * @hide + */ +public class Filter implements AutoCloseable { + private long mNativeContext; + private FilterCallback mCallback; + int mId; + + private native int nativeConfigureFilter( + int type, int subType, FilterConfiguration settings); + private native int nativeGetId(); + private native int nativeSetDataSource(Tuner.Filter source); + private native int nativeStartFilter(); + private native int nativeStopFilter(); + private native int nativeFlushFilter(); + private native int nativeRead(byte[] buffer, int offset, int size); + private native int nativeClose(); + + private Filter(int id) { + mId = id; + } + + private void onFilterStatus(int status) { + } + + /** + * Configures the filter. + * + * @param settings the settings of the filter. + * @return result status of the operation. + * @hide + */ + public int configure(FilterConfiguration settings) { + int subType = -1; + Settings s = settings.getSettings(); + if (s != null) { + subType = s.getType(); + } + return nativeConfigureFilter(settings.getType(), subType, settings); + } + + /** + * Gets the filter Id. + * + * @return the hardware resource Id for the filter. + * @hide + */ + public int getId() { + return nativeGetId(); + } + + /** + * Sets the filter's data source. + * + * A filter uses demux as data source by default. If the data was packetized + * by multiple protocols, multiple filters may need to work together to + * extract all protocols' header. Then a filter's data source can be output + * from another filter. + * + * @param source the filter instance which provides data input. Switch to + * use demux as data source if the filter instance is NULL. + * @return result status of the operation. + * @hide + */ + public int setDataSource(@Nullable Tuner.Filter source) { + return nativeSetDataSource(source); + } + + /** + * Starts the filter. + * + * @return result status of the operation. + * @hide + */ + public int start() { + return nativeStartFilter(); + } + + + /** + * Stops the filter. + * + * @return result status of the operation. + * @hide + */ + public int stop() { + return nativeStopFilter(); + } + + /** + * Flushes the filter. + * + * @return result status of the operation. + * @hide + */ + public int flush() { + return nativeFlushFilter(); + } + + /** @hide */ + public int read(@NonNull byte[] buffer, int offset, int size) { + size = Math.min(size, buffer.length - offset); + return nativeRead(buffer, offset, size); + } + + /** + * Release the Filter instance. + * + * @hide + */ + @Override + public void close() { + nativeClose(); + } +} diff --git a/media/java/android/media/tv/tuner/FilterConfiguration.java b/media/java/android/media/tv/tuner/FilterConfiguration.java deleted file mode 100644 index b80a82a904b6..000000000000 --- a/media/java/android/media/tv/tuner/FilterConfiguration.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * 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.annotation.Nullable; -import android.media.tv.tuner.TunerConstants.FilterType; - -import java.util.List; - -/** - * Demux Filter configuration. - * - * @hide - */ -public abstract class FilterConfiguration { - @Nullable - protected final Settings mSettings; - - protected FilterConfiguration(Settings settings) { - mSettings = settings; - } - - /** - * Gets filter configuration type - */ - @FilterType - public abstract int getType(); - - public Settings getSettings() { - return mSettings; - } - - // TODO: more builders and getters - - /** - * Filter configuration for a TS filter. - */ - public static class TsFilterConfiguration extends FilterConfiguration { - private int mTpid; - - private TsFilterConfiguration(Settings settings, int tpid) { - super(settings); - mTpid = tpid; - } - - @Override - public int getType() { - return TunerConstants.FILTER_TYPE_TS; - } - - /** - * Creates a new builder. - */ - public static Builder newBuilder() { - return new Builder(); - } - - /** - * Builder for TsFilterConfiguration. - */ - public static class Builder { - private Settings mSettings; - private int mTpid; - - /** - * Sets settings. - */ - public Builder setSettings(Settings settings) { - mSettings = settings; - return this; - } - - /** - * Sets TPID. - */ - public Builder setTpid(int tpid) { - mTpid = tpid; - return this; - } - - /** - * Builds a TsFilterConfiguration instance. - */ - public TsFilterConfiguration build() { - return new TsFilterConfiguration(mSettings, mTpid); - } - } - } - - /** - * Filter configuration for a MMTP filter. - */ - public static class MmtpFilterConfiguration extends FilterConfiguration { - private int mMmtpPid; - - public MmtpFilterConfiguration(Settings settings) { - super(settings); - } - - @Override - public int getType() { - return TunerConstants.FILTER_TYPE_MMTP; - } - } - - - /** - * Filter configuration for a IP filter. - */ - public static class IpFilterConfiguration extends FilterConfiguration { - private byte[] mSrcIpAddress; - private byte[] mDstIpAddress; - private int mSrcPort; - private int mDstPort; - private boolean mPassthrough; - - public IpFilterConfiguration(Settings settings) { - super(settings); - } - - @Override - public int getType() { - return TunerConstants.FILTER_TYPE_IP; - } - } - - - /** - * Filter configuration for a TLV filter. - */ - public static class TlvFilterConfiguration extends FilterConfiguration { - private int mPacketType; - private boolean mIsCompressedIpPacket; - private boolean mPassthrough; - - public TlvFilterConfiguration(Settings settings) { - super(settings); - } - - @Override - public int getType() { - return TunerConstants.FILTER_TYPE_TLV; - } - } - - - /** - * Filter configuration for a ALP filter. - */ - public static class AlpFilterConfiguration extends FilterConfiguration { - private int mPacketType; - private int mLengthType; - - public AlpFilterConfiguration(Settings settings) { - super(settings); - } - - @Override - public int getType() { - return TunerConstants.FILTER_TYPE_ALP; - } - } - - - /** - * Settings for filters of different subtypes. - */ - public abstract static class Settings { - protected final int mType; - - protected Settings(int type) { - mType = type; - } - - /** - * Gets filter settings type. - * @return - */ - int getType() { - return mType; - } - } - - /** - * Filter Settings for Section data according to ISO/IEC 13818-1. - */ - public static class SectionSettings extends Settings { - - private SectionSettings(int mainType) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION)); - } - } - - /** - * Bits Settings for Section Filter. - */ - public static class SectionSettingsWithSectionBits extends SectionSettings { - private List<Byte> mFilter; - private List<Byte> mMask; - private List<Byte> mMode; - - private SectionSettingsWithSectionBits(int mainType) { - super(mainType); - } - } - - /** - * Table information for Section Filter. - */ - public static class SectionSettingsWithTableInfo extends SectionSettings { - private int mTableId; - private int mVersion; - - private SectionSettingsWithTableInfo(int mainType) { - super(mainType); - } - } - - /** - * Filter Settings for a PES Data. - */ - public static class PesSettings extends Settings { - private int mStreamId; - private boolean mIsRaw; - - private PesSettings(int mainType, int streamId, boolean isRaw) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES)); - mStreamId = streamId; - mIsRaw = isRaw; - } - - /** - * Creates a builder for PesSettings. - */ - public static Builder newBuilder(int mainType) { - return new Builder(mainType); - } - - /** - * Builder for PesSettings. - */ - public static class Builder { - private final int mMainType; - private int mStreamId; - private boolean mIsRaw; - - public Builder(int mainType) { - mMainType = mainType; - } - - /** - * Sets stream ID. - */ - public Builder setStreamId(int streamId) { - mStreamId = streamId; - return this; - } - - /** - * Sets whether it's raw. - * true if the filter send onFilterStatus instead of onFilterEvent. - */ - public Builder setIsRaw(boolean isRaw) { - mIsRaw = isRaw; - return this; - } - - /** - * Builds a PesSettings instance. - */ - public PesSettings build() { - return new PesSettings(mMainType, mStreamId, mIsRaw); - } - } - } - - /** - * Filter Settings for a Video and Audio. - */ - public static class AvSettings extends Settings { - private boolean mIsPassthrough; - - private AvSettings(int mainType, boolean isAudio) { - super(TunerUtils.getFilterSubtype( - mainType, - isAudio - ? TunerConstants.FILTER_SUBTYPE_AUDIO - : TunerConstants.FILTER_SUBTYPE_VIDEO)); - } - } - - /** - * Filter Settings for a Download. - */ - public static class DownloadSettings extends Settings { - private int mDownloadId; - - public DownloadSettings(int mainType) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD)); - } - } - - /** - * The Settings for the record in DVR. - */ - public static class RecordSettings extends Settings { - private int mIndexType; - private int mIndexMask; - - public RecordSettings(int mainType) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD)); - } - } - -} diff --git a/media/java/android/media/tv/tuner/FrontendCapabilities.java b/media/java/android/media/tv/tuner/FrontendCapabilities.java deleted file mode 100644 index fcfd7c8c8639..000000000000 --- a/media/java/android/media/tv/tuner/FrontendCapabilities.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * 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; - -/** - * Frontend Capabilities. - * @hide - */ -public class FrontendCapabilities { - /** Analog Capabilities. */ - public class Analog extends FrontendCapabilities { - private final int mTypeCap; - private final int mSifStandardCap; - - Analog(int typeCap, int sifStandardCap) { - mTypeCap = typeCap; - mSifStandardCap = sifStandardCap; - } - /** - * Gets type capability. - */ - public int getTypeCapability() { - return mTypeCap; - } - /** Gets SIF standard capability. */ - public int getSifStandardCapability() { - return mSifStandardCap; - } - } - - /** ATSC Capabilities. */ - public class Atsc extends FrontendCapabilities { - private final int mModulationCap; - - Atsc(int modulationCap) { - mModulationCap = modulationCap; - } - /** Gets modulation capability. */ - public int getModulationCapability() { - return mModulationCap; - } - } - - /** ATSC-3 Capabilities. */ - public class Atsc3 extends FrontendCapabilities { - private final int mBandwidthCap; - private final int mModulationCap; - private final int mTimeInterleaveModeCap; - private final int mCodeRateCap; - private final int mFecCap; - private final int mDemodOutputFormatCap; - - Atsc3(int bandwidthCap, int modulationCap, int timeInterleaveModeCap, int codeRateCap, - int fecCap, int demodOutputFormatCap) { - mBandwidthCap = bandwidthCap; - mModulationCap = modulationCap; - mTimeInterleaveModeCap = timeInterleaveModeCap; - mCodeRateCap = codeRateCap; - mFecCap = fecCap; - mDemodOutputFormatCap = demodOutputFormatCap; - } - - /** Gets bandwidth capability. */ - public int getBandwidthCapability() { - return mBandwidthCap; - } - /** Gets modulation capability. */ - public int getModulationCapability() { - return mModulationCap; - } - /** Gets time interleave mod capability. */ - public int getTimeInterleaveModeCapability() { - return mTimeInterleaveModeCap; - } - /** Gets code rate capability. */ - public int getCodeRateCapability() { - return mCodeRateCap; - } - /** Gets FEC capability. */ - public int getFecCapability() { - return mFecCap; - } - /** Gets demodulator output format capability. */ - public int getDemodOutputFormatCapability() { - return mDemodOutputFormatCap; - } - } - - /** DVBS Capabilities. */ - public class Dvbs extends FrontendCapabilities { - private final int mModulationCap; - private final long mInnerFecCap; - private final int mStandard; - - Dvbs(int modulationCap, long innerFecCap, int standard) { - mModulationCap = modulationCap; - mInnerFecCap = innerFecCap; - mStandard = standard; - } - - /** Gets modulation capability. */ - public int getModulationCapability() { - return mModulationCap; - } - /** Gets inner FEC capability. */ - public long getInnerFecCapability() { - return mInnerFecCap; - } - /** Gets DVBS standard capability. */ - public int getStandardCapability() { - return mStandard; - } - } - - /** DVBC Capabilities. */ - public class Dvbc extends FrontendCapabilities { - private final int mModulationCap; - private final int mFecCap; - private final int mAnnexCap; - - Dvbc(int modulationCap, int fecCap, int annexCap) { - mModulationCap = modulationCap; - mFecCap = fecCap; - mAnnexCap = annexCap; - } - - /** Gets modulation capability. */ - public int getModulationCapability() { - return mModulationCap; - } - /** Gets FEC capability. */ - public int getFecCapability() { - return mFecCap; - } - /** Gets annex capability. */ - public int getAnnexCapability() { - return mAnnexCap; - } - } - - /** DVBT Capabilities. */ - public class Dvbt extends FrontendCapabilities { - private final int mTransmissionModeCap; - private final int mBandwidthCap; - private final int mConstellationCap; - private final int mCoderateCap; - private final int mHierarchyCap; - private final int mGuardIntervalCap; - private final boolean mIsT2Supported; - private final boolean mIsMisoSupported; - - Dvbt(int transmissionModeCap, int bandwidthCap, int constellationCap, int coderateCap, - int hierarchyCap, int guardIntervalCap, boolean isT2Supported, - boolean isMisoSupported) { - mTransmissionModeCap = transmissionModeCap; - mBandwidthCap = bandwidthCap; - mConstellationCap = constellationCap; - mCoderateCap = coderateCap; - mHierarchyCap = hierarchyCap; - mGuardIntervalCap = guardIntervalCap; - mIsT2Supported = isT2Supported; - mIsMisoSupported = isMisoSupported; - } - - /** Gets transmission mode capability. */ - public int getTransmissionModeCapability() { - return mTransmissionModeCap; - } - /** Gets bandwidth capability. */ - public int getBandwidthCapability() { - return mBandwidthCap; - } - /** Gets constellation capability. */ - public int getConstellationCapability() { - return mConstellationCap; - } - /** Gets code rate capability. */ - public int getCodeRateCapability() { - return mCoderateCap; - } - /** Gets hierarchy capability. */ - public int getHierarchyCapability() { - return mHierarchyCap; - } - /** Gets guard interval capability. */ - public int getGuardIntervalCapability() { - return mGuardIntervalCap; - } - /** Returns whether T2 is supported. */ - public boolean getIsT2Supported() { - return mIsT2Supported; - } - /** Returns whether MISO is supported. */ - public boolean getIsMisoSupported() { - return mIsMisoSupported; - } - } - - /** ISDBS Capabilities. */ - public class Isdbs extends FrontendCapabilities { - private final int mModulationCap; - private final int mCoderateCap; - - Isdbs(int modulationCap, int coderateCap) { - mModulationCap = modulationCap; - mCoderateCap = coderateCap; - } - - /** Gets modulation capability. */ - public int getModulationCapability() { - return mModulationCap; - } - /** Gets code rate capability. */ - public int getCodeRateCapability() { - return mCoderateCap; - } - } - - /** ISDBS-3 Capabilities. */ - public class Isdbs3 extends FrontendCapabilities { - private final int mModulationCap; - private final int mCoderateCap; - - Isdbs3(int modulationCap, int coderateCap) { - mModulationCap = modulationCap; - mCoderateCap = coderateCap; - } - - /** Gets modulation capability. */ - public int getModulationCapability() { - return mModulationCap; - } - /** Gets code rate capability. */ - public int getCodeRateCapability() { - return mCoderateCap; - } - } - - /** ISDBC Capabilities. */ - public class Isdbc extends FrontendCapabilities { - private final int mModeCap; - private final int mBandwidthCap; - private final int mModulationCap; - private final int mCoderateCap; - private final int mGuardIntervalCap; - - Isdbc(int modeCap, int bandwidthCap, int modulationCap, int coderateCap, - int guardIntervalCap) { - mModeCap = modeCap; - mBandwidthCap = bandwidthCap; - mModulationCap = modulationCap; - mCoderateCap = coderateCap; - mGuardIntervalCap = guardIntervalCap; - } - - /** Gets mode capability. */ - public int getModeCapability() { - return mModeCap; - } - /** Gets bandwidth capability. */ - public int getBandwidthCapability() { - return mBandwidthCap; - } - /** Gets modulation capability. */ - public int getModulationCapability() { - return mModulationCap; - } - /** Gets code rate capability. */ - public int getCodeRateCapability() { - return mCoderateCap; - } - /** Gets guard interval capability. */ - public int getGuardIntervalCapability() { - return mGuardIntervalCap; - } - } -} diff --git a/media/java/android/media/tv/tuner/TimeFilter.java b/media/java/android/media/tv/tuner/TimeFilter.java new file mode 100644 index 000000000000..8bd0d26ce951 --- /dev/null +++ b/media/java/android/media/tv/tuner/TimeFilter.java @@ -0,0 +1,127 @@ +/* + * 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.annotation.Nullable; +import android.media.tv.tuner.TunerConstants.Result; + +/** + * A timer filter is used to filter data based on timestamps. + * + * <p> If the timestamp is set, data is discarded if its timestamp is smaller than the + * timestamp in this time filter. + * + * <p> The format of the timestamps is the same as PTS defined in ISO/IEC 13818-1:2019. The + * timestamps may or may not be related to PTS or DTS. + * + * @hide + */ +public class TimeFilter implements AutoCloseable { + private native int nativeSetTimestamp(long timestamp); + private native int nativeClearTimestamp(); + private native Long nativeGetTimestamp(); + private native Long nativeGetSourceTime(); + private native int nativeClose(); + + private boolean mEnable = false; + + /** + * Set timestamp for time based filter. + * + * It is used to set initial timestamp and enable time filtering. Once set, the time will be + * increased automatically like a clock. Contents are discarded if their timestamps are + * older than the time in the time filter. + * + * This method can be called more than once to reset the initial timestamp. + * + * @param timestamp initial timestamp for the time filter before it's increased. It's + * based on the 90KHz counter, and it's the same format as PTS (Presentation Time Stamp) + * defined in ISO/IEC 13818-1:2019. The timestamps may or may not be related to PTS or DTS. + * @return result status of the operation. + */ + @Result + public int setCurrentTimestamp(long timestamp) { + int res = nativeSetTimestamp(timestamp); + // TODO: use a constant for SUCCESS + if (res == 0) { + mEnable = true; + } + return res; + } + + /** + * Clear the timestamp in the time filter. + * + * It is used to clear the time value of the time filter. Time filtering is disabled then. + * + * @return result status of the operation. + */ + @Result + public int clearTimestamp() { + int res = nativeClearTimestamp(); + if (res == 0) { + mEnable = false; + } + return res; + } + + /** + * Get the current time in the time filter. + * + * It is used to inquiry current time in the time filter. + * + * @return current timestamp in the time filter. It's based on the 90KHz counter, and it's + * the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019. The + * timestamps may or may not be related to PTS or DTS. {@code null} if the timestamp is + * never set. + */ + @Nullable + public Long getTimeStamp() { + if (!mEnable) { + return null; + } + return nativeGetTimestamp(); + } + + /** + * Get the timestamp from the beginning of incoming data stream. + * + * It is used to inquiry the timestamp from the beginning of incoming data stream. + * + * @return first timestamp of incoming data stream. It's based on the 90KHz counter, and + * it's the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019. + * The timestamps may or may not be related to PTS or DTS. + */ + @Nullable + public Long getSourceTime() { + if (!mEnable) { + return null; + } + return nativeGetSourceTime(); + } + + /** + * Close the Time Filter instance + * + * It is to release the TimeFilter instance. Resources are reclaimed so the instance must + * not be accessed after this method is called. + */ + @Override + public void close() { + nativeClose(); + } +} diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 43f9a8973e80..962a7f6d58f6 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -21,16 +21,14 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; -import android.media.tv.tuner.FilterConfiguration.Settings; -import android.media.tv.tuner.TunerConstants.DemuxPidType; import android.media.tv.tuner.TunerConstants.FilterStatus; import android.media.tv.tuner.TunerConstants.FilterSubtype; -import android.media.tv.tuner.TunerConstants.FilterType; import android.media.tv.tuner.TunerConstants.FrontendScanType; import android.media.tv.tuner.TunerConstants.LnbPosition; import android.media.tv.tuner.TunerConstants.LnbTone; import android.media.tv.tuner.TunerConstants.LnbVoltage; import android.media.tv.tuner.TunerConstants.Result; +import android.media.tv.tuner.filter.FilterConfiguration.FilterType; import android.media.tv.tuner.filter.FilterEvent; import android.media.tv.tuner.frontend.FrontendCallback; import android.media.tv.tuner.frontend.FrontendInfo; @@ -39,7 +37,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import java.io.FileDescriptor; import java.util.List; /** @@ -123,6 +120,7 @@ public final class Tuner implements AutoCloseable { private native int nativeDisconnectCiCam(); private native FrontendInfo nativeGetFrontendInfo(int id); private native Filter nativeOpenFilter(int type, int subType, int bufferSize); + private native TimeFilter nativeOpenTimeFilter(); private native List<Integer> nativeGetLnbIds(); private native Lnb nativeOpenLnbById(int id); @@ -456,122 +454,11 @@ public final class Tuner implements AutoCloseable { * Tuner data filter. * * <p> This class is used to filter wanted data according to the filter's configuration. + * TODO: remove */ public class Filter { - private long mNativeContext; - private FilterCallback mCallback; - int mId; - - private native int nativeConfigureFilter( - int type, int subType, FilterConfiguration settings); - private native int nativeGetId(); - private native int nativeSetDataSource(Filter source); - private native int nativeStartFilter(); - private native int nativeStopFilter(); - private native int nativeFlushFilter(); - private native int nativeRead(byte[] buffer, int offset, int size); - private native int nativeClose(); - - private Filter(int id) { - mId = id; - } - - private void onFilterStatus(int status) { - if (mHandler != null) { - mHandler.sendMessage( - mHandler.obtainMessage(MSG_ON_FILTER_STATUS, status, 0, this)); - } - } - - /** - * Configures the filter. - * - * @param settings the settings of the filter. - * @return result status of the operation. - * @hide - */ - public int configure(FilterConfiguration settings) { - int subType = -1; - Settings s = settings.getSettings(); - if (s != null) { - subType = s.getType(); - } - return nativeConfigureFilter(settings.getType(), subType, settings); - } - - /** - * Gets the filter Id. - * - * @return the hardware resource Id for the filter. - * @hide - */ - public int getId() { - return nativeGetId(); - } - - /** - * Sets the filter's data source. - * - * A filter uses demux as data source by default. If the data was packetized - * by multiple protocols, multiple filters may need to work together to - * extract all protocols' header. Then a filter's data source can be output - * from another filter. - * - * @param source the filter instance which provides data input. Switch to - * use demux as data source if the filter instance is NULL. - * @return result status of the operation. - * @hide - */ - public int setDataSource(@Nullable Filter source) { - return nativeSetDataSource(source); - } - - /** - * Starts the filter. - * - * @return result status of the operation. - * @hide - */ - public int start() { - return nativeStartFilter(); - } - - - /** - * Stops the filter. - * - * @return result status of the operation. - * @hide - */ - public int stop() { - return nativeStopFilter(); - } - - /** - * Flushes the filter. - * - * @return result status of the operation. - * @hide - */ - public int flush() { - return nativeFlushFilter(); - } - - /** @hide */ - public int read(@NonNull byte[] buffer, int offset, int size) { - size = Math.min(size, buffer.length - offset); - return nativeRead(buffer, offset, size); - } - - /** - * Release the Filter instance. - * - * @return result status of the operation. - * @hide - */ - public int close() { - return nativeClose(); - } + FilterCallback mCallback; + private Filter() {} } private Filter openFilter(@FilterType int mainType, @FilterSubtype int subType, int bufferSize, @@ -587,6 +474,18 @@ public final class Tuner implements AutoCloseable { return filter; } + /** + * Open a time filter instance. + * + * It is used to open time filter of demux. + * + * @return a time filter instance. + * @hide + */ + public TimeFilter openTimeFilter() { + return nativeOpenTimeFilter(); + } + /** @hide */ public class Lnb { private int mId; @@ -699,81 +598,11 @@ public final class Tuner implements AutoCloseable { * <p> Descrambler is a hardware component used to descramble data. * * <p> This class controls the TIS interaction with Tuner HAL. - * TODO: make it static and extends Closable. + * TODO: Remove */ public class Descrambler { - private long mNativeContext; - - private native int nativeAddPid(int pidType, int pid, Filter filter); - private native int nativeRemovePid(int pidType, int pid, Filter filter); - private native int nativeSetKeyToken(byte[] keyToken); - private native int nativeClose(); - - private Descrambler() {} - - /** - * Add packets' PID to the descrambler for descrambling. - * - * The descrambler will start descrambling packets with this PID. Multiple PIDs can be added - * into one descrambler instance because descambling can happen simultaneously on packets - * from different PIDs. - * - * If the Descrambler previously contained a filter for the PID, the old filter is replaced - * by the specified filter. - * - * @param pidType the type of the PID. - * @param pid the PID of packets to start to be descrambled. - * @param filter an optional filter instance to identify upper stream. - * @return result status of the operation. - * - * @hide - */ - public int addPid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) { - return nativeAddPid(pidType, pid, filter); + private Descrambler() { } - - /** - * Remove packets' PID from the descrambler - * - * The descrambler will stop descrambling packets with this PID. - * - * @param pidType the type of the PID. - * @param pid the PID of packets to stop to be descrambled. - * @param filter an optional filter instance to identify upper stream. - * @return result status of the operation. - * - * @hide - */ - public int removePid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) { - return nativeRemovePid(pidType, pid, filter); - } - - /** - * Set a key token to link descrambler to a key slot - * - * A descrambler instance can have only one key slot to link, but a key slot can hold a few - * keys for different purposes. - * - * @param keyToken the token to be used to link the key slot. - * @return result status of the operation. - * - * @hide - */ - public int setKeyToken(byte[] keyToken) { - return nativeSetKeyToken(keyToken); - } - - /** - * Release the descrambler instance. - * - * @return result status of the operation. - * - * @hide - */ - public int close() { - return nativeClose(); - } - } /** @@ -788,137 +617,6 @@ public final class Tuner implements AutoCloseable { return nativeOpenDescrambler(); } - // TODO: consider splitting Dvr to Playback and Recording - /** @hide */ - public class Dvr { - private long mNativeContext; - private DvrCallback mCallback; - - private native int nativeAttachFilter(Filter filter); - private native int nativeDetachFilter(Filter filter); - private native int nativeConfigureDvr(DvrSettings settings); - private native int nativeStartDvr(); - private native int nativeStopDvr(); - private native int nativeFlushDvr(); - private native int nativeClose(); - private native void nativeSetFileDescriptor(FileDescriptor fd); - private native int nativeRead(int size); - private native int nativeRead(byte[] bytes, int offset, int size); - private native int nativeWrite(int size); - private native int nativeWrite(byte[] bytes, int offset, int size); - - private Dvr() {} - - /** - * Attaches a filter to DVR interface for recording. - * - * @param filter the filter to be attached. - * @return result status of the operation. - */ - public int attachFilter(Filter filter) { - return nativeAttachFilter(filter); - } - - /** - * Detaches a filter from DVR interface. - * - * @param filter the filter to be detached. - * @return result status of the operation. - */ - public int detachFilter(Filter filter) { - return nativeDetachFilter(filter); - } - - /** - * Configures the DVR. - * - * @param settings the settings of the DVR interface. - * @return result status of the operation. - */ - public int configure(DvrSettings settings) { - return nativeConfigureDvr(settings); - } - - /** - * Starts DVR. - * - * Starts consuming playback data or producing data for recording. - * - * @return result status of the operation. - */ - public int start() { - return nativeStartDvr(); - } - - /** - * Stops DVR. - * - * Stops consuming playback data or producing data for recording. - * - * @return result status of the operation. - */ - public int stop() { - return nativeStopDvr(); - } - - /** - * Flushed DVR data. - * - * @return result status of the operation. - */ - public int flush() { - return nativeFlushDvr(); - } - - /** - * closes the DVR instance to release resources. - * - * @return result status of the operation. - */ - public int close() { - return nativeClose(); - } - - /** - * Sets file descriptor to read/write data. - */ - public void setFileDescriptor(FileDescriptor fd) { - nativeSetFileDescriptor(fd); - } - - /** - * Reads data from the file for DVR playback. - */ - public int read(int size) { - return nativeRead(size); - } - - /** - * Reads data from the buffer for DVR playback. - */ - public int read(@NonNull byte[] bytes, int offset, int size) { - if (size + offset > bytes.length) { - throw new ArrayIndexOutOfBoundsException( - "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size); - } - return nativeRead(bytes, offset, size); - } - - /** - * Writes recording data to file. - */ - public int write(int size) { - return nativeWrite(size); - } - - /** - * Writes recording data to buffer. - */ - public int write(@NonNull byte[] bytes, int offset, int size) { - return nativeWrite(bytes, offset, size); - } - } - private Dvr openDvr(int type, int bufferSize) { Dvr dvr = nativeOpenDvr(type, bufferSize); return dvr; diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index d24e582a8b21..bbaa5180aece 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -128,21 +128,6 @@ public final class TunerConstants { public static final int FRONTEND_SETTINGS_ISDBT = 9; /** @hide */ - @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP}) - @Retention(RetentionPolicy.SOURCE) - public @interface FilterType {} - /** @hide */ - public static final int FILTER_TYPE_TS = Constants.DemuxFilterMainType.TS; - /** @hide */ - public static final int FILTER_TYPE_MMTP = Constants.DemuxFilterMainType.MMTP; - /** @hide */ - public static final int FILTER_TYPE_IP = Constants.DemuxFilterMainType.IP; - /** @hide */ - public static final int FILTER_TYPE_TLV = Constants.DemuxFilterMainType.TLV; - /** @hide */ - public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP; - - /** @hide */ @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, @@ -227,41 +212,6 @@ public final class TunerConstants { /** @hide */ public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND; - /** @hide */ - @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}) - @Retention(RetentionPolicy.SOURCE) - public @interface ScanMessageType {} - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_LOCKED = Constants.FrontendScanMessageType.LOCKED; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_END = Constants.FrontendScanMessageType.END; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_PROGRESS_PERCENT = - Constants.FrontendScanMessageType.PROGRESS_PERCENT; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_FREQUENCY = - Constants.FrontendScanMessageType.FREQUENCY; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_SYMBOL_RATE = - Constants.FrontendScanMessageType.SYMBOL_RATE; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_GROUP_IDS = - Constants.FrontendScanMessageType.GROUP_IDS; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS = - Constants.FrontendScanMessageType.INPUT_STREAM_IDS; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_STANDARD = - Constants.FrontendScanMessageType.STANDARD; - /** @hide */ - public static final int SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO = - Constants.FrontendScanMessageType.ATSC3_PLP_INFO; - /** @hide */ @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER, diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java index a7ccb0288e03..8780b726e3b2 100644 --- a/media/java/android/media/tv/tuner/TunerUtils.java +++ b/media/java/android/media/tv/tuner/TunerUtils.java @@ -20,7 +20,8 @@ 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; +import android.media.tv.tuner.filter.FilterConfiguration; +import android.media.tv.tuner.filter.FilterConfiguration.FilterType; /** * Utility class for tuner framework. @@ -50,7 +51,7 @@ public final class TunerUtils { * @param subtype filter subtype. */ public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) { - if (mainType == TunerConstants.FILTER_TYPE_TS) { + if (mainType == FilterConfiguration.FILTER_TYPE_TS) { switch (subtype) { case TunerConstants.FILTER_SUBTYPE_UNDEFINED: return Constants.DemuxTsFilterType.UNDEFINED; @@ -73,7 +74,7 @@ public final class TunerUtils { default: break; } - } else if (mainType == TunerConstants.FILTER_TYPE_MMTP) { + } else if (mainType == FilterConfiguration.FILTER_TYPE_MMTP) { switch (subtype) { case TunerConstants.FILTER_SUBTYPE_UNDEFINED: return Constants.DemuxMmtpFilterType.UNDEFINED; @@ -95,7 +96,7 @@ public final class TunerUtils { break; } - } else if (mainType == TunerConstants.FILTER_TYPE_IP) { + } else if (mainType == FilterConfiguration.FILTER_TYPE_IP) { switch (subtype) { case TunerConstants.FILTER_SUBTYPE_UNDEFINED: return Constants.DemuxIpFilterType.UNDEFINED; @@ -112,7 +113,7 @@ public final class TunerUtils { default: break; } - } else if (mainType == TunerConstants.FILTER_TYPE_TLV) { + } else if (mainType == FilterConfiguration.FILTER_TYPE_TLV) { switch (subtype) { case TunerConstants.FILTER_SUBTYPE_UNDEFINED: return Constants.DemuxTlvFilterType.UNDEFINED; @@ -125,7 +126,7 @@ public final class TunerUtils { default: break; } - } else if (mainType == TunerConstants.FILTER_TYPE_ALP) { + } else if (mainType == FilterConfiguration.FILTER_TYPE_ALP) { switch (subtype) { case TunerConstants.FILTER_SUBTYPE_UNDEFINED: return Constants.DemuxAlpFilterType.UNDEFINED; diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java new file mode 100644 index 000000000000..f0fe533093ba --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java @@ -0,0 +1,35 @@ +/* + * 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.filter; + +/** + * Filter configuration for a ALP filter. + * @hide + */ +public class AlpFilterConfiguration extends FilterConfiguration { + private int mPacketType; + private int mLengthType; + + public AlpFilterConfiguration(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return FilterConfiguration.FILTER_TYPE_ALP; + } +} diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java new file mode 100644 index 000000000000..a7c49d5585f8 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/AvSettings.java @@ -0,0 +1,36 @@ +/* + * 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.filter; + +import android.media.tv.tuner.TunerConstants; +import android.media.tv.tuner.TunerUtils; + +/** + * Filter Settings for a Video and Audio. + * @hide + */ +public class AvSettings extends Settings { + private boolean mIsPassthrough; + + private AvSettings(int mainType, boolean isAudio) { + super(TunerUtils.getFilterSubtype( + mainType, + isAudio + ? TunerConstants.FILTER_SUBTYPE_AUDIO + : TunerConstants.FILTER_SUBTYPE_VIDEO)); + } +} diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java new file mode 100644 index 000000000000..0742b1166ede --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java @@ -0,0 +1,32 @@ +/* + * 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.filter; + +import android.media.tv.tuner.TunerConstants; +import android.media.tv.tuner.TunerUtils; + +/** + * Filter Settings for a Download. + * @hide + */ +public class DownloadSettings extends Settings { + private int mDownloadId; + + public DownloadSettings(int mainType) { + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD)); + } +} diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java new file mode 100644 index 000000000000..99b10cde34f9 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java @@ -0,0 +1,78 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.filter; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.hardware.tv.tuner.V1_0.Constants; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Filter configuration used to configure filters. + * + * @hide + */ +public abstract class FilterConfiguration { + + /** @hide */ + @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP}) + @Retention(RetentionPolicy.SOURCE) + public @interface FilterType {} + + /** + * TS filter type. + */ + public static final int FILTER_TYPE_TS = Constants.DemuxFilterMainType.TS; + /** + * MMTP filter type. + */ + public static final int FILTER_TYPE_MMTP = Constants.DemuxFilterMainType.MMTP; + /** + * IP filter type. + */ + public static final int FILTER_TYPE_IP = Constants.DemuxFilterMainType.IP; + /** + * TLV filter type. + */ + public static final int FILTER_TYPE_TLV = Constants.DemuxFilterMainType.TLV; + /** + * ALP filter type. + */ + public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP; + + @Nullable + private final Settings mSettings; + + /* package */ FilterConfiguration(Settings settings) { + mSettings = settings; + } + + /** + * Gets filter configuration type. + * @hide + */ + @FilterType + public abstract int getType(); + + /** @hide */ + @Nullable + public Settings getSettings() { + return mSettings; + } +} diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java new file mode 100644 index 000000000000..c89636887628 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java @@ -0,0 +1,38 @@ +/* + * 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.filter; + +/** + * Filter configuration for a IP filter. + * @hide + */ +public class IpFilterConfiguration extends FilterConfiguration { + private byte[] mSrcIpAddress; + private byte[] mDstIpAddress; + private int mSrcPort; + private int mDstPort; + private boolean mPassthrough; + + public IpFilterConfiguration(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return FilterConfiguration.FILTER_TYPE_IP; + } +} diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java new file mode 100644 index 000000000000..9045ce67a61f --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java @@ -0,0 +1,34 @@ +/* + * 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.filter; + +/** + * Filter configuration for a MMTP filter. + * @hide + */ +public class MmtpFilterConfiguration extends FilterConfiguration { + private int mMmtpPid; + + public MmtpFilterConfiguration(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return FilterConfiguration.FILTER_TYPE_MMTP; + } +} diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java new file mode 100644 index 000000000000..f38abf12e120 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/PesSettings.java @@ -0,0 +1,92 @@ +/* + * 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.filter; + +import android.annotation.NonNull; +import android.media.tv.tuner.TunerConstants; +import android.media.tv.tuner.TunerUtils; +import android.media.tv.tuner.filter.FilterConfiguration.FilterType; + +/** + * Filter Settings for a PES Data. + * + * @hide + */ +public class PesSettings extends Settings { + private final int mStreamId; + private final boolean mIsRaw; + + private PesSettings(@FilterType int mainType, int streamId, boolean isRaw) { + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES)); + mStreamId = streamId; + mIsRaw = isRaw; + } + + /** + * Creates a builder for {@link PesSettings}. + * + * @param mainType the filter main type of the settings. + */ + @NonNull + public static Builder newBuilder(@FilterType int mainType) { + return new Builder(mainType); + } + + /** + * Builder for {@link PesSettings}. + */ + public static class Builder { + private final int mMainType; + private int mStreamId; + private boolean mIsRaw; + + private Builder(int mainType) { + mMainType = mainType; + } + + /** + * Sets stream ID. + * + * @param streamId the stream ID. + */ + @NonNull + public Builder setStreamId(int streamId) { + mStreamId = streamId; + return this; + } + + /** + * Sets whether it's raw. + * + * @param isRaw {@code true} if the data is raw. Filter sends onFilterStatus callback + * instead of onFilterEvent for raw data. {@code false} otherwise. + */ + @NonNull + public Builder setIsRaw(boolean isRaw) { + mIsRaw = isRaw; + return this; + } + + /** + * Builds a {@link PesSettings} object. + */ + @NonNull + public PesSettings build() { + return new PesSettings(mMainType, mStreamId, mIsRaw); + } + } +} diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java new file mode 100644 index 000000000000..701868afc789 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java @@ -0,0 +1,33 @@ +/* + * 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.filter; + +import android.media.tv.tuner.TunerConstants; +import android.media.tv.tuner.TunerUtils; + +/** + * The Settings for the record in DVR. + * @hide + */ +public class RecordSettings extends Settings { + private int mIndexType; + private int mIndexMask; + + public RecordSettings(int mainType) { + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD)); + } +} diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java new file mode 100644 index 000000000000..36e3d7cfee43 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java @@ -0,0 +1,31 @@ +/* + * 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.filter; + +import android.media.tv.tuner.TunerConstants; +import android.media.tv.tuner.TunerUtils; + +/** + * Filter Settings for Section data according to ISO/IEC 13818-1. + * @hide + */ +public class SectionSettings extends Settings { + + SectionSettings(int mainType) { + super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION)); + } +} diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java new file mode 100644 index 000000000000..414ea6790bf5 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java @@ -0,0 +1,33 @@ +/* + * 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.filter; + +import java.util.List; + +/** + * Bits Settings for Section Filter. + * @hide + */ +public class SectionSettingsWithSectionBits extends SectionSettings { + private List<Byte> mFilter; + private List<Byte> mMask; + private List<Byte> mMode; + + private SectionSettingsWithSectionBits(int mainType) { + super(mainType); + } +} diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java new file mode 100644 index 000000000000..0df1d7308e60 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java @@ -0,0 +1,30 @@ +/* + * 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.filter; + +/** + * Table information for Section Filter. + * @hide + */ +public class SectionSettingsWithTableInfo extends SectionSettings { + private int mTableId; + private int mVersion; + + private SectionSettingsWithTableInfo(int mainType) { + super(mainType); + } +} diff --git a/media/java/android/media/tv/tuner/filter/Settings.java b/media/java/android/media/tv/tuner/filter/Settings.java new file mode 100644 index 000000000000..146aca74ce0e --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/Settings.java @@ -0,0 +1,39 @@ +/* + * 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.filter; + +/** + * Settings for filters of different subtypes. + * + * @hide + */ +public abstract class Settings { + private final int mType; + + /* package */ Settings(int type) { + mType = type; + } + + /** + * Gets filter settings type. + * + * @hide + */ + public int getType() { + return mType; + } +} diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java new file mode 100644 index 000000000000..de8ee754a28c --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java @@ -0,0 +1,36 @@ +/* + * 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.filter; + +/** + * Filter configuration for a TLV filter. + * @hide + */ +public class TlvFilterConfiguration extends FilterConfiguration { + private int mPacketType; + private boolean mIsCompressedIpPacket; + private boolean mPassthrough; + + public TlvFilterConfiguration(Settings settings) { + super(settings); + } + + @Override + public int getType() { + return FilterConfiguration.FILTER_TYPE_TLV; + } +} diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java new file mode 100644 index 000000000000..d0241b6aba09 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java @@ -0,0 +1,84 @@ +/* + * 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.filter; + +import android.annotation.NonNull; + +/** + * Filter configuration for a TS filter. + * + * @hide + */ +public class TsFilterConfiguration extends FilterConfiguration { + private final int mTpid; + + private TsFilterConfiguration(Settings settings, int tpid) { + super(settings); + mTpid = tpid; + } + + @Override + public int getType() { + return FilterConfiguration.FILTER_TYPE_TS; + } + + /** + * Creates a builder for {@link TsFilterConfiguration}. + */ + @NonNull + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builder for {@link TsFilterConfiguration}. + */ + public static class Builder { + private Settings mSettings; + private int mTpid; + + /** + * Sets filter settings. + * + * @param settings the filter settings. + */ + @NonNull + public Builder setSettings(@NonNull Settings settings) { + mSettings = settings; + return this; + } + + /** + * Sets Tag Protocol ID. + * + * @param tpid the Tag Protocol ID. + */ + @NonNull + public Builder setTpid(int tpid) { + mTpid = tpid; + return this; + } + + /** + * Builds a {@link TsFilterConfiguration} object. + */ + @NonNull + public TsFilterConfiguration build() { + return new TsFilterConfiguration(mSettings, mTpid); + } + } +} diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java new file mode 100644 index 000000000000..2962e98790e5 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java @@ -0,0 +1,41 @@ +/* + * 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.frontend; + +/** + * Analog Capabilities. + * @hide + */ +public class AnalogFrontendCapabilities extends FrontendCapabilities { + private final int mTypeCap; + private final int mSifStandardCap; + + AnalogFrontendCapabilities(int typeCap, int sifStandardCap) { + mTypeCap = typeCap; + mSifStandardCap = sifStandardCap; + } + /** + * Gets type capability. + */ + public int getTypeCapability() { + return mTypeCap; + } + /** Gets SIF standard capability. */ + public int getSifStandardCapability() { + return mSifStandardCap; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java new file mode 100644 index 000000000000..677f9387c6d2 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java @@ -0,0 +1,65 @@ +/* + * 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.frontend; + +/** + * ATSC-3 Capabilities. + * @hide + */ +public class Atsc3FrontendCapabilities extends FrontendCapabilities { + private final int mBandwidthCap; + private final int mModulationCap; + private final int mTimeInterleaveModeCap; + private final int mCodeRateCap; + private final int mFecCap; + private final int mDemodOutputFormatCap; + + Atsc3FrontendCapabilities(int bandwidthCap, int modulationCap, int timeInterleaveModeCap, + int codeRateCap, int fecCap, int demodOutputFormatCap) { + mBandwidthCap = bandwidthCap; + mModulationCap = modulationCap; + mTimeInterleaveModeCap = timeInterleaveModeCap; + mCodeRateCap = codeRateCap; + mFecCap = fecCap; + mDemodOutputFormatCap = demodOutputFormatCap; + } + + /** Gets bandwidth capability. */ + public int getBandwidthCapability() { + return mBandwidthCap; + } + /** Gets modulation capability. */ + public int getModulationCapability() { + return mModulationCap; + } + /** Gets time interleave mod capability. */ + public int getTimeInterleaveModeCapability() { + return mTimeInterleaveModeCap; + } + /** Gets code rate capability. */ + public int getCodeRateCapability() { + return mCodeRateCap; + } + /** Gets FEC capability. */ + public int getFecCapability() { + return mFecCap; + } + /** Gets demodulator output format capability. */ + public int getDemodOutputFormatCapability() { + return mDemodOutputFormatCap; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java new file mode 100644 index 000000000000..6ae3c632f5db --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java @@ -0,0 +1,33 @@ +/* + * 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.frontend; + +/** + * ATSC Capabilities. + * @hide + */ +public class AtscFrontendCapabilities extends FrontendCapabilities { + private final int mModulationCap; + + AtscFrontendCapabilities(int modulationCap) { + mModulationCap = modulationCap; + } + /** Gets modulation capability. */ + public int getModulationCapability() { + return mModulationCap; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java new file mode 100644 index 000000000000..edea7af06774 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java @@ -0,0 +1,46 @@ +/* + * 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.frontend; + +/** + * DVBC Capabilities. + * @hide + */ +public class DvbcFrontendCapabilities extends FrontendCapabilities { + private final int mModulationCap; + private final int mFecCap; + private final int mAnnexCap; + + DvbcFrontendCapabilities(int modulationCap, int fecCap, int annexCap) { + mModulationCap = modulationCap; + mFecCap = fecCap; + mAnnexCap = annexCap; + } + + /** Gets modulation capability. */ + public int getModulationCapability() { + return mModulationCap; + } + /** Gets FEC capability. */ + public int getFecCapability() { + return mFecCap; + } + /** Gets annex capability. */ + public int getAnnexCapability() { + return mAnnexCap; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java new file mode 100644 index 000000000000..f5a41574cd04 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java @@ -0,0 +1,46 @@ +/* + * 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.frontend; + +/** + * DVBS Capabilities. + * @hide + */ +public class DvbsFrontendCapabilities extends FrontendCapabilities { + private final int mModulationCap; + private final long mInnerFecCap; + private final int mStandard; + + DvbsFrontendCapabilities(int modulationCap, long innerFecCap, int standard) { + mModulationCap = modulationCap; + mInnerFecCap = innerFecCap; + mStandard = standard; + } + + /** Gets modulation capability. */ + public int getModulationCapability() { + return mModulationCap; + } + /** Gets inner FEC capability. */ + public long getInnerFecCapability() { + return mInnerFecCap; + } + /** Gets DVBS standard capability. */ + public int getStandardCapability() { + return mStandard; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java new file mode 100644 index 000000000000..e9c16ddd4dc8 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java @@ -0,0 +1,78 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.frontend; + +/** + * DVBT Capabilities. + * @hide + */ +public class DvbtFrontendCapabilities extends FrontendCapabilities { + private final int mTransmissionModeCap; + private final int mBandwidthCap; + private final int mConstellationCap; + private final int mCoderateCap; + private final int mHierarchyCap; + private final int mGuardIntervalCap; + private final boolean mIsT2Supported; + private final boolean mIsMisoSupported; + + DvbtFrontendCapabilities(int transmissionModeCap, int bandwidthCap, int constellationCap, + int coderateCap, int hierarchyCap, int guardIntervalCap, boolean isT2Supported, + boolean isMisoSupported) { + mTransmissionModeCap = transmissionModeCap; + mBandwidthCap = bandwidthCap; + mConstellationCap = constellationCap; + mCoderateCap = coderateCap; + mHierarchyCap = hierarchyCap; + mGuardIntervalCap = guardIntervalCap; + mIsT2Supported = isT2Supported; + mIsMisoSupported = isMisoSupported; + } + + /** Gets transmission mode capability. */ + public int getTransmissionModeCapability() { + return mTransmissionModeCap; + } + /** Gets bandwidth capability. */ + public int getBandwidthCapability() { + return mBandwidthCap; + } + /** Gets constellation capability. */ + public int getConstellationCapability() { + return mConstellationCap; + } + /** Gets code rate capability. */ + public int getCodeRateCapability() { + return mCoderateCap; + } + /** Gets hierarchy capability. */ + public int getHierarchyCapability() { + return mHierarchyCap; + } + /** Gets guard interval capability. */ + public int getGuardIntervalCapability() { + return mGuardIntervalCap; + } + /** Returns whether T2 is supported. */ + public boolean getIsT2Supported() { + return mIsT2Supported; + } + /** Returns whether MISO is supported. */ + public boolean getIsMisoSupported() { + return mIsMisoSupported; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java index 91776e17dc21..0992eb665330 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java @@ -16,8 +16,6 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.ScanMessage; - /** * Frontend Callback. * diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java new file mode 100644 index 000000000000..7350bc0c3914 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java @@ -0,0 +1,24 @@ +/* + * 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.frontend; + +/** + * Frontend Capabilities. + * @hide + */ +public abstract class FrontendCapabilities { +} diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java index ef6c029fe626..5d03570eea80 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java @@ -16,7 +16,6 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.FrontendCapabilities; import android.media.tv.tuner.TunerConstants.FrontendType; /** diff --git a/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java new file mode 100644 index 000000000000..6544b17609c2 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java @@ -0,0 +1,59 @@ +/* + * 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.frontend; + +/** + * ISDBC Capabilities. + * @hide + */ +public class IsdbcFrontendCapabilities extends FrontendCapabilities { + private final int mModeCap; + private final int mBandwidthCap; + private final int mModulationCap; + private final int mCoderateCap; + private final int mGuardIntervalCap; + + IsdbcFrontendCapabilities(int modeCap, int bandwidthCap, int modulationCap, int coderateCap, + int guardIntervalCap) { + mModeCap = modeCap; + mBandwidthCap = bandwidthCap; + mModulationCap = modulationCap; + mCoderateCap = coderateCap; + mGuardIntervalCap = guardIntervalCap; + } + + /** Gets mode capability. */ + public int getModeCapability() { + return mModeCap; + } + /** Gets bandwidth capability. */ + public int getBandwidthCapability() { + return mBandwidthCap; + } + /** Gets modulation capability. */ + public int getModulationCapability() { + return mModulationCap; + } + /** Gets code rate capability. */ + public int getCodeRateCapability() { + return mCoderateCap; + } + /** Gets guard interval capability. */ + public int getGuardIntervalCapability() { + return mGuardIntervalCap; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java new file mode 100644 index 000000000000..92832b7fcbdd --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java @@ -0,0 +1,40 @@ +/* + * 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.frontend; + +/** + * ISDBS-3 Capabilities. + * @hide + */ +public class Isdbs3FrontendCapabilities extends FrontendCapabilities { + private final int mModulationCap; + private final int mCoderateCap; + + Isdbs3FrontendCapabilities(int modulationCap, int coderateCap) { + mModulationCap = modulationCap; + mCoderateCap = coderateCap; + } + + /** Gets modulation capability. */ + public int getModulationCapability() { + return mModulationCap; + } + /** Gets code rate capability. */ + public int getCodeRateCapability() { + return mCoderateCap; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java new file mode 100644 index 000000000000..b930b2578092 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java @@ -0,0 +1,40 @@ +/* + * 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.frontend; + +/** + * ISDBS Capabilities. + * @hide + */ +public class IsdbsFrontendCapabilities extends FrontendCapabilities { + private final int mModulationCap; + private final int mCoderateCap; + + IsdbsFrontendCapabilities(int modulationCap, int coderateCap) { + mModulationCap = modulationCap; + mCoderateCap = coderateCap; + } + + /** Gets modulation capability. */ + public int getModulationCapability() { + return mModulationCap; + } + /** Gets code rate capability. */ + public int getCodeRateCapability() { + return mCoderateCap; + } +} diff --git a/media/java/android/media/tv/tuner/ScanMessage.java b/media/java/android/media/tv/tuner/frontend/ScanMessage.java index 35f54f8447c0..dd687dd2959c 100644 --- a/media/java/android/media/tv/tuner/ScanMessage.java +++ b/media/java/android/media/tv/tuner/frontend/ScanMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open 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,9 +14,13 @@ * limitations under the License. */ -package android.media.tv.tuner; +package android.media.tv.tuner.frontend; -import android.media.tv.tuner.TunerConstants.ScanMessageType; +import android.annotation.IntDef; +import android.hardware.tv.tuner.V1_0.Constants; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Message from frontend during scan operations. @@ -24,6 +28,43 @@ import android.media.tv.tuner.TunerConstants.ScanMessageType; * @hide */ public class ScanMessage { + + /** @hide */ + @IntDef({ + LOCKED, + END, + PROGRESS_PERCENT, + FREQUENCY, + SYMBOL_RATE, + PLP_IDS, + GROUP_IDS, + INPUT_STREAM_IDS, + STANDARD, + ATSC3_PLP_INFO + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + /** @hide */ + public static final int LOCKED = Constants.FrontendScanMessageType.LOCKED; + /** @hide */ + public static final int END = Constants.FrontendScanMessageType.END; + /** @hide */ + public static final int PROGRESS_PERCENT = Constants.FrontendScanMessageType.PROGRESS_PERCENT; + /** @hide */ + public static final int FREQUENCY = Constants.FrontendScanMessageType.FREQUENCY; + /** @hide */ + public static final int SYMBOL_RATE = Constants.FrontendScanMessageType.SYMBOL_RATE; + /** @hide */ + public static final int PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS; + /** @hide */ + public static final int GROUP_IDS = Constants.FrontendScanMessageType.GROUP_IDS; + /** @hide */ + public static final int INPUT_STREAM_IDS = Constants.FrontendScanMessageType.INPUT_STREAM_IDS; + /** @hide */ + public static final int STANDARD = Constants.FrontendScanMessageType.STANDARD; + /** @hide */ + public static final int ATSC3_PLP_INFO = Constants.FrontendScanMessageType.ATSC3_PLP_INFO; + private final int mType; private final Object mValue; @@ -33,69 +74,69 @@ public class ScanMessage { } /** Gets scan message type. */ - @ScanMessageType + @Type public int getMessageType() { return mType; } /** Message indicates whether frontend is locked or not. */ public boolean getIsLocked() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_LOCKED) { + if (mType != 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) { + if (mType != END) { throw new IllegalStateException(); } return (Boolean) mValue; } /** Progress message in percent. */ public int getProgressPercent() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PROGRESS_PERCENT) { + if (mType != PROGRESS_PERCENT) { throw new IllegalStateException(); } return (Integer) mValue; } /** Gets frequency. */ public int getFrequency() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_FREQUENCY) { + if (mType != FREQUENCY) { throw new IllegalStateException(); } return (Integer) mValue; } /** Gets symbol rate. */ public int getSymbolRate() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_SYMBOL_RATE) { + if (mType != SYMBOL_RATE) { throw new IllegalStateException(); } return (Integer) mValue; } /** Gets PLP IDs. */ public int[] getPlpIds() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_PLP_IDS) { + if (mType != PLP_IDS) { throw new IllegalStateException(); } return (int[]) mValue; } /** Gets group IDs. */ public int[] getGroupIds() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_GROUP_IDS) { + if (mType != GROUP_IDS) { throw new IllegalStateException(); } return (int[]) mValue; } /** Gets Input stream IDs. */ public int[] getInputStreamIds() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS) { + if (mType != 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) { + if (mType != STANDARD) { throw new IllegalStateException(); } return (int) mValue; @@ -103,7 +144,7 @@ public class ScanMessage { /** Gets PLP information for ATSC3. */ public Atsc3PlpInfo[] getAtsc3PlpInfos() { - if (mType != TunerConstants.SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO) { + if (mType != ATSC3_PLP_INFO) { throw new IllegalStateException(); } return (Atsc3PlpInfo[]) mValue; diff --git a/media/java/android/mtp/MtpPropertyList.java b/media/java/android/mtp/MtpPropertyList.java index 557f099c25c1..53d838d84518 100644 --- a/media/java/android/mtp/MtpPropertyList.java +++ b/media/java/android/mtp/MtpPropertyList.java @@ -16,7 +16,8 @@ package android.mtp; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.util.ArrayList; import java.util.List; diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index c7dbca61f90a..ba752633718c 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -16,9 +16,8 @@ package android.mtp; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.storage.StorageVolume; -import android.provider.MediaStore; /** * This class represents a storage unit on an MTP device. diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index 86a1076af122..06adf30a8303 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -21,8 +21,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.UnsupportedAppUsage; import android.app.Service; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; diff --git a/media/jni/Android.bp b/media/jni/Android.bp index ee6761344613..536a061190d7 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -143,6 +143,10 @@ cc_library_shared { "libutils", ], + header_libs: [ + "libstagefright_foundation_headers", + ], + export_include_dirs: ["."], cflags: [ diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index f0f368815d9d..4f1125f5e482 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -696,6 +696,10 @@ static jobject android_media_tv_Tuner_open_filter( return tuner->openFilter(filterType, bufferSize); } +static jobject android_media_tv_Tuner_open_time_filter(JNIEnv, jobject) { + return NULL; +} + static DemuxFilterSettings getFilterSettings( JNIEnv *env, int type, int subtype, jobject filterSettingsObj) { DemuxFilterSettings filterSettings; @@ -840,6 +844,28 @@ static int android_media_tv_Tuner_close_filter(JNIEnv*, jobject) { return 0; } +// TODO: implement TimeFilter functions +static int android_media_tv_Tuner_time_filter_set_timestamp( + JNIEnv, jobject, jlong) { + return 0; +} + +static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv, jobject) { + return 0; +} + +static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv, jobject) { + return NULL; +} + +static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv, jobject) { + return NULL; +} + +static int android_media_tv_Tuner_time_filter_close(JNIEnv, jobject) { + return 0; +} + static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->openDescrambler(); @@ -1119,6 +1145,8 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_get_frontend_info }, { "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;", (void *)android_media_tv_Tuner_open_filter }, + { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/Tuner$TimeFilter;", + (void *)android_media_tv_Tuner_open_time_filter }, { "nativeGetLnbIds", "()Ljava/util/List;", (void *)android_media_tv_Tuner_get_lnb_ids }, { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Tuner$Lnb;", @@ -1144,6 +1172,16 @@ static const JNINativeMethod gFilterMethods[] = { { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter }, }; +static const JNINativeMethod gTimeFilterMethods[] = { + { "nativeSetTimeStamp", "(J)I", (void *)android_media_tv_Tuner_time_filter_set_timestamp }, + { "nativeClearTimeStamp", "()I", (void *)android_media_tv_Tuner_time_filter_clear_timestamp }, + { "nativeGetTimeStamp", "()Ljava/lang/Long;", + (void *)android_media_tv_Tuner_time_filter_get_timestamp }, + { "nativeGetSourceTime", "()Ljava/lang/Long;", + (void *)android_media_tv_Tuner_time_filter_get_source_time }, + { "nativeClose", "()I", (void *)android_media_tv_Tuner_time_filter_close }, +}; + static const JNINativeMethod gDescramblerMethods[] = { { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I", (void *)android_media_tv_Tuner_add_pid }, @@ -1194,6 +1232,13 @@ static bool register_android_media_tv_Tuner(JNIEnv *env) { return false; } if (AndroidRuntime::registerNativeMethods( + env, "android/media/tv/tuner/Tuner$TimeFilter", + gTimeFilterMethods, + NELEM(gTimeFilterMethods)) != JNI_OK) { + ALOGE("Failed to register time filter native methods"); + return false; + } + if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/Tuner$Descrambler", gDescramblerMethods, NELEM(gDescramblerMethods)) != JNI_OK) { diff --git a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java index dfbf5d20e074..121443f56285 100644 --- a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java +++ b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java @@ -17,12 +17,11 @@ package android.media.effect; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.core.Filter; import android.filterfw.core.FilterFactory; import android.filterfw.core.FilterFunction; import android.filterfw.core.Frame; -import android.media.effect.EffectContext; /** * Effect subclass for effects based on a single Filter. Subclasses need only invoke the diff --git a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java index 52615bf09faa..3a7f1ed4f7ec 100644 --- a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java +++ b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java @@ -17,11 +17,11 @@ package android.filterfw; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.filterfw.core.AsyncRunner; -import android.filterfw.core.FilterGraph; import android.filterfw.core.FilterContext; +import android.filterfw.core.FilterGraph; import android.filterfw.core.FrameManager; import android.filterfw.core.GraphRunner; import android.filterfw.core.RoundRobinScheduler; diff --git a/media/mca/filterfw/java/android/filterfw/core/Filter.java b/media/mca/filterfw/java/android/filterfw/core/Filter.java index 4f56b923f6ed..a608ef5be3f4 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Filter.java +++ b/media/mca/filterfw/java/android/filterfw/core/Filter.java @@ -17,19 +17,15 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.FilterContext; -import android.filterfw.core.FilterPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.io.TextGraphReader; -import android.filterfw.io.GraphIOException; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.format.ObjectFormat; +import android.filterfw.io.GraphIOException; +import android.filterfw.io.TextGraphReader; import android.util.Log; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.Thread; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java index a19220ef85f8..6b0a2193dceb 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java +++ b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java @@ -17,11 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Filter; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameManager; -import android.filterfw.core.GLEnvironment; +import android.compat.annotation.UnsupportedAppUsage; import java.util.HashMap; import java.util.HashSet; diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java index e6ca11ffca3c..35a298fd6dfb 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java +++ b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java @@ -17,6 +17,11 @@ package android.filterfw.core; +import android.compat.annotation.UnsupportedAppUsage; +import android.filterpacks.base.FrameBranch; +import android.filterpacks.base.NullFilter; +import android.util.Log; + import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -25,14 +30,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.Stack; -import android.filterfw.core.FilterContext; -import android.filterfw.core.KeyValueMap; -import android.filterpacks.base.FrameBranch; -import android.filterpacks.base.NullFilter; - -import android.annotation.UnsupportedAppUsage; -import android.util.Log; - /** * @hide */ diff --git a/media/mca/filterfw/java/android/filterfw/core/Frame.java b/media/mca/filterfw/java/android/filterfw/core/Frame.java index e880783247a3..c4d935ae4873 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Frame.java +++ b/media/mca/filterfw/java/android/filterfw/core/Frame.java @@ -17,9 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; import java.nio.ByteBuffer; diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java index eb0ff0a32c3f..a87e9b9ffbcf 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java +++ b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java @@ -17,9 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; +import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; import java.util.Map.Entry; diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java index 85c8fcd9787d..e49aaf1d6fad 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java +++ b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java @@ -17,10 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.MutableFrameFormat; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java index e25d6a7d70ab..7e4e8a64a81f 100644 --- a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java +++ b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java @@ -17,13 +17,12 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.NativeAllocatorTag; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; +import android.media.MediaRecorder; import android.os.Looper; import android.util.Log; import android.view.Surface; -import android.media.MediaRecorder; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java index 9e3025fafb6e..1ccd7feaa7c3 100644 --- a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java +++ b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java @@ -17,15 +17,10 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.StopWatchMap; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; -import android.opengl.GLES20; import android.graphics.Rect; +import android.opengl.GLES20; import java.nio.ByteBuffer; diff --git a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java index 250cfaaba9d4..b57e8bb7262e 100644 --- a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java +++ b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java @@ -17,7 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java index ae2ad99899f0..da00b1ffb180 100644 --- a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java +++ b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java @@ -17,9 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; +import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; diff --git a/media/mca/filterfw/java/android/filterfw/core/Program.java b/media/mca/filterfw/java/android/filterfw/core/Program.java index 376c08554eb2..145388e4437e 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Program.java +++ b/media/mca/filterfw/java/android/filterfw/core/Program.java @@ -17,8 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java index f41636e7cf76..e043be0e27bd 100644 --- a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java +++ b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java @@ -17,12 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; -import android.filterfw.core.NativeAllocatorTag; -import android.filterfw.core.Program; -import android.filterfw.core.StopWatchMap; -import android.filterfw.core.VertexFrame; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.geometry.Quad; import android.opengl.GLES20; diff --git a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java index ac087305287f..0e05092d0cdd 100644 --- a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java +++ b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java @@ -17,7 +17,7 @@ package android.filterfw.format; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.core.FrameFormat; import android.filterfw.core.MutableFrameFormat; import android.graphics.Bitmap; diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Point.java b/media/mca/filterfw/java/android/filterfw/geometry/Point.java index d7acf12dd1de..96d2d7b08b74 100644 --- a/media/mca/filterfw/java/android/filterfw/geometry/Point.java +++ b/media/mca/filterfw/java/android/filterfw/geometry/Point.java @@ -17,8 +17,7 @@ package android.filterfw.geometry; -import android.annotation.UnsupportedAppUsage; -import java.lang.Math; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java index 610e5b80399d..2b308a91576f 100644 --- a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java +++ b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java @@ -17,10 +17,8 @@ package android.filterfw.geometry; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.geometry.Point; +import android.compat.annotation.UnsupportedAppUsage; -import java.lang.Float; import java.util.Arrays; import java.util.Collections; import java.util.List; 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 04fccc7e0f94..8c0273b06e8c 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -46,8 +46,8 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService public static final String ROUTE_ID5_TO_TRANSFER_TO = "route_id5_to_transfer_to"; public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to"; - public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category"; - public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route"; + public static final String ROUTE_ID_SPECIAL_TYPE = "route_special_type"; + public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type Route"; public static final int VOLUME_MAX = 100; public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume"; @@ -58,49 +58,49 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService public static final String ACTION_REMOVE_ROUTE = "com.android.mediarouteprovider.action_remove_route"; - public static final String CATEGORY_SAMPLE = - "com.android.mediarouteprovider.CATEGORY_SAMPLE"; - public static final String CATEGORY_SPECIAL = - "com.android.mediarouteprovider.CATEGORY_SPECIAL"; + public static final String TYPE_SAMPLE = + "com.android.mediarouteprovider.TYPE_SAMPLE"; + public static final String TYPE_SPECIAL = + "com.android.mediarouteprovider.TYPE_SPECIAL"; Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); - Map<String, Integer> mRouteSessionMap = new HashMap<>(); + Map<String, String> mRouteSessionMap = new HashMap<>(); private int mNextSessionId = 1000; private void initializeRoutes() { MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .setDeviceType(DEVICE_TYPE_TV) .build(); MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .setDeviceType(DEVICE_TYPE_SPEAKER) .build(); MediaRoute2Info route3 = new MediaRoute2Info.Builder( ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .build(); MediaRoute2Info route4 = new MediaRoute2Info.Builder( ROUTE_ID4_TO_SELECT_AND_DESELECT, ROUTE_NAME4) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .build(); MediaRoute2Info route5 = new MediaRoute2Info.Builder( ROUTE_ID5_TO_TRANSFER_TO, ROUTE_NAME5) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .build(); MediaRoute2Info routeSpecial = - new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY) - .addSupportedCategory(CATEGORY_SAMPLE) - .addSupportedCategory(CATEGORY_SPECIAL) + new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_TYPE, ROUTE_NAME_SPECIAL_TYPE) + .addRouteType(TYPE_SAMPLE) + .addRouteType(TYPE_SPECIAL) .build(); MediaRoute2Info fixedVolumeRoute = new MediaRoute2Info.Builder(ROUTE_ID_FIXED_VOLUME, ROUTE_NAME_FIXED_VOLUME) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_FIXED) .build(); MediaRoute2Info variableVolumeRoute = new MediaRoute2Info.Builder(ROUTE_ID_VARIABLE_VOLUME, ROUTE_NAME_VARIABLE_VOLUME) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolumeMax(VOLUME_MAX) .build(); @@ -167,7 +167,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onCreateSession(String packageName, String routeId, String controlCategory, + public void onCreateSession(String packageName, String routeId, String routeType, long requestId) { MediaRoute2Info route = mRoutes.get(routeId); if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { @@ -177,7 +177,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } maybeDeselectRoute(routeId); - final int sessionId = mNextSessionId; + final String sessionId = String.valueOf(mNextSessionId); mNextSessionId++; mRoutes.put(routeId, new MediaRoute2Info.Builder(route) @@ -186,7 +186,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService mRouteSessionMap.put(routeId, sessionId); RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( - sessionId, packageName, controlCategory) + sessionId, packageName, routeType) .addSelectedRoute(routeId) .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT) .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO) @@ -196,7 +196,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) { + public void onDestroySession(String sessionId, RouteSessionInfo lastSessionInfo) { for (String routeId : lastSessionInfo.getSelectedRoutes()) { mRouteSessionMap.remove(routeId); MediaRoute2Info route = mRoutes.get(routeId); @@ -209,7 +209,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onSelectRoute(int sessionId, String routeId) { + public void onSelectRoute(String sessionId, String routeId) { RouteSessionInfo sessionInfo = getSessionInfo(sessionId); MediaRoute2Info route = mRoutes.get(routeId); if (route == null || sessionInfo == null) { @@ -218,7 +218,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService maybeDeselectRoute(routeId); mRoutes.put(routeId, new MediaRoute2Info.Builder(route) - .setClientPackageName(sessionInfo.getPackageName()) + .setClientPackageName(sessionInfo.getClientPackageName()) .build()); mRouteSessionMap.put(routeId, sessionId); @@ -232,7 +232,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onDeselectRoute(int sessionId, String routeId) { + public void onDeselectRoute(String sessionId, String routeId) { RouteSessionInfo sessionInfo = getSessionInfo(sessionId); MediaRoute2Info route = mRoutes.get(routeId); @@ -254,7 +254,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onTransferToRoute(int sessionId, String routeId) { + public void onTransferToRoute(String sessionId, String routeId) { RouteSessionInfo sessionInfo = getSessionInfo(sessionId); RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) .clearSelectedRoutes() @@ -271,7 +271,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService return; } - int sessionId = mRouteSessionMap.get(routeId); + String sessionId = mRouteSessionMap.get(routeId); onDeselectRoute(sessionId, routeId); } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java index 6fe847bf5f3a..ce4bb8ef2688 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java @@ -23,18 +23,18 @@ import static android.media.MediaRoute2Info.DEVICE_TYPE_TV; import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED; import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE; -import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_ALL; -import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_SPECIAL; -import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SAMPLE; -import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SPECIAL; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID2; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID4_TO_SELECT_AND_DESELECT; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID5_TO_TRANSFER_TO; -import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_CATEGORY; +import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_TYPE; import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_VARIABLE_VOLUME; import static com.android.mediaroutertest.MediaRouterManagerTest.SYSTEM_PROVIDER_ID; +import static com.android.mediaroutertest.MediaRouterManagerTest.TYPES_ALL; +import static com.android.mediaroutertest.MediaRouterManagerTest.TYPES_SPECIAL; +import static com.android.mediaroutertest.MediaRouterManagerTest.TYPE_SAMPLE; +import static com.android.mediaroutertest.MediaRouterManagerTest.TYPE_SPECIAL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -50,6 +50,7 @@ import android.media.MediaRouter2; import android.media.MediaRouter2.RouteCallback; import android.media.MediaRouter2.RouteSessionController; import android.media.MediaRouter2.SessionCallback; +import android.media.RouteDiscoveryRequest; import android.media.RouteSessionInfo; import android.net.Uri; import android.os.Parcel; @@ -95,14 +96,14 @@ public class MediaRouter2Test { } /** - * Tests if we get proper routes for application that has special control category. + * Tests if we get proper routes for application that has special route type. */ @Test public void testGetRoutes() throws Exception { - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_SPECIAL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_SPECIAL); assertEquals(1, routes.size()); - assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY)); + assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE)); } @Test @@ -114,7 +115,7 @@ public class MediaRouter2Test { .setIconUri(new Uri.Builder().path("icon").build()) .setVolume(5) .setVolumeMax(20) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE) .setDeviceType(DEVICE_TYPE_SPEAKER) .build(); @@ -137,7 +138,7 @@ public class MediaRouter2Test { .setClientPackageName("com.android.mediaroutertest") .setConnectionState(CONNECTION_STATE_CONNECTING) .setIconUri(new Uri.Builder().path("icon").build()) - .addSupportedCategory(CATEGORY_SAMPLE) + .addRouteType(TYPE_SAMPLE) .setVolume(5) .setVolumeMax(20) .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE) @@ -168,9 +169,9 @@ public class MediaRouter2Test { .setClientPackageName("another.client.package").build(); assertNotEquals(route, routeClient); - MediaRoute2Info routeCategory = new MediaRoute2Info.Builder(route) - .addSupportedCategory(CATEGORY_SPECIAL).build(); - assertNotEquals(route, routeCategory); + MediaRoute2Info routeType = new MediaRoute2Info.Builder(route) + .addRouteType(TYPE_SPECIAL).build(); + assertNotEquals(route, routeType); MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route) .setVolume(10).build(); @@ -191,7 +192,7 @@ public class MediaRouter2Test { @Test public void testControlVolumeWithRouter() throws Exception { - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(TYPES_ALL); MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); assertNotNull(volRoute); @@ -202,12 +203,14 @@ public class MediaRouter2Test { awaitOnRouteChanged( () -> mRouter2.requestUpdateVolume(volRoute, deltaVolume), ROUTE_ID_VARIABLE_VOLUME, - (route -> route.getVolume() == originalVolume + deltaVolume)); + (route -> route.getVolume() == originalVolume + deltaVolume), + TYPES_ALL); awaitOnRouteChanged( () -> mRouter2.requestSetVolume(volRoute, originalVolume), ROUTE_ID_VARIABLE_VOLUME, - (route -> route.getVolume() == originalVolume)); + (route -> route.getVolume() == originalVolume), + TYPES_ALL); } @Test @@ -234,13 +237,13 @@ public class MediaRouter2Test { @Test public void testRequestCreateSessionWithInvalidArguments() { MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build(); - String controlCategory = "controlCategory"; + String routeType = "routeType"; // Tests null route assertThrows(NullPointerException.class, - () -> mRouter2.requestCreateSession(null, controlCategory)); + () -> mRouter2.requestCreateSession(null, routeType)); - // Tests null or empty control category + // Tests null or empty route type assertThrows(IllegalArgumentException.class, () -> mRouter2.requestCreateSession(route, null)); assertThrows(IllegalArgumentException.class, @@ -249,10 +252,10 @@ public class MediaRouter2Test { @Test public void testRequestCreateSessionSuccess() throws Exception { - final List<String> sampleControlCategory = new ArrayList<>(); - sampleControlCategory.add(CATEGORY_SAMPLE); + final List<String> sampleRouteType = new ArrayList<>(); + sampleRouteType.add(TYPE_SAMPLE); - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType); MediaRoute2Info route = routes.get(ROUTE_ID1); assertNotNull(route); @@ -266,25 +269,25 @@ public class MediaRouter2Test { public void onSessionCreated(RouteSessionController controller) { assertNotNull(controller); assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)); - assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory())); + assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType())); controllers.add(controller); successLatch.countDown(); } @Override public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedControlCategory) { + String requestedRouteType) { failureLatch.countDown(); } }; // TODO: Remove this once the MediaRouter2 becomes always connected to the service. RouteCallback routeCallback = new RouteCallback(); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route, CATEGORY_SAMPLE); + mRouter2.requestCreateSession(route, TYPE_SAMPLE); assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); // onSessionCreationFailed should not be called. @@ -298,10 +301,10 @@ public class MediaRouter2Test { @Test public void testRequestCreateSessionFailure() throws Exception { - final List<String> sampleControlCategory = new ArrayList<>(); - sampleControlCategory.add(CATEGORY_SAMPLE); + final List<String> sampleRouteType = new ArrayList<>(); + sampleRouteType.add(TYPE_SAMPLE); - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType); MediaRoute2Info route = routes.get(ROUTE_ID3_SESSION_CREATION_FAILED); assertNotNull(route); @@ -319,20 +322,20 @@ public class MediaRouter2Test { @Override public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedControlCategory) { + String requestedRouteType) { assertEquals(route, requestedRoute); - assertTrue(TextUtils.equals(CATEGORY_SAMPLE, requestedControlCategory)); + assertTrue(TextUtils.equals(TYPE_SAMPLE, requestedRouteType)); failureLatch.countDown(); } }; // TODO: Remove this once the MediaRouter2 becomes always connected to the service. RouteCallback routeCallback = new RouteCallback(); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route, CATEGORY_SAMPLE); + mRouter2.requestCreateSession(route, TYPE_SAMPLE); assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); // onSessionCreated should not be called. @@ -346,8 +349,8 @@ public class MediaRouter2Test { @Test public void testRequestCreateSessionMultipleSessions() throws Exception { - final List<String> sampleControlCategory = new ArrayList<>(); - sampleControlCategory.add(CATEGORY_SAMPLE); + final List<String> sampleRouteType = new ArrayList<>(); + sampleRouteType.add(TYPE_SAMPLE); final CountDownLatch successLatch = new CountDownLatch(2); final CountDownLatch failureLatch = new CountDownLatch(1); @@ -363,12 +366,12 @@ public class MediaRouter2Test { @Override public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedControlCategory) { + String requestedRouteType) { failureLatch.countDown(); } }; - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType); MediaRoute2Info route1 = routes.get(ROUTE_ID1); MediaRoute2Info route2 = routes.get(ROUTE_ID2); assertNotNull(route1); @@ -376,12 +379,12 @@ public class MediaRouter2Test { // TODO: Remove this once the MediaRouter2 becomes always connected to the service. RouteCallback routeCallback = new RouteCallback(); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route1, CATEGORY_SAMPLE); - mRouter2.requestCreateSession(route2, CATEGORY_SAMPLE); + mRouter2.requestCreateSession(route1, TYPE_SAMPLE); + mRouter2.requestCreateSession(route2, TYPE_SAMPLE); assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); // onSessionCreationFailed should not be called. @@ -395,8 +398,9 @@ public class MediaRouter2Test { assertNotEquals(controller1.getSessionId(), controller2.getSessionId()); assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1)); assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2)); - assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller1.getControlCategory())); - assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller2.getControlCategory())); + assertTrue(TextUtils.equals(TYPE_SAMPLE, controller1.getRouteType())); + assertTrue(TextUtils.equals(TYPE_SAMPLE, controller2.getRouteType())); + } finally { releaseControllers(createdControllers); mRouter2.unregisterRouteCallback(routeCallback); @@ -406,10 +410,10 @@ public class MediaRouter2Test { @Test public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception { - final List<String> sampleControlCategory = new ArrayList<>(); - sampleControlCategory.add(CATEGORY_SAMPLE); + final List<String> sampleRouteType = new ArrayList<>(); + sampleRouteType.add(TYPE_SAMPLE); - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType); MediaRoute2Info route = routes.get(ROUTE_ID1); assertNotNull(route); @@ -427,18 +431,18 @@ public class MediaRouter2Test { @Override public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedControlCategory) { + String requestedRouteType) { failureLatch.countDown(); } }; // TODO: Remove this once the MediaRouter2 becomes always connected to the service. RouteCallback routeCallback = new RouteCallback(); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route, CATEGORY_SAMPLE); + mRouter2.requestCreateSession(route, TYPE_SAMPLE); // Unregisters session callback mRouter2.unregisterSessionCallback(sessionCallback); @@ -456,10 +460,10 @@ public class MediaRouter2Test { // TODO: Add tests for illegal inputs if needed (e.g. selecting already selected route) @Test public void testRouteSessionControllerSelectAndDeselectRoute() throws Exception { - final List<String> sampleControlCategory = new ArrayList<>(); - sampleControlCategory.add(CATEGORY_SAMPLE); + final List<String> sampleRouteType = new ArrayList<>(); + sampleRouteType.add(TYPE_SAMPLE); - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType); MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1); assertNotNull(routeToCreateSessionWith); @@ -474,7 +478,7 @@ public class MediaRouter2Test { public void onSessionCreated(RouteSessionController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); - assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory())); + assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType())); controllers.add(controller); onSessionCreatedLatch.countDown(); } @@ -483,20 +487,21 @@ public class MediaRouter2Test { public void onSessionInfoChanged(RouteSessionController controller, RouteSessionInfo oldInfo, RouteSessionInfo newInfo) { if (onSessionCreatedLatch.getCount() != 0 - || controllers.get(0).getSessionId() != controller.getSessionId()) { + || !TextUtils.equals( + controllers.get(0).getSessionId(), controller.getSessionId())) { return; } if (onSessionInfoChangedLatchForSelect.getCount() != 0) { // Check oldInfo - assertEquals(controller.getSessionId(), oldInfo.getSessionId()); + assertEquals(controller.getSessionId(), oldInfo.getId()); assertEquals(1, oldInfo.getSelectedRoutes().size()); assertTrue(oldInfo.getSelectedRoutes().contains(ROUTE_ID1)); assertTrue(oldInfo.getSelectableRoutes().contains( ROUTE_ID4_TO_SELECT_AND_DESELECT)); // Check newInfo - assertEquals(controller.getSessionId(), newInfo.getSessionId()); + assertEquals(controller.getSessionId(), newInfo.getId()); assertEquals(2, newInfo.getSelectedRoutes().size()); assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID1)); assertTrue(newInfo.getSelectedRoutes().contains( @@ -507,7 +512,7 @@ public class MediaRouter2Test { onSessionInfoChangedLatchForSelect.countDown(); } else { // Check newInfo - assertEquals(controller.getSessionId(), newInfo.getSessionId()); + assertEquals(controller.getSessionId(), newInfo.getId()); assertEquals(1, newInfo.getSelectedRoutes().size()); assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID1)); assertFalse(newInfo.getSelectedRoutes().contains( @@ -522,11 +527,11 @@ public class MediaRouter2Test { // TODO: Remove this once the MediaRouter2 becomes always connected to the service. RouteCallback routeCallback = new RouteCallback(); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE); + mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE); assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); @@ -554,10 +559,10 @@ public class MediaRouter2Test { @Test public void testRouteSessionControllerTransferToRoute() throws Exception { - final List<String> sampleControlCategory = new ArrayList<>(); - sampleControlCategory.add(CATEGORY_SAMPLE); + final List<String> sampleRouteType = new ArrayList<>(); + sampleRouteType.add(TYPE_SAMPLE); - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType); MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1); assertNotNull(routeToCreateSessionWith); @@ -570,8 +575,12 @@ public class MediaRouter2Test { @Override public void onSessionCreated(RouteSessionController controller) { assertNotNull(controller); + android.util.Log.d(TAG, "selected route ids "); + for (String routeId : getRouteIds(controller.getSelectedRoutes())) { + android.util.Log.d(TAG, "route id : " + routeId); + } assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); - assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory())); + assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType())); controllers.add(controller); onSessionCreatedLatch.countDown(); } @@ -580,18 +589,19 @@ public class MediaRouter2Test { public void onSessionInfoChanged(RouteSessionController controller, RouteSessionInfo oldInfo, RouteSessionInfo newInfo) { if (onSessionCreatedLatch.getCount() != 0 - || controllers.get(0).getSessionId() != controller.getSessionId()) { + || !TextUtils.equals( + controllers.get(0).getSessionId(), controller.getSessionId())) { return; } // Check oldInfo - assertEquals(controller.getSessionId(), oldInfo.getSessionId()); + assertEquals(controller.getSessionId(), oldInfo.getId()); assertEquals(1, oldInfo.getSelectedRoutes().size()); assertTrue(oldInfo.getSelectedRoutes().contains(ROUTE_ID1)); assertTrue(oldInfo.getTransferrableRoutes().contains(ROUTE_ID5_TO_TRANSFER_TO)); // Check newInfo - assertEquals(controller.getSessionId(), newInfo.getSessionId()); + assertEquals(controller.getSessionId(), newInfo.getId()); assertEquals(1, newInfo.getSelectedRoutes().size()); assertFalse(newInfo.getSelectedRoutes().contains(ROUTE_ID1)); assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID5_TO_TRANSFER_TO)); @@ -603,11 +613,11 @@ public class MediaRouter2Test { // TODO: Remove this once the MediaRouter2 becomes always connected to the service. RouteCallback routeCallback = new RouteCallback(); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE); + mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE); assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); @@ -632,10 +642,10 @@ public class MediaRouter2Test { @Test public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception { - final List<String> sampleControlCategory = new ArrayList<>(); - sampleControlCategory.add(CATEGORY_SAMPLE); + final List<String> sampleRouteType = new ArrayList<>(); + sampleRouteType.add(TYPE_SAMPLE); - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType); MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1); assertNotNull(routeToCreateSessionWith); @@ -649,7 +659,7 @@ public class MediaRouter2Test { public void onSessionCreated(RouteSessionController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); - assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory())); + assertTrue(TextUtils.equals(TYPE_SAMPLE, controller.getRouteType())); controllers.add(controller); onSessionCreatedLatch.countDown(); } @@ -658,7 +668,8 @@ public class MediaRouter2Test { public void onSessionInfoChanged(RouteSessionController controller, RouteSessionInfo oldInfo, RouteSessionInfo newInfo) { if (onSessionCreatedLatch.getCount() != 0 - || controllers.get(0).getSessionId() != controller.getSessionId()) { + || !TextUtils.equals( + controllers.get(0).getSessionId(), controller.getSessionId())) { return; } onSessionInfoChangedLatch.countDown(); @@ -667,11 +678,11 @@ public class MediaRouter2Test { // TODO: Remove this once the MediaRouter2 becomes always connected to the service. RouteCallback routeCallback = new RouteCallback(); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE); + mRouter2.requestCreateSession(routeToCreateSessionWith, TYPE_SAMPLE); assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); @@ -701,17 +712,16 @@ public class MediaRouter2Test { static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) { Map<String, MediaRoute2Info> routeMap = new HashMap<>(); for (MediaRoute2Info route : routes) { - // intentionally not using route.getUniqueId() for convenience. routeMap.put(route.getId(), route); } return routeMap; } - Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories) + Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> routeTypes) throws Exception { CountDownLatch latch = new CountDownLatch(1); - // A dummy callback is required to send control category info. + // A dummy callback is required to send route type info. RouteCallback routeCallback = new RouteCallback() { @Override public void onRoutesAdded(List<MediaRoute2Info> routes) { @@ -724,8 +734,8 @@ public class MediaRouter2Test { } }; - mRouter2.setControlCategories(controlCategories); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, + new RouteDiscoveryRequest.Builder(routeTypes, true).build()); try { latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); return createRouteMap(mRouter2.getRoutes()); @@ -741,7 +751,7 @@ public class MediaRouter2Test { } /** - * Returns a list of IDs (not uniqueId) of the given route list. + * Returns a list of IDs of the given route list. */ List<String> getRouteIds(@NonNull List<MediaRoute2Info> routes) { List<String> result = new ArrayList<>(); @@ -752,7 +762,8 @@ public class MediaRouter2Test { } void awaitOnRouteChanged(Runnable task, String routeId, - Predicate<MediaRoute2Info> predicate) throws Exception { + Predicate<MediaRoute2Info> predicate, + List<String> routeTypes) throws Exception { CountDownLatch latch = new CountDownLatch(1); RouteCallback routeCallback = new RouteCallback() { @Override @@ -763,7 +774,8 @@ public class MediaRouter2Test { } } }; - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, + new RouteDiscoveryRequest.Builder(routeTypes, true).build()); try { task.run(); assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 83c7c173e8e8..9ff9177c1b40 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -31,6 +31,7 @@ import android.media.MediaRouter2; import android.media.MediaRouter2.RouteCallback; import android.media.MediaRouter2.SessionCallback; import android.media.MediaRouter2Manager; +import android.media.RouteDiscoveryRequest; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -57,41 +58,48 @@ import java.util.function.Predicate; public class MediaRouterManagerTest { private static final String TAG = "MediaRouterManagerTest"; - // Must be the same as SampleMediaRoute2ProviderService - public static final String ROUTE_ID1 = "route_id1"; + public static final String SAMPLE_PROVIDER_ROUTES_ID_PREFIX = + "com.android.mediarouteprovider.example/.SampleMediaRoute2ProviderService:"; + + // Must be the same as SampleMediaRoute2ProviderService except the prefix of IDs. + public static final String ROUTE_ID1 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id1"; public static final String ROUTE_NAME1 = "Sample Route 1"; - public static final String ROUTE_ID2 = "route_id2"; + public static final String ROUTE_ID2 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id2"; public static final String ROUTE_NAME2 = "Sample Route 2"; public static final String ROUTE_ID3_SESSION_CREATION_FAILED = - "route_id3_session_creation_failed"; + SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id3_session_creation_failed"; public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed"; public static final String ROUTE_ID4_TO_SELECT_AND_DESELECT = - "route_id4_to_select_and_deselect"; + SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id4_to_select_and_deselect"; public static final String ROUTE_NAME4 = "Sample Route 4 - Route to select and deselect"; - public static final String ROUTE_ID5_TO_TRANSFER_TO = "route_id5_to_transfer_to"; + public static final String ROUTE_ID5_TO_TRANSFER_TO = + SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id5_to_transfer_to"; public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to"; - public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category"; - public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route"; + public static final String ROUTE_ID_SPECIAL_TYPE = + SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_type"; + public static final String ROUTE_NAME_SPECIAL_TYPE = "Special Type Route"; public static final String SYSTEM_PROVIDER_ID = "com.android.server.media/.SystemMediaRoute2Provider"; public static final int VOLUME_MAX = 100; - public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume"; + public static final String ROUTE_ID_FIXED_VOLUME = + SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_fixed_volume"; public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route"; - public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume"; + public static final String ROUTE_ID_VARIABLE_VOLUME = + SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_variable_volume"; public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route"; public static final String ACTION_REMOVE_ROUTE = "com.android.mediarouteprovider.action_remove_route"; - public static final String CATEGORY_SAMPLE = - "com.android.mediarouteprovider.CATEGORY_SAMPLE"; - public static final String CATEGORY_SPECIAL = - "com.android.mediarouteprovider.CATEGORY_SPECIAL"; + public static final String TYPE_SAMPLE = + "com.android.mediarouteprovider.TYPE_SAMPLE"; + public static final String TYPE_SPECIAL = + "com.android.mediarouteprovider.TYPE_SPECIAL"; - private static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO"; + private static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO"; private static final int TIMEOUT_MS = 5000; @@ -105,18 +113,18 @@ public class MediaRouterManagerTest { private final List<RouteCallback> mRouteCallbacks = new ArrayList<>(); private final List<SessionCallback> mSessionCallbacks = new ArrayList<>(); - public static final List<String> CATEGORIES_ALL = new ArrayList(); - public static final List<String> CATEGORIES_SPECIAL = new ArrayList(); - private static final List<String> CATEGORIES_LIVE_AUDIO = new ArrayList<>(); + public static final List<String> TYPES_ALL = new ArrayList(); + public static final List<String> TYPES_SPECIAL = new ArrayList(); + private static final List<String> TYPES_LIVE_AUDIO = new ArrayList<>(); static { - CATEGORIES_ALL.add(CATEGORY_SAMPLE); - CATEGORIES_ALL.add(CATEGORY_SPECIAL); - CATEGORIES_ALL.add(CATEGORY_LIVE_AUDIO); + TYPES_ALL.add(TYPE_SAMPLE); + TYPES_ALL.add(TYPE_SPECIAL); + TYPES_ALL.add(TYPE_LIVE_AUDIO); - CATEGORIES_SPECIAL.add(CATEGORY_SPECIAL); + TYPES_SPECIAL.add(TYPE_SPECIAL); - CATEGORIES_LIVE_AUDIO.add(CATEGORY_LIVE_AUDIO); + TYPES_LIVE_AUDIO.add(TYPE_LIVE_AUDIO); } @Before @@ -173,7 +181,7 @@ public class MediaRouterManagerTest { @Test public void testOnRoutesRemoved() throws Exception { CountDownLatch latch = new CountDownLatch(1); - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL); addRouterCallback(new RouteCallback()); addManagerCallback(new MediaRouter2Manager.Callback() { @@ -195,14 +203,14 @@ public class MediaRouterManagerTest { } /** - * Tests if we get proper routes for application that has special control category. + * Tests if we get proper routes for application that has special route type. */ @Test - public void testControlCategory() throws Exception { - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_SPECIAL); + public void testRouteType() throws Exception { + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_SPECIAL); assertEquals(1, routes.size()); - assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY)); + assertNotNull(routes.get(ROUTE_ID_SPECIAL_TYPE)); } /** @@ -211,7 +219,7 @@ public class MediaRouterManagerTest { */ @Test public void testRouterOnSessionCreated() throws Exception { - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL); CountDownLatch latch = new CountDownLatch(1); @@ -247,7 +255,7 @@ public class MediaRouterManagerTest { @Ignore("TODO: test session created callback instead of onRouteSelected") public void testManagerOnRouteSelected() throws Exception { CountDownLatch latch = new CountDownLatch(1); - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL); addRouterCallback(new RouteCallback()); addManagerCallback(new MediaRouter2Manager.Callback() { @@ -277,7 +285,7 @@ public class MediaRouterManagerTest { public void testGetActiveRoutes() throws Exception { CountDownLatch latch = new CountDownLatch(1); - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL); addRouterCallback(new RouteCallback()); addManagerCallback(new MediaRouter2Manager.Callback() { @Override @@ -313,7 +321,7 @@ public class MediaRouterManagerTest { @Test @Ignore("TODO: enable when session is released") public void testSingleProviderSelect() throws Exception { - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL); addRouterCallback(new RouteCallback()); awaitOnRouteChangedManager( @@ -338,7 +346,7 @@ public class MediaRouterManagerTest { @Test public void testControlVolumeWithManager() throws Exception { - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL); MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); int originalVolume = volRoute.getVolume(); @@ -357,7 +365,7 @@ public class MediaRouterManagerTest { @Test public void testVolumeHandling() throws Exception { - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(TYPES_ALL); MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME); MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); @@ -367,11 +375,11 @@ public class MediaRouterManagerTest { assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax()); } - Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories) + Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeTypes) throws Exception { CountDownLatch latch = new CountDownLatch(2); - // A dummy callback is required to send control category info. + // A dummy callback is required to send route type info. RouteCallback routeCallback = new RouteCallback(); MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() { @Override @@ -386,16 +394,16 @@ public class MediaRouterManagerTest { } @Override - public void onControlCategoriesChanged(String packageName, List<String> categories) { + public void onControlCategoriesChanged(String packageName, List<String> routeTypes) { if (TextUtils.equals(mPackageName, packageName) - && controlCategories.equals(categories)) { + && routeTypes.equals(routeTypes)) { latch.countDown(); } } }; mManager.registerCallback(mExecutor, managerCallback); - mRouter2.setControlCategories(controlCategories); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, + new RouteDiscoveryRequest.Builder(routeTypes, true).build()); try { latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); return createRouteMap(mManager.getAvailableRoutes(mPackageName)); @@ -430,7 +438,6 @@ public class MediaRouterManagerTest { static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) { Map<String, MediaRoute2Info> routeMap = new HashMap<>(); for (MediaRoute2Info route : routes) { - // intentionally not using route.getUniqueId() for convenience. routeMap.put(route.getId(), route); } return routeMap; @@ -443,7 +450,7 @@ public class MediaRouterManagerTest { private void addRouterCallback(RouteCallback routeCallback) { mRouteCallbacks.add(routeCallback); - mRouter2.registerRouteCallback(mExecutor, routeCallback); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryRequest.EMPTY); } private void addSessionCallback(SessionCallback sessionCallback) { diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java new file mode 100644 index 000000000000..60d131a8fb7e --- /dev/null +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteDiscoveryRequestTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.mediaroutertest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.media.RouteDiscoveryRequest; +import android.os.Parcel; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class RouteDiscoveryRequestTest { + @Before + public void setUp() throws Exception { } + + @After + public void tearDown() throws Exception { } + + @Test + public void testEquality() { + List<String> testTypes = new ArrayList<>(); + testTypes.add("TEST_TYPE_1"); + testTypes.add("TEST_TYPE_2"); + RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true) + .build(); + + RouteDiscoveryRequest requestRebuilt = new RouteDiscoveryRequest.Builder(request) + .build(); + + assertEquals(request, requestRebuilt); + + Parcel parcel = Parcel.obtain(); + parcel.writeParcelable(request, 0); + parcel.setDataPosition(0); + RouteDiscoveryRequest requestFromParcel = parcel.readParcelable(null); + + assertEquals(request, requestFromParcel); + } + + @Test + public void testInequality() { + List<String> testTypes = new ArrayList<>(); + testTypes.add("TEST_TYPE_1"); + testTypes.add("TEST_TYPE_2"); + + List<String> testTypes2 = new ArrayList<>(); + testTypes.add("TEST_TYPE_3"); + + RouteDiscoveryRequest request = new RouteDiscoveryRequest.Builder(testTypes, true) + .build(); + + RouteDiscoveryRequest requestTypes = new RouteDiscoveryRequest.Builder(request) + .setRouteTypes(testTypes2) + .build(); + assertNotEquals(request, requestTypes); + + RouteDiscoveryRequest requestActiveScan = new RouteDiscoveryRequest.Builder(request) + .setActiveScan(false) + .build(); + assertNotEquals(request, requestActiveScan); + } +} diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java index 2e81a646b0db..9971fc3bbe9f 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java @@ -29,6 +29,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest public class RouteSessionTest { + private static final String TEST_SESSION_ID = "test_session_id"; private static final String TEST_PACKAGE_NAME = "com.android.mediaroutertest"; private static final String TEST_CONTROL_CATEGORY = "com.android.mediaroutertest.category"; @@ -36,17 +37,17 @@ public class RouteSessionTest { @Test public void testValidity() { - RouteSessionInfo emptyPackageSession = new RouteSessionInfo.Builder(1, + RouteSessionInfo emptyPackageSession = new RouteSessionInfo.Builder(TEST_SESSION_ID, "", TEST_CONTROL_CATEGORY) .addSelectedRoute(TEST_ROUTE_ID1) .build(); - RouteSessionInfo emptyCategorySession = new RouteSessionInfo.Builder(1, + RouteSessionInfo emptyCategorySession = new RouteSessionInfo.Builder(TEST_SESSION_ID, TEST_PACKAGE_NAME, "") .addSelectedRoute(TEST_ROUTE_ID1) .build(); - RouteSessionInfo emptySelectedRouteSession = new RouteSessionInfo.Builder(1, + RouteSessionInfo emptySelectedRouteSession = new RouteSessionInfo.Builder(TEST_SESSION_ID, TEST_PACKAGE_NAME, TEST_CONTROL_CATEGORY) .build(); @@ -54,9 +55,9 @@ public class RouteSessionTest { .addSelectedRoute(TEST_ROUTE_ID1) .build(); - assertFalse(emptySelectedRouteSession.isValid()); assertFalse(emptyPackageSession.isValid()); assertFalse(emptyCategorySession.isValid()); + assertFalse(emptySelectedRouteSession.isValid()); assertTrue(validSession.isValid()); } } diff --git a/mms/OWNERS b/mms/OWNERS index ba00d5d75010..befc320b949c 100644 --- a/mms/OWNERS +++ b/mms/OWNERS @@ -12,3 +12,5 @@ satk@google.com shuoq@google.com refuhoo@google.com nazaninb@google.com +sarahchin@google.com +dbright@google.com
\ No newline at end of file diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java index 65542673a607..24ea3cfc5a75 100644 --- a/mms/java/android/telephony/MmsManager.java +++ b/mms/java/android/telephony/MmsManager.java @@ -16,8 +16,11 @@ package android.telephony; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread; import android.app.PendingIntent; +import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; @@ -27,22 +30,16 @@ import com.android.internal.telephony.IMms; /** * Manages MMS operations such as sending multimedia messages. - * Get this object by calling the static method {@link #getInstance()}. - * @hide */ -public class MmsManager { +public final class MmsManager { private static final String TAG = "MmsManager"; - - /** Singleton object constructed during class initialization. */ - private static final MmsManager sInstance = new MmsManager(); + private final Context mContext; /** - * Get the MmsManager singleton instance. - * - * @return the {@link MmsManager} singleton instance. + * @hide */ - public static MmsManager getInstance() { - return sInstance; + public MmsManager(@NonNull Context context) { + mContext = context; } /** @@ -56,8 +53,9 @@ public class MmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message * is successfully sent, or failed */ - public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl, - Bundle configOverrides, PendingIntent sentIntent) { + public void sendMultimediaMessage(int subId, @NonNull Uri contentUri, + @Nullable String locationUrl, @Nullable Bundle configOverrides, + @Nullable PendingIntent sentIntent) { try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { @@ -84,8 +82,9 @@ public class MmsManager { * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty */ - public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri, - Bundle configOverrides, PendingIntent downloadedIntent) { + public void downloadMultimediaMessage(int subId, @NonNull String locationUrl, + @NonNull Uri contentUri, @Nullable Bundle configOverrides, + @Nullable PendingIntent downloadedIntent) { try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp index 1aebeaf1e7e8..26c7f8d709e7 100644 --- a/native/graphics/jni/bitmap.cpp +++ b/native/graphics/jni/bitmap.cpp @@ -16,6 +16,7 @@ #include <android/bitmap.h> #include <android/graphics/bitmap.h> +#include <android/data_space.h> int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info) { @@ -29,6 +30,15 @@ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, return ANDROID_BITMAP_RESULT_SUCCESS; } +int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) { + if (NULL == env || NULL == jbitmap) { + return ADATASPACE_UNKNOWN; // Or return a real error? + } + + android::graphics::Bitmap bitmap(env, jbitmap); + return bitmap.getDataSpace(); +} + int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) { if (NULL == env || NULL == jbitmap) { return ANDROID_BITMAP_RESULT_BAD_PARAMETER; diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt index a601d8af2830..6adb95520d6c 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -1,6 +1,7 @@ LIBJNIGRAPHICS { global: AndroidBitmap_getInfo; + AndroidBitmap_getDataSpace; AndroidBitmap_lockPixels; AndroidBitmap_unlockPixels; local: diff --git a/opengl/java/android/opengl/EGL14.java b/opengl/java/android/opengl/EGL14.java index 728e6e18cc31..90b46fd5901a 100644 --- a/opengl/java/android/opengl/EGL14.java +++ b/opengl/java/android/opengl/EGL14.java @@ -18,11 +18,11 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; import android.view.Surface; -import android.view.SurfaceView; import android.view.SurfaceHolder; +import android.view.SurfaceView; /** * EGL 1.4 diff --git a/opengl/java/android/opengl/GLES20.java b/opengl/java/android/opengl/GLES20.java index d66e7ac84a3b..e853e4447daa 100644 --- a/opengl/java/android/opengl/GLES20.java +++ b/opengl/java/android/opengl/GLES20.java @@ -19,7 +19,7 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** OpenGL ES 2.0 */ diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 8a3e6a0b0fd5..75131b0f6b9c 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -16,7 +16,7 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Trace; import android.util.AttributeSet; diff --git a/opengl/java/javax/microedition/khronos/egl/EGL10.java b/opengl/java/javax/microedition/khronos/egl/EGL10.java index 8a2517062d4d..ea571c7311a1 100644 --- a/opengl/java/javax/microedition/khronos/egl/EGL10.java +++ b/opengl/java/javax/microedition/khronos/egl/EGL10.java @@ -16,8 +16,7 @@ package javax.microedition.khronos.egl; -import android.annotation.UnsupportedAppUsage; -import java.lang.String; +import android.compat.annotation.UnsupportedAppUsage; public interface EGL10 extends EGL { int EGL_SUCCESS = 0x3000; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java index ed945e7d4e72..f7802d205a3a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java @@ -18,6 +18,7 @@ package com.android.systemui.car; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.logging.NotifLog; @@ -40,8 +41,9 @@ public class CarNotificationEntryManager extends NotificationEntryManager { NotifLog notifLog, NotificationGroupManager groupManager, NotificationRankingManager rankingManager, - KeyguardEnvironment keyguardEnvironment) { - super(notifLog, groupManager, rankingManager, keyguardEnvironment); + KeyguardEnvironment keyguardEnvironment, + FeatureFlags featureFlags) { + super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags); } @Override diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 1e19786f5d41..ec445d4dcbee 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -160,7 +160,7 @@ public class ExternalStorageProvider extends FileSystemProvider { final int userId = UserHandle.myUserId(); final List<VolumeInfo> volumes = mStorageManager.getVolumes(); for (VolumeInfo volume : volumes) { - if (!volume.isMountedReadable()) continue; + if (!volume.isMountedReadable() || volume.getMountUserId() != userId) continue; final String rootId; final String title; @@ -192,9 +192,8 @@ public class ExternalStorageProvider extends FileSystemProvider { title = mStorageManager.getBestVolumeDescription(privateVol); storageUuid = StorageManager.convert(privateVol.fsUuid); } - } else if ((volume.getType() == VolumeInfo.TYPE_PUBLIC - || volume.getType() == VolumeInfo.TYPE_STUB) - && volume.getMountUserId() == userId) { + } else if (volume.getType() == VolumeInfo.TYPE_PUBLIC + || volume.getType() == VolumeInfo.TYPE_STUB) { rootId = volume.getFsUuid(); title = mStorageManager.getBestVolumeDescription(volume); storageUuid = null; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index a2bd210b67a6..a784e04ee6a0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; @@ -148,17 +151,18 @@ public class A2dpProfile implements LocalBluetoothProfile { } public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - return mService.connect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService == null) { + return false; } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -182,12 +186,12 @@ public class A2dpProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -197,11 +201,11 @@ public class A2dpProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } boolean isA2dpPlaying() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index bc03c343a909..8ca5a74652dc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; @@ -116,18 +119,15 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.connect(device); + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { if (mService == null) { return false; } - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -141,12 +141,12 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -156,11 +156,11 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index 560cb3b9b5b4..d65b5da22056 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -112,18 +115,15 @@ public class HeadsetProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.connect(device); + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { if (mService == null) { return false; } - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -165,12 +165,12 @@ public class HeadsetProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -180,11 +180,11 @@ public class HeadsetProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index b4b55f363020..9f1af669c708 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -146,17 +149,18 @@ public class HearingAidProfile implements LocalBluetoothProfile { } public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - return mService.connect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - // Downgrade priority as user is disconnecting the hearing aid. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService == null) { + return false; } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -180,12 +184,12 @@ public class HearingAidProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -195,11 +199,11 @@ public class HearingAidProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java index a372e23654e0..678f2e37c6bf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -126,7 +129,7 @@ final class HfpClientProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.connect(device); + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } @Override @@ -134,11 +137,8 @@ final class HfpClientProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } @Override @@ -154,13 +154,13 @@ final class HfpClientProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } @Override public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -171,11 +171,11 @@ final class HfpClientProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index 975a1e67af5b..588083e73481 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -99,13 +102,17 @@ public class HidProfile implements LocalBluetoothProfile { } public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - return mService.connect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - return mService.disconnect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -119,12 +126,12 @@ public class HidProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -132,11 +139,11 @@ public class HidProfile implements LocalBluetoothProfile { public void setPreferred(BluetoothDevice device, boolean preferred) { if (mService == null) return; if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java index 95139a1bfab9..7d121aaa1ad1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -115,18 +118,15 @@ public final class MapClientProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.connect(device); + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { if (mService == null) { return false; } - // Downgrade priority as user is disconnecting. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -140,12 +140,12 @@ public final class MapClientProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -155,11 +155,11 @@ public final class MapClientProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java index 31a0eea56b42..a96a4e73feea 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java @@ -16,6 +16,8 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -119,10 +121,8 @@ public class MapProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -136,12 +136,12 @@ public class MapProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -155,7 +155,7 @@ public class MapProfile implements LocalBluetoothProfile { mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java index 4ea0df621bea..56267fc596cf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -129,7 +132,7 @@ public final class PbapClientProfile implements LocalBluetoothProfile { return false; } Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress()); - return mService.connect(device); + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { @@ -137,7 +140,7 @@ public final class PbapClientProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.disconnect(device); + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -151,12 +154,12 @@ public final class PbapClientProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -166,11 +169,11 @@ public final class PbapClientProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java index 3f920a8cf1dd..f7c0bf5c8c9d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java @@ -16,6 +16,8 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -96,8 +98,10 @@ public class PbapServerProfile implements LocalBluetoothProfile { } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - return mService.disconnect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java index 0ca4d6195a32..3022c5b566eb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -112,17 +115,15 @@ final class SapProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.connect(device); + return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } public boolean disconnect(BluetoothDevice device) { if (mService == null) { return false; } - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); + + return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -136,12 +137,12 @@ final class SapProfile implements LocalBluetoothProfile { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } public int getPreferred(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @@ -151,11 +152,11 @@ final class SapProfile implements LocalBluetoothProfile { return; } if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 5b4ef3a47386..70b56ed0b391 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -194,6 +194,14 @@ public class LocalMediaManager implements BluetoothCallback { } } + void dispatchDeviceAttributesChanged() { + synchronized (mCallbacks) { + for (DeviceCallback callback : mCallbacks) { + callback.onDeviceAttributesChanged(); + } + } + } + /** * Stop scan MediaDevice */ @@ -306,14 +314,12 @@ public class LocalMediaManager implements BluetoothCallback { } mCurrentConnectedDevice = connectDevice; updatePhoneMediaDeviceSummary(); - dispatchDeviceListUpdate(); + dispatchDeviceAttributesChanged(); } @Override public void onDeviceAttributesChanged() { - addPhoneDeviceIfNecessary(); - removePhoneMediaDeviceIfNecessary(); - dispatchDeviceListUpdate(); + dispatchDeviceAttributesChanged(); } } @@ -327,7 +333,7 @@ public class LocalMediaManager implements BluetoothCallback { * * @param devices MediaDevice list */ - void onDeviceListUpdate(List<MediaDevice> devices); + default void onDeviceListUpdate(List<MediaDevice> devices) {}; /** * Callback for notifying the connected device is changed. @@ -338,6 +344,12 @@ public class LocalMediaManager implements BluetoothCallback { * {@link MediaDeviceState#STATE_CONNECTING}, * {@link MediaDeviceState#STATE_DISCONNECTED} */ - void onSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state); + default void onSelectedDeviceStateChanged(MediaDevice device, + @MediaDeviceState int state) {}; + + /** + * Callback for notifying the device attributes is changed. + */ + default void onDeviceAttributesChanged() {}; } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java index 976445eb8c04..ccb6646cf683 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; @@ -67,13 +70,13 @@ public class A2dpSinkProfileTest { @Test public void connect_shouldConnectBluetoothA2dpSink() { mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); } @Test public void disconnect_shouldDisconnectBluetoothA2dpSink() { mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java index 69c020dd5c08..91807609df1a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; @@ -67,13 +70,13 @@ public class HfpClientProfileTest { @Test public void connect_shouldConnectBluetoothHeadsetClient() { mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); } @Test public void disconnect_shouldDisconnectBluetoothHeadsetClient() { mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java index 6f667094a5aa..1425c381256b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; @@ -67,13 +70,13 @@ public class MapClientProfileTest { @Test public void connect_shouldConnectBluetoothMapClient() { mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); } @Test public void disconnect_shouldDisconnectBluetoothMapClient() { mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java index b21ec9c3e52a..15f560bef73e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; @@ -67,13 +70,13 @@ public class PbapClientProfileTest { @Test public void connect_shouldConnectBluetoothPbapClient() { mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); } @Test public void disconnect_shouldDisconnectBluetoothPbapClient() { mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java index ec880345f6f0..4f978a822890 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; @@ -66,13 +69,13 @@ public class SapProfileTest { @Test public void connect_shouldConnectBluetoothSap() { mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); } @Test public void disconnect_shouldDisconnectBluetoothSap() { mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); + verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 98bb74ad0718..894aa78a978e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -355,7 +355,7 @@ public class LocalMediaManagerTest { mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2); assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2); - verify(mCallback).onDeviceListUpdate(any()); + verify(mCallback).onDeviceAttributesChanged(); } @Test @@ -373,7 +373,7 @@ public class LocalMediaManagerTest { mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1); - verify(mCallback, never()).onDeviceListUpdate(any()); + verify(mCallback, never()).onDeviceAttributesChanged(); } @Test @@ -382,6 +382,6 @@ public class LocalMediaManagerTest { mLocalMediaManager.mMediaDeviceCallback.onDeviceAttributesChanged(); - verify(mCallback).onDeviceListUpdate(any()); + verify(mCallback).onDeviceAttributesChanged(); } } diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index f40d3a160513..96a98dca8a5b 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -8,6 +8,7 @@ android_app { libs: [ "telephony-common", "ims-common", + "unsupportedappusage", ], static_libs: [ "junit", @@ -40,6 +41,7 @@ android_test { libs: [ "android.test.base", "android.test.mock", + "unsupportedappusage", ], resource_dirs: ["res"], aaptflags: [ diff --git a/packages/SettingsProvider/res/values/overlayable.xml b/packages/SettingsProvider/res/values/overlayable.xml deleted file mode 100644 index dc41a77d0e2d..000000000000 --- a/packages/SettingsProvider/res/values/overlayable.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?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/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 049b9f0e903e..5636cc8a3ca3 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -16,7 +16,7 @@ package android.provider.settings.backup; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.provider.Settings; /** Information relating to the Secure settings which should be backed up */ diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 89b19de8dfcb..3f5b0daa06a7 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -16,7 +16,7 @@ package android.provider.settings.backup; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.provider.Settings; /** Information about the system settings to back up */ diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 94ab0f11927d..c5d4fa9f1b40 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -25,7 +25,7 @@ import static android.provider.settings.validators.SettingsValidators.URI_VALIDA import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.hardware.display.ColorDisplayManager; import android.os.BatteryManager; diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md index 56a637fe588c..28657f28e53b 100644 --- a/packages/SystemUI/docs/broadcasts.md +++ b/packages/SystemUI/docs/broadcasts.md @@ -42,24 +42,29 @@ Acquire the dispatcher by using `@Inject` to obtain a `BroadcastDispatcher`. The ```kotlin /** - * Register a receiver for broadcast with the dispatcher - * - * @param receiver A receiver to dispatch the [Intent] - * @param filter A filter to determine what broadcasts should be dispatched to this receiver. - * It will only take into account actions and categories for filtering. It must - * have at least one action. - * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the - * main handler. Pass `null` to use the default. - * @param user A user handle to determine which broadcast should be dispatched to this receiver. - * By default, it is the current user. - * @throws IllegalArgumentException if the filter has other constraints that are not actions or - * categories or the filter has no actions. - */ + * Register a receiver for broadcast with the dispatcher + * + * @param receiver A receiver to dispatch the [Intent] + * @param filter A filter to determine what broadcasts should be dispatched to this receiver. + * It will only take into account actions and categories for filtering. It must + * have at least one action. + * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an + * executor in the main thread (default). + * @param user A user handle to determine which broadcast should be dispatched to this receiver. + * By default, it is the current user. + * @throws IllegalArgumentException if the filter has other constraints that are not actions or + * categories or the filter has no actions. + */ @JvmOverloads -fun registerReceiver(BroadcastReceiver, IntentFilter, Handler? = mainHandler, UserHandle = context.user) +fun registerReceiver( + BroadcastReceiver, + IntentFilter, + Executor? = context.mainExecutor, + UserHandle = context.user +) { ``` -All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Handler`, pass `null` for the `Handler`. +All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Executor`, pass `null` for the `Executor`. In the same way as with `Context`, subscribing the same `BroadcastReceiver` for the same user using different filters will result on two subscriptions, not in replacing the filter. diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml new file mode 100644 index 000000000000..e91f840fe238 --- /dev/null +++ b/packages/SystemUI/res/layout/media_carousel.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Carousel for media controls --> +<HorizontalScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_height" + android:padding="@dimen/qs_media_padding" + android:scrollbars="none" + android:visibility="gone" + > + <LinearLayout + android:id="@+id/media_carousel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + <!-- QSMediaPlayers will be added here dynamically --> + </LinearLayout> +</HorizontalScrollView> diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml index f0ac08bdad37..982aa8ef6d16 100644 --- a/packages/SystemUI/res/layout/people_strip.xml +++ b/packages/SystemUI/res/layout/people_strip.xml @@ -18,7 +18,10 @@ <com.android.systemui.statusbar.notification.stack.PeopleHubView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="105dp"> + android:layout_height="@dimen/notification_section_header_height" + android:focusable="true" + android:clickable="true" +> <com.android.systemui.statusbar.notification.row.NotificationBackgroundView android:id="@+id/backgroundNormal" @@ -34,199 +37,56 @@ android:id="@+id/people_list" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="12dp" - android:paddingBottom="12dp" android:gravity="center" android:orientation="horizontal"> - <View - android:layout_width="8dp" - android:layout_height="match_parent" + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:layout_weight="1" + android:layout_marginStart="@dimen/notification_section_header_padding_left" + android:gravity="start" + android:textAlignment="gravity" + android:text="@string/notification_section_header_conversations" + android:textSize="12sp" + android:textColor="@color/notification_section_header_label_color" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" /> - <LinearLayout - android:layout_width="70dp" - android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:visibility="invisible"> - - <ImageView - android:id="@+id/person_icon" - android:layout_width="36dp" - android:layout_height="36dp" - android:scaleType="fitCenter" - /> - - <TextView - android:id="@+id/person_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:ellipsize="end" - android:maxLines="2" - android:textAlignment="center" - /> - - </LinearLayout> - - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:scaleType="fitCenter" /> - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:scaleType="fitCenter" /> - <LinearLayout - android:layout_width="70dp" - android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:visibility="invisible"> - - <ImageView - android:id="@+id/person_icon" - android:layout_width="36dp" - android:layout_height="36dp" - android:scaleType="fitCenter" - /> - - <TextView - android:id="@+id/person_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:ellipsize="end" - android:maxLines="2" - android:textAlignment="center" - /> - - </LinearLayout> - - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:scaleType="fitCenter" /> - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" - /> - - <LinearLayout - android:layout_width="70dp" - android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:visibility="invisible"> - - <ImageView - android:id="@+id/person_icon" - android:layout_width="36dp" - android:layout_height="36dp" - android:scaleType="fitCenter" - /> - - <TextView - android:id="@+id/person_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:ellipsize="end" - android:maxLines="2" - android:textAlignment="center" - /> - - </LinearLayout> - - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:scaleType="fitCenter" /> - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" - /> - - <LinearLayout - android:layout_width="70dp" - android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:visibility="invisible"> - - <ImageView - android:id="@+id/person_icon" - android:layout_width="36dp" - android:layout_height="36dp" - android:scaleType="fitCenter" - /> - - <TextView - android:id="@+id/person_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:ellipsize="end" - android:maxLines="2" - android:textAlignment="center" - /> - - </LinearLayout> - - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" - /> - - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" - /> - - <LinearLayout - android:layout_width="70dp" - android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" - android:visibility="invisible"> - - <ImageView - android:id="@+id/person_icon" - android:layout_width="36dp" - android:layout_height="36dp" - android:scaleType="fitCenter" - /> - - <TextView - android:id="@+id/person_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:ellipsize="end" - android:maxLines="2" - android:textAlignment="center" - /> - - </LinearLayout> - - <View - android:layout_width="8dp" - android:layout_height="match_parent" - android:layout_weight="1" + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginEnd="8dp" + android:padding="8dp" + android:scaleType="fitCenter" /> </LinearLayout> @@ -236,4 +96,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> -</com.android.systemui.statusbar.notification.stack.PeopleHubView>
\ No newline at end of file +</com.android.systemui.statusbar.notification.stack.PeopleHubView> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 52c0a26f09ba..4f532b7b751d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1163,6 +1163,9 @@ <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] --> <string name="notification_section_header_gentle">Silent notifications</string> + <!-- Section title for conversational notifications. [CHAR LIMIT=40] --> + <string name="notification_section_header_conversations">Conversations</string> + <!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] --> <string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 431862f1cc11..a58e3d78bb08 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1638,7 +1638,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); - broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mHandler); + broadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler); final IntentFilter allUserFilter = new IntentFilter(); allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); @@ -1649,8 +1649,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab allUserFilter.addAction(ACTION_USER_UNLOCKED); allUserFilter.addAction(ACTION_USER_STOPPED); allUserFilter.addAction(ACTION_USER_REMOVED); - broadcastDispatcher.registerReceiver(mBroadcastAllReceiver, allUserFilter, mHandler, - UserHandle.ALL); + broadcastDispatcher.registerReceiverWithHandler(mBroadcastAllReceiver, allUserFilter, + mHandler, UserHandle.ALL); mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener); try { diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index eecc54c678c0..bbe972dea11f 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -121,6 +121,7 @@ import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakReporter; import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.wm.DisplayImeController; import com.android.systemui.wm.DisplayWindowController; import com.android.systemui.wm.SystemWindows; @@ -321,6 +322,7 @@ public class Dependency { @Inject Lazy<StatusBar> mStatusBar; @Inject Lazy<DisplayWindowController> mDisplayWindowController; @Inject Lazy<SystemWindows> mSystemWindows; + @Inject Lazy<DisplayImeController> mDisplayImeController; @Inject public Dependency() { @@ -509,6 +511,7 @@ public class Dependency { mProviders.put(StatusBar.class, mStatusBar::get); mProviders.put(DisplayWindowController.class, mDisplayWindowController::get); mProviders.put(SystemWindows.class, mSystemWindows::get); + mProviders.put(DisplayImeController.class, mDisplayImeController::get); // TODO(b/118592525): to support multi-display , we start to add something which is // per-display, while others may be global. I think it's time to add diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt index 8c7075bee6cc..f14c4cd8e6c6 100644 --- a/packages/SystemUI/src/com/android/systemui/DumpController.kt +++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt @@ -30,6 +30,14 @@ import javax.inject.Singleton /** * Controller that allows any [Dumpable] to subscribe and be dumped along with other SystemUI * dependencies. + * + * To dump a specific dumpable on-demand: + * + * ``` + * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService dependency DumpController <tag1>,<tag2>,<tag3> + * ``` + * + * Where tag1, tag2, etc. are the tags of the dumpables you want to dump. */ @Singleton class DumpController @Inject constructor() : Dumpable { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 0e736dcd11a8..c533755c76da 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -266,7 +266,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); - mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter, mHandler); + mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt index 8cb0cc5db1d3..cedf7c354ccc 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.IntentFilter import android.os.Handler +import android.os.HandlerExecutor import android.os.Looper import android.os.Message import android.os.UserHandle @@ -32,13 +33,14 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import java.io.FileDescriptor import java.io.PrintWriter +import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton data class ReceiverData( val receiver: BroadcastReceiver, val filter: IntentFilter, - val handler: Handler, + val executor: Executor, val user: UserHandle ) @@ -76,24 +78,49 @@ open class BroadcastDispatcher @Inject constructor ( * @param filter A filter to determine what broadcasts should be dispatched to this receiver. * It will only take into account actions and categories for filtering. It must * have at least one action. - * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the - * main handler. Pass `null` to use the default. + * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. * @param user A user handle to determine which broadcast should be dispatched to this receiver. * By default, it is the current user. * @throws IllegalArgumentException if the filter has other constraints that are not actions or * categories or the filter has no actions. */ + @Deprecated(message = "Replacing Handler for Executor in SystemUI", + replaceWith = ReplaceWith("registerReceiver(receiver, filter, executor, user)")) @JvmOverloads - fun registerReceiver( + fun registerReceiverWithHandler( receiver: BroadcastReceiver, filter: IntentFilter, - handler: Handler? = mainHandler, + handler: Handler, user: UserHandle = context.user ) { + registerReceiver(receiver, filter, HandlerExecutor(handler), user) + } + + /** + * Register a receiver for broadcast with the dispatcher + * + * @param receiver A receiver to dispatch the [Intent] + * @param filter A filter to determine what broadcasts should be dispatched to this receiver. + * It will only take into account actions and categories for filtering. It must + * have at least one action. + * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an + * executor in the main thread (default). + * @param user A user handle to determine which broadcast should be dispatched to this receiver. + * By default, it is the current user. + * @throws IllegalArgumentException if the filter has other constraints that are not actions or + * categories or the filter has no actions. + */ + @JvmOverloads + fun registerReceiver( + receiver: BroadcastReceiver, + filter: IntentFilter, + executor: Executor? = context.mainExecutor, + user: UserHandle = context.user + ) { checkFilter(filter) this.handler .obtainMessage(MSG_ADD_RECEIVER, - ReceiverData(receiver, filter, handler ?: mainHandler, user)) + ReceiverData(receiver, filter, executor ?: context.mainExecutor, user)) .sendToTarget() } diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt index b2942bb14c6b..0c631aacab82 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt @@ -193,7 +193,7 @@ class UserBroadcastDispatcher( it.filter.hasAction(intent.action) && it.filter.matchCategories(intent.categories) == null } ?.forEach { - it.handler.post { + it.executor.execute { if (DEBUG) Log.w(TAG, "[$index] Dispatching ${intent.action} to ${it.receiver}") it.receiver.pendingResult = pendingResult diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 7cd29ea48199..d835ee1865bf 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -344,11 +344,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mSavedBubbleKeysPerUser = new SparseSetArray<>(); mCurrentUserId = mNotifUserManager.getCurrentUserId(); mNotifUserManager.addUserChangedListener( - newUserId -> { - saveBubbles(mCurrentUserId); - mBubbleData.dismissAll(DISMISS_USER_CHANGED); - restoreBubbles(newUserId); - mCurrentUserId = newUserId; + new NotificationLockscreenUserManager.UserChangedListener() { + @Override + public void onUserChanged(int newUserId) { + BubbleController.this.saveBubbles(mCurrentUserId); + mBubbleData.dismissAll(DISMISS_USER_CHANGED); + BubbleController.this.restoreBubbles(newUserId); + mCurrentUserId = newUserId; + } }); mUserCreatedBubbles = new HashSet<>(); @@ -743,9 +746,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi return !isAutogroupSummary; } else { // If it's not a user dismiss it's a cancel. + for (int i = 0; i < bubbleChildren.size(); i++) { + // First check if any of these are user-created (i.e. experimental bubbles) + if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) { + // Experimental bubble! Intercept the removal. + return true; + } + } + // Not an experimental bubble, safe to remove. mBubbleData.removeSuppressedSummary(groupKey); - - // Remove any associated bubble children. + // Remove any associated bubble children with the summary. for (int i = 0; i < bubbleChildren.size(); i++) { Bubble bubbleChild = bubbleChildren.get(i); mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(), diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 59d68bca93c3..6528f3762473 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -97,13 +97,11 @@ public class ExpandedAnimationController private boolean mSpringingBubbleToTouch = false; private int mExpandedViewPadding; - private float mLauncherGridDiff; public ExpandedAnimationController(Point displaySize, int expandedViewPadding, int orientation) { updateOrientation(orientation, displaySize); mExpandedViewPadding = expandedViewPadding; - mLauncherGridDiff = 30f; } /** @@ -569,15 +567,7 @@ public class ExpandedAnimationController * @return Space between bubbles in row above expanded view. */ private float getSpaceBetweenBubbles() { - /** - * Ordered left to right: - * Screen edge - * [mExpandedViewPadding] - * Expanded view edge - * [launcherGridDiff] --- arbitrary value until launcher exports widths - * Launcher's app icon grid edge that we must match - */ - final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2; + final float rowMargins = mExpandedViewPadding * 2; final float maxRowWidth = getWidthForDisplayingBubbles() - rowMargins; final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 8d10552332ba..53a23b89f943 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -20,7 +20,6 @@ import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import android.app.INotificationManager; import android.content.Context; -import android.content.pm.IPackageManager; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.NightDisplayListener; import android.os.Handler; @@ -115,13 +114,6 @@ public class DependencyProvider { /** */ @Singleton @Provides - public IPackageManager provideIPackageManager() { - return IPackageManager.Stub.asInterface(ServiceManager.getService("package")); - } - - /** */ - @Singleton - @Provides public LayoutInflater providerLayoutInflater(Context context) { return LayoutInflater.from(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index bb12b53fec4c..26337b1f24b1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -27,6 +27,7 @@ import android.app.NotificationManager; import android.app.WallpaperManager; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.IPackageManager; import android.content.res.Resources; import android.hardware.SensorPrivacyManager; import android.os.Handler; @@ -123,6 +124,13 @@ public class SystemServicesModule { return WindowManagerGlobal.getWindowManagerService(); } + /** */ + @Singleton + @Provides + public IPackageManager provideIPackageManager() { + return IPackageManager.Stub.asInterface(ServiceManager.getService("package")); + } + @Singleton @Provides static KeyguardManager provideKeyguardManager(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index c9faf69cfd6f..4e887262659e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -91,7 +91,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi if (mDebuggable) { IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_AOD_BRIGHTNESS); - mBroadcastDispatcher.registerReceiver(this, filter, handler, UserHandle.ALL); + mBroadcastDispatcher.registerReceiverWithHandler(this, filter, handler, UserHandle.ALL); } } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 59ac329e1983..48750fa5e769 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -233,7 +233,7 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks { filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_USER_SWITCHED); - mBroadcastDispatcher.registerReceiver(this, filter, mHandler); + mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt index f710f7fc47e2..f66a1ece1868 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt @@ -68,7 +68,7 @@ class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTil override fun updateResources(): Boolean { with(mContext.resources) { smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size) - cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) + cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2 cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin) } requestLayout() diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java index f7e4c794836e..1077834e7146 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -124,7 +124,7 @@ public class QSMediaPlayer { } } }); - btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay)); + btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play)); btn.setImageTintList(ColorStateList.valueOf(mForegroundColor)); btn.setVisibility(View.VISIBLE); @@ -199,8 +199,7 @@ public class QSMediaPlayer { List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser()); if (info != null) { for (ResolveInfo inf : info) { - if (inf.activityInfo.packageName.equals(notif.contentIntent.getCreatorPackage())) { - Log.d(TAG, "Found receiver for package: " + inf); + if (inf.activityInfo.packageName.equals(mController.getPackageName())) { mRecvComponent = inf.getComponentInfo().getComponentName(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 51e352b30019..35b8312ba25c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -184,21 +184,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne // Add media carousel if (useQsMediaPlayer(context)) { - HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext); - mediaScrollView.setHorizontalScrollBarEnabled(false); - int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height); - int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); - LayoutParams lpView = new LayoutParams(LayoutParams.MATCH_PARENT, playerHeight); - lpView.setMarginStart(padding); - lpView.setMarginEnd(padding); - addView(mediaScrollView, lpView); - - LayoutParams lpCarousel = new LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.WRAP_CONTENT); - mMediaCarousel = new LinearLayout(mContext); - mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL); - mediaScrollView.addView(mMediaCarousel, lpCarousel); - mediaScrollView.setVisibility(View.GONE); + HorizontalScrollView mediaScrollView = (HorizontalScrollView) LayoutInflater.from( + mContext).inflate(R.layout.media_carousel, this, false); + mMediaCarousel = mediaScrollView.findViewById(R.id.media_carousel); + addView(mediaScrollView); } else { mMediaCarousel = null; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java index d40e25064352..cec1cb2fb53b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java @@ -106,7 +106,7 @@ public class QuickQSMediaPlayer { } } }); - btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay)); + btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play)); btn.setImageTintList(ColorStateList.valueOf(mForegroundColor)); btn.setVisibility(View.VISIBLE); } @@ -136,14 +136,25 @@ public class QuickQSMediaPlayer { * @param actionsContainer a LinearLayout containing the media action buttons * @param actionsToShow indices of which actions to display in the mini player * (max 3: Notification.MediaStyle.MAX_MEDIA_BUTTONS_IN_COMPACT) + * @param contentIntent Intent to send when user taps on the view */ public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, - View actionsContainer, int[] actionsToShow) { - Log.d(TAG, "Setting media session: " + token); + View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) { mToken = token; mForegroundColor = iconColor; mBackgroundColor = bgColor; - mController = new MediaController(mContext, token); + + String oldPackage = ""; + if (mController != null) { + oldPackage = mController.getPackageName(); + } + MediaController controller = new MediaController(mContext, token); + boolean samePlayer = mToken.equals(token) && oldPackage.equals(controller.getPackageName()); + if (mController != null && !samePlayer && !isPlaying(controller)) { + // Only update if this is a different session and currently playing + return; + } + mController = controller; MediaMetadata mMediaMetadata = mController.getMetadata(); // Try to find a receiver for the media button that matches this app @@ -153,7 +164,6 @@ public class QuickQSMediaPlayer { if (info != null) { for (ResolveInfo inf : info) { if (inf.activityInfo.packageName.equals(mController.getPackageName())) { - Log.d(TAG, "Found receiver for package: " + inf); mRecvComponent = inf.getComponentInfo().getComponentName(); } } @@ -165,6 +175,16 @@ public class QuickQSMediaPlayer { return; } + // Action + mMediaNotifView.setOnClickListener(v -> { + try { + contentIntent.send(); + mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Pending intent was canceled: " + e.getMessage()); + } + }); + // Album art addAlbumArtBackground(mMediaMetadata, mBackgroundColor); @@ -237,12 +257,12 @@ public class QuickQSMediaPlayer { * Check whether the media controlled by this player is currently playing * @return whether it is playing, or false if no controller information */ - public boolean isPlaying() { - if (mController == null) { + public boolean isPlaying(MediaController controller) { + if (controller == null) { return false; } - PlaybackState state = mController.getPlaybackState(); + PlaybackState state = controller.getPlaybackState(); if (state == null) { return false; } @@ -261,12 +281,11 @@ public class QuickQSMediaPlayer { private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) { Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius); - if (albumArt != null) { - Rect bounds = new Rect(); - mMediaNotifView.getBoundsOnScreen(bounds); - int width = bounds.width(); - int height = bounds.height(); - + Rect bounds = new Rect(); + mMediaNotifView.getBoundsOnScreen(bounds); + int width = bounds.width(); + int height = bounds.height(); + if (albumArt != null && width > 0 && height > 0) { Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true); Bitmap scaled = scaleBitmap(original, width, height); Canvas canvas = new Canvas(scaled); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index db52e7d37a92..b05d4fdf7db7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -85,20 +85,19 @@ public class QuickQSPanel extends QSPanel { mHorizontalLinearLayout.setClipChildren(false); mHorizontalLinearLayout.setClipToPadding(false); - LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1); - mTileLayout = new DoubleLineTileLayout(context); mMediaTileLayout = mTileLayout; mRegularTileLayout = new HeaderTileLayout(context); + LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1); lp.setMarginEnd(10); lp.setMarginStart(0); mHorizontalLinearLayout.addView((View) mTileLayout, lp); mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout); - - lp.setMarginEnd(0); - lp.setMarginStart(10); - mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp); + LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1); + lp2.setMarginEnd(0); + lp2.setMarginStart(25); + mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp2); sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java index bbff117c6f81..ad79cadcc12d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -322,7 +322,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); try { mUserReceiverRegistered.set(true); - mBroadcastDispatcher.registerReceiver(this, filter, mHandler, mUser); + mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, mUser); } catch (Exception ex) { mUserReceiverRegistered.set(false); Log.e(TAG, "Could not register unlock receiver", ex); diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java index 077d2602e43f..9599d77bf65a 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java @@ -98,7 +98,8 @@ public abstract class CurrentUserTracker { if (!mReceiverRegistered) { mCurrentUserId = ActivityManager.getCurrentUser(); IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); - mBroadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL); + mBroadcastDispatcher.registerReceiver(this, filter, null, + UserHandle.ALL); mReceiverRegistered = true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 2005d794c9d3..ac05c53c38dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -40,7 +40,7 @@ import javax.inject.Singleton; * You will probably need to restart systemui for the changes to be picked up: * * {@code - * $ adb shell am crash com.android.systemui + * $ adb shell am restart com.android.systemui * } */ @Singleton @@ -59,6 +59,11 @@ public class FeatureFlags { return getDeviceConfigFlag("notification.newpipeline.enabled", false); } + public boolean isNewNotifPipelineRenderingEnabled() { + return isNewNotifPipelineEnabled() + && getDeviceConfigFlag("notification.newpipeline.rendering", false); + } + private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { synchronized (mCachedDeviceConfigFlags) { for (String key : properties.getKeyset()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index ff4ce9492082..ebf7c2d58c2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -49,6 +49,12 @@ public interface NotificationLockscreenUserManager { /** Adds a listener to be notified when the current user changes. */ void addUserChangedListener(UserChangedListener listener); + /** + * Removes a listener previously registered with + * {@link #addUserChangedListener(UserChangedListener)} + */ + void removeUserChangedListener(UserChangedListener listener); + SparseArray<UserInfo> getCurrentProfiles(); void setLockscreenPublicMode(boolean isProfilePublic, int userId); @@ -79,6 +85,7 @@ public interface NotificationLockscreenUserManager { /** Notified when the current user changes. */ interface UserChangedListener { - void onUserChanged(int userId); + default void onUserChanged(int userId) {} + default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 2e369b3295b8..976531d8b49d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -117,51 +117,63 @@ public class NotificationLockscreenUserManagerImpl implements @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - updateCurrentProfilesCache(); - Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); - - updateLockscreenNotificationSetting(); - updatePublicMode(); - // The filtering needs to happen before the update call below in order to make sure - // the presenter has the updated notifications from the new user - getEntryManager().reapplyFilterAndSort("user switched"); - mPresenter.onUserSwitched(mCurrentUserId); - - for (UserChangedListener listener : mListeners) { - listener.onUserChanged(mCurrentUserId); - } - } else if (Intent.ACTION_USER_ADDED.equals(action)) { - updateCurrentProfilesCache(); - } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { - // Start the overview connection to the launcher service - Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser(); - } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) { - final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT); - final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX); - if (intentSender != null) { - try { - mContext.startIntentSender(intentSender, null, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - /* ignore */ + switch (action) { + case Intent.ACTION_USER_SWITCHED: + mCurrentUserId = intent.getIntExtra( + Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL); + updateCurrentProfilesCache(); + + Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); + + updateLockscreenNotificationSetting(); + updatePublicMode(); + // The filtering needs to happen before the update call below in order to + // make sure + // the presenter has the updated notifications from the new user + getEntryManager().reapplyFilterAndSort("user switched"); + mPresenter.onUserSwitched(mCurrentUserId); + + for (UserChangedListener listener : mListeners) { + listener.onUserChanged(mCurrentUserId); } - } - if (notificationKey != null) { - NotificationEntry entry = - getEntryManager().getActiveNotificationUnfiltered(notificationKey); - final int count = getEntryManager().getActiveNotificationsCount(); - final int rank = entry != null ? entry.getRanking().getRank() : 0; - NotificationVisibility.NotificationLocation location = - NotificationLogger.getNotificationLocation(entry); - final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey, - rank, count, true, location); - try { - mBarService.onNotificationClick(notificationKey, nv); - } catch (RemoteException exception) { - /* ignore */ + break; + case Intent.ACTION_USER_ADDED: + case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: + case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: + updateCurrentProfilesCache(); + break; + case Intent.ACTION_USER_UNLOCKED: + // Start the overview connection to the launcher service + Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser(); + break; + case NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION: + final IntentSender intentSender = intent.getParcelableExtra( + Intent.EXTRA_INTENT); + final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX); + if (intentSender != null) { + try { + mContext.startIntentSender(intentSender, null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + /* ignore */ + } } - } + if (notificationKey != null) { + NotificationEntry entry = + getEntryManager().getActiveNotificationUnfiltered(notificationKey); + final int count = getEntryManager().getActiveNotificationsCount(); + final int rank = entry != null ? entry.getRanking().getRank() : 0; + NotificationVisibility.NotificationLocation location = + NotificationLogger.getNotificationLocation(entry); + final NotificationVisibility nv = NotificationVisibility.obtain( + notificationKey, + rank, count, true, location); + try { + mBarService.onNotificationClick(notificationKey, nv); + } catch (RemoteException exception) { + /* ignore */ + } + } + break; } } }; @@ -266,6 +278,8 @@ public class NotificationLockscreenUserManagerImpl implements filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_UNLOCKED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter); IntentFilter internalFilter = new IntentFilter(); @@ -489,6 +503,11 @@ public class NotificationLockscreenUserManagerImpl implements } } } + mMainHandler.post(() -> { + for (UserChangedListener listener : mListeners) { + listener.onCurrentProfilesChanged(mCurrentProfiles); + } + }); } public boolean isAnyProfilePublicMode() { @@ -555,6 +574,11 @@ public class NotificationLockscreenUserManagerImpl implements mListeners.add(listener); } + @Override + public void removeUserChangedListener(UserChangedListener listener) { + mListeners.remove(listener); + } + // public void updatePublicMode() { // //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns // // false when it should be true. Therefore, if we are not on the SHADE, don't even bother diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 33d97a150048..a2578ab3cfa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -34,6 +34,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; @@ -131,6 +132,7 @@ public class NotificationEntryManager implements private final KeyguardEnvironment mKeyguardEnvironment; private final NotificationGroupManager mGroupManager; private final NotificationRankingManager mRankingManager; + private final FeatureFlags mFeatureFlags; private NotificationPresenter mPresenter; private RankingMap mLatestRankingMap; @@ -170,11 +172,13 @@ public class NotificationEntryManager implements NotifLog notifLog, NotificationGroupManager groupManager, NotificationRankingManager rankingManager, - KeyguardEnvironment keyguardEnvironment) { + KeyguardEnvironment keyguardEnvironment, + FeatureFlags featureFlags) { mNotifLog = notifLog; mGroupManager = groupManager; mRankingManager = rankingManager; mKeyguardEnvironment = keyguardEnvironment; + mFeatureFlags = featureFlags; } /** Once called, the NEM will start processing notification events from system server. */ @@ -290,6 +294,10 @@ public class NotificationEntryManager implements * WARNING: this will call back into us. Don't hold any locks. */ @Override + public void handleInflationException(NotificationEntry n, Exception e) { + handleInflationException(n.getSbn(), e); + } + public void handleInflationException(StatusBarNotification n, Exception e) { removeNotificationInternal( n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */, @@ -529,9 +537,12 @@ public class NotificationEntryManager implements NotificationEntry entry = new NotificationEntry(notification, ranking); Dependency.get(LeakDetector.class).trackInstance(entry); + // Construct the expanded view. - requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, - REASON_CANCEL)); + if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { + requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, + REASON_CANCEL)); + } abortExistingInflation(key, "addNotification"); mPendingNotifications.put(key, entry); @@ -574,8 +585,11 @@ public class NotificationEntryManager implements listener.onPreEntryUpdated(entry); } - requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, - REASON_CANCEL)); + if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { + requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, + REASON_CANCEL)); + } + updateNotifications("updateNotificationInternal"); if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java index ec1efa58868e..b960b42b3e2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Objects; /** * Represents a set of grouped notifications. The final notification list is usually a mix of @@ -58,22 +57,15 @@ public class GroupEntry extends ListEntry { @VisibleForTesting public void setSummary(@Nullable NotificationEntry summary) { - if (!Objects.equals(mSummary, summary)) { - mSummary = summary; - onGroupingUpdated(); - } + mSummary = summary; } void clearChildren() { - if (mChildren.size() != 0) { - mChildren.clear(); - onGroupingUpdated(); - } + mChildren.clear(); } void addChild(NotificationEntry child) { mChildren.add(child); - onGroupingUpdated(); } void sortChildren(Comparator<? super NotificationEntry> c) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java index 601b3e053e8e..dc68c4bdba78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java @@ -18,38 +18,20 @@ package com.android.systemui.statusbar.notification.collection; import android.annotation.Nullable; -import com.android.systemui.Dependency; -import com.android.systemui.statusbar.notification.collection.provider.DerivedMember; -import com.android.systemui.statusbar.notification.collection.provider.IsHighPriorityProvider; -import com.android.systemui.statusbar.phone.NotificationGroupManager; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - /** * Abstract superclass for top-level entries, i.e. things that can appear in the final notification * list shown to users. In practice, this means either GroupEntries or NotificationEntries. */ public abstract class ListEntry { private final String mKey; - private final IsHighPriorityProvider mIsHighPriorityProvider = new IsHighPriorityProvider(); - private final List<DerivedMember> mDerivedMemberList = Arrays.asList(mIsHighPriorityProvider); @Nullable private GroupEntry mParent; @Nullable private GroupEntry mPreviousParent; private int mSection; int mFirstAddedIteration = -1; - // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic - // replaced in GroupEntry and NotifListBuilderImpl - private final NotificationGroupManager mGroupManager; - ListEntry(String key) { mKey = key; - - // TODO: (b/145659174) remove - mGroupManager = Dependency.get(NotificationGroupManager.class); } public String getKey() { @@ -68,11 +50,7 @@ public abstract class ListEntry { } void setParent(@Nullable GroupEntry parent) { - if (!Objects.equals(mParent, parent)) { - invalidateParent(); - mParent = parent; - onGroupingUpdated(); - } + mParent = parent; } @Nullable public GroupEntry getPreviousParent() { @@ -91,58 +69,4 @@ public abstract class ListEntry { void setSection(int section) { mSection = section; } - - /** - * Resets the cached values of DerivedMembers. - */ - void invalidateDerivedMembers() { - for (int i = 0; i < mDerivedMemberList.size(); i++) { - mDerivedMemberList.get(i).invalidate(); - } - } - - /** - * Whether this notification is shown to the user as a high priority notification: visible on - * the lock screen/status bar and in the top section in the shade. - */ - public boolean isHighPriority() { - return mIsHighPriorityProvider.get(this); - } - - private void invalidateParent() { - // invalidate our parent (GroupEntry) since DerivedMembers may be dependent on children - if (getParent() != null) { - getParent().invalidateDerivedMembers(); - } - - // TODO: (b/145659174) remove - final NotificationEntry notifEntry = getRepresentativeEntry(); - if (notifEntry != null && mGroupManager.isGroupChild(notifEntry.getSbn())) { - NotificationEntry summary = mGroupManager.getLogicalGroupSummary(notifEntry.getSbn()); - if (summary != null) { - summary.invalidateDerivedMembers(); - } - } - } - - void onGroupingUpdated() { - for (int i = 0; i < mDerivedMemberList.size(); i++) { - mDerivedMemberList.get(i).onGroupingUpdated(); - } - invalidateParent(); - } - - void onSbnUpdated() { - for (int i = 0; i < mDerivedMemberList.size(); i++) { - mDerivedMemberList.get(i).onSbnUpdated(); - } - invalidateParent(); - } - - void onRankingUpdated() { - for (int i = 0; i < mDerivedMemberList.size(); i++) { - mDerivedMemberList.get(i).onRankingUpdated(); - } - invalidateParent(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 873cdbc91d25..856b75b7e36c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -299,6 +299,14 @@ public class NotifCollection { if (!isLifetimeExtended(entry)) { Ranking ranking = requireRanking(rankingMap, entry.getKey()); entry.setRanking(ranking); + + // TODO: (b/145659174) update the sbn's overrideGroupKey in + // NotificationEntry.setRanking instead of here once we fully migrate to the + // NewNotifPipeline + final String newOverrideGroupKey = ranking.getOverrideGroupKey(); + if (!Objects.equals(entry.getSbn().getOverrideGroupKey(), newOverrideGroupKey)) { + entry.getSbn().setOverrideGroupKey(newOverrideGroupKey); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java new file mode 100644 index 000000000000..fc04827a9d6a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; +import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator; + +/** + * Used by the {@link PreparationCoordinator}. When notifications are added or updated, the + * NotifInflater is asked to (re)inflated and prepare their views. This inflation occurs off the + * main thread. When the inflation is finished, NotifInflater will trigger its InflationCallback. + */ +public interface NotifInflater { + + /** + * Callback used when inflation is finished. + */ + void setInflationCallback(InflationCallback callback); + + /** + * Called to rebind the entry's views. + */ + void rebindViews(NotificationEntry entry); + + /** + * Called to inflate the views of an entry. Views are not considered inflated until all of its + * views are bound. Once all views are inflated, the InflationCallback is triggered. + */ + void inflateViews(NotificationEntry entry); + + /** + * Request to stop the inflation of an entry. For example, called when a notification is + * removed and no longer needs to be inflated. + */ + void abortInflation(NotificationEntry entry); + + /** + * Callback once all the views are inflated and bound for a given NotificationEntry. + */ + interface InflationCallback { + void onInflationFinished(NotificationEntry entry); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java new file mode 100644 index 000000000000..0d175574f16b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; + +import android.os.RemoteException; +import android.service.notification.NotificationStats; +import android.service.notification.StatusBarNotification; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.statusbar.notification.InflationException; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Handles notification inflating, rebinding, and inflation aborting. + * + * Currently a wrapper for NotificationRowBinderImpl. + */ +@Singleton +public class NotifInflaterImpl implements NotifInflater { + + private final IStatusBarService mStatusBarService; + private final NotifCollection mNotifCollection; + + private NotificationRowBinderImpl mNotificationRowBinder; + private InflationCallback mExternalInflationCallback; + + @Inject + public NotifInflaterImpl( + IStatusBarService statusBarService, + NotifCollection notifCollection) { + mStatusBarService = statusBarService; + mNotifCollection = notifCollection; + } + + /** + * Attaches the row binder for inflation. + */ + public void setRowBinder(NotificationRowBinderImpl rowBinder) { + mNotificationRowBinder = rowBinder; + mNotificationRowBinder.setInflationCallback(mInflationCallback); + } + + @Override + public void setInflationCallback(InflationCallback callback) { + mExternalInflationCallback = callback; + } + + @Override + public void rebindViews(NotificationEntry entry) { + inflateViews(entry); + } + + /** + * Called to inflate the views of an entry. Views are not considered inflated until all of its + * views are bound. + */ + @Override + public void inflateViews(NotificationEntry entry) { + try { + entry.setHasInflationError(false); + requireBinder().inflateViews(entry, getDismissCallback(entry)); + } catch (InflationException e) { + // logged in mInflationCallback.handleInflationException + } + } + + @Override + public void abortInflation(NotificationEntry entry) { + entry.abortTask(); + } + + private Runnable getDismissCallback(NotificationEntry entry) { + return new Runnable() { + @Override + public void run() { + int dismissalSurface = NotificationStats.DISMISSAL_SHADE; + /** + * TODO: determine dismissal surface (ie: shade / headsup / aod) + * see {@link NotificationLogger#logNotificationClear} + */ + mNotifCollection.dismissNotification( + entry, + 0, + new DismissedByUserStats( + dismissalSurface, + DISMISS_SENTIMENT_NEUTRAL, + NotificationVisibility.obtain(entry.getKey(), + entry.getRanking().getRank(), + mNotifCollection.getNotifs().size(), + true, + NotificationLogger.getNotificationLocation(entry)) + )); + } + }; + } + + private NotificationRowBinderImpl requireBinder() { + if (mNotificationRowBinder == null) { + throw new RuntimeException("NotificationRowBinder must be attached before using " + + "NotifInflaterImpl."); + } + return mNotificationRowBinder; + } + + private final NotificationContentInflater.InflationCallback mInflationCallback = + new NotificationContentInflater.InflationCallback() { + @Override + public void handleInflationException( + NotificationEntry entry, + Exception e) { + entry.setHasInflationError(true); + try { + final StatusBarNotification sbn = entry.getSbn(); + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + sbn.getPackageName(), + sbn.getTag(), + sbn.getId(), + sbn.getUid(), + sbn.getInitialPid(), + e.getMessage(), + sbn.getUserId()); + } catch (RemoteException ex) { + } + } + + @Override + public void onAsyncInflationFinished( + NotificationEntry entry, + int inflatedFlags) { + if (mExternalInflationCallback != null) { + mExternalInflationCallback.onInflationFinished(entry); + } + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 4f4fb240162d..7301fe1df398 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -102,6 +102,9 @@ public final class NotificationEntry extends ListEntry { /** If this was a group child that was promoted to the top level, then who did the promoting. */ @Nullable NotifPromoter mNotifPromoter; + /** If this notification had an issue with inflating. Only used with the NewNotifPipeline **/ + private boolean mHasInflationError; + /* * Old members @@ -201,11 +204,8 @@ public final class NotificationEntry extends ListEntry { + " doesn't match existing key " + mKey); } - if (!Objects.equals(mSbn, sbn)) { - mSbn = sbn; - mBubbleMetadata = mSbn.getNotification().getBubbleMetadata(); - onSbnUpdated(); - } + mSbn = sbn; + mBubbleMetadata = mSbn.getNotification().getBubbleMetadata(); } /** @@ -230,10 +230,7 @@ public final class NotificationEntry extends ListEntry { + " doesn't match existing key " + mKey); } - if (!Objects.equals(mRanking, ranking)) { - mRanking = ranking; - onRankingUpdated(); - } + mRanking = ranking; } /* @@ -576,6 +573,18 @@ public final class NotificationEntry extends ListEntry { remoteInputTextWhenReset = null; } + void setHasInflationError(boolean hasError) { + mHasInflationError = hasError; + } + + /** + * Whether this notification had an error when attempting to inflate. This is only used in + * the NewNotifPipeline + */ + public boolean hasInflationError() { + return mHasInflationError; + } + public void setHasSentReply() { hasSentReply = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index 7010943559ba..3bbd722517f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -24,6 +24,7 @@ import android.service.notification.StatusBarNotification import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.logging.NotifEvent import com.android.systemui.statusbar.notification.logging.NotifLog import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier @@ -54,7 +55,8 @@ open class NotificationRankingManager @Inject constructor( private val notifFilter: NotificationFilter, private val notifLog: NotifLog, sectionsFeatureManager: NotificationSectionsFeatureManager, - private val peopleNotificationIdentifier: PeopleNotificationIdentifier + private val peopleNotificationIdentifier: PeopleNotificationIdentifier, + private val highPriorityProvider: HighPriorityProvider ) { var rankingMap: RankingMap? = null @@ -81,6 +83,9 @@ open class NotificationRankingManager @Inject constructor( val aHeadsUp = a.isRowHeadsUp val bHeadsUp = b.isRowHeadsUp + val aIsHighPriority = a.isHighPriority() + val bIsHighPriority = b.isHighPriority() + when { usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1 aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1 @@ -90,8 +95,8 @@ open class NotificationRankingManager @Inject constructor( aMedia != bMedia -> if (aMedia) -1 else 1 // Upsort PRIORITY_MAX system notifications aSystemMax != bSystemMax -> if (aSystemMax) -1 else 1 - a.isHighPriority != b.isHighPriority -> - -1 * a.isHighPriority.compareTo(b.isHighPriority) + aIsHighPriority != bIsHighPriority -> + -1 * aIsHighPriority.compareTo(bIsHighPriority) aRank != bRank -> aRank - bRank else -> nb.notification.`when`.compareTo(na.notification.`when`) } @@ -154,7 +159,7 @@ open class NotificationRankingManager @Inject constructor( ) { if (usePeopleFiltering && entry.isPeopleNotification()) { entry.bucket = BUCKET_PEOPLE - } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) { + } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority()) { entry.bucket = BUCKET_ALERTING } else { entry.bucket = BUCKET_SILENT @@ -178,10 +183,6 @@ open class NotificationRankingManager @Inject constructor( // TODO: notify group manager here? groupManager.onEntryUpdated(entry, oldSbn) } - - // TODO: (b/145659174) remove after moving to new NotifPipeline - // (should be able to remove all groupManager code post-migration) - entry.invalidateDerivedMembers() } } } @@ -191,6 +192,9 @@ open class NotificationRankingManager @Inject constructor( sbn.isPeopleNotification() private fun StatusBarNotification.isPeopleNotification() = peopleNotificationIdentifier.isPeopleNotification(this) + + private fun NotificationEntry.isHighPriority() = + highPriorityProvider.isHighPriority(this) } // Convenience functions diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index 6c93618e5395..6dc647d33046 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java @@ -109,16 +109,18 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { public void setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, HeadsUpManager headsUpManager, - NotificationContentInflater.InflationCallback inflationCallback, BindRowCallback bindRowCallback) { mPresenter = presenter; mListContainer = listContainer; mHeadsUpManager = headsUpManager; - mInflationCallback = inflationCallback; mBindRowCallback = bindRowCallback; mOnAppOpsClickListener = mGutsManager::openGuts; } + public void setInflationCallback(NotificationContentInflater.InflationCallback callback) { + mInflationCallback = callback; + } + public void setNotificationClicker(NotificationClicker clicker) { mNotificationClicker = clicker; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index 9312c2260d46..db107f531e9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; @@ -62,6 +63,7 @@ public class KeyguardCoordinator implements Coordinator { private final BroadcastDispatcher mBroadcastDispatcher; private final StatusBarStateController mStatusBarStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final HighPriorityProvider mHighPriorityProvider; @Inject public KeyguardCoordinator( @@ -71,15 +73,16 @@ public class KeyguardCoordinator implements Coordinator { NotificationLockscreenUserManager lockscreenUserManager, BroadcastDispatcher broadcastDispatcher, StatusBarStateController statusBarStateController, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + HighPriorityProvider highPriorityProvider) { mContext = context; mMainHandler = mainThreadHandler; mKeyguardStateController = keyguardStateController; mLockscreenUserManager = lockscreenUserManager; - mBroadcastDispatcher = broadcastDispatcher; mStatusBarStateController = statusBarStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mHighPriorityProvider = highPriorityProvider; } @Override @@ -151,7 +154,7 @@ public class KeyguardCoordinator implements Coordinator { } if (NotificationUtils.useNewInterruptionModel(mContext) && hideSilentNotificationsOnLockscreen()) { - return entry.isHighPriority(); + return mHighPriorityProvider.isHighPriority(entry); } else { return entry.getRepresentativeEntry() != null && !entry.getRepresentativeEntry().getRanking().isAmbient(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index 132471933bb2..eeb54abf92b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import com.android.systemui.Dumpable; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender; @@ -45,14 +46,19 @@ public class NotifCoordinators implements Dumpable { */ @Inject public NotifCoordinators( + FeatureFlags featureFlags, KeyguardCoordinator keyguardCoordinator, RankingCoordinator rankingCoordinator, ForegroundCoordinator foregroundCoordinator, - DeviceProvisionedCoordinator deviceProvisionedCoordinator) { + DeviceProvisionedCoordinator deviceProvisionedCoordinator, + PreparationCoordinator preparationCoordinator) { mCoordinators.add(keyguardCoordinator); mCoordinators.add(rankingCoordinator); mCoordinators.add(foregroundCoordinator); mCoordinators.add(deviceProvisionedCoordinator); + if (featureFlags.isNewNotifPipelineRenderingEnabled()) { + mCoordinators.add(preparationCoordinator); + } // TODO: add new Coordinators here! (b/145134683, b/112656837) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java new file mode 100644 index 000000000000..a14f0e1cf631 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator; + +import com.android.systemui.statusbar.notification.collection.NotifCollection; +import com.android.systemui.statusbar.notification.collection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.NotifInflater; +import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; +import com.android.systemui.statusbar.notification.logging.NotifEvent; +import com.android.systemui.statusbar.notification.logging.NotifLog; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Kicks off notification inflation and view rebinding when a notification is added or updated. + * Aborts inflation when a notification is removed. + * + * If a notification is not done inflating, this coordinator will filter the notification out + * from the NotifListBuilder. + */ +@Singleton +public class PreparationCoordinator implements Coordinator { + private static final String TAG = "PreparationCoordinator"; + + private final NotifLog mNotifLog; + private final NotifInflater mNotifInflater; + private final List<NotificationEntry> mPendingNotifications = new ArrayList<>(); + + @Inject + public PreparationCoordinator(NotifLog notifLog, NotifInflaterImpl notifInflater) { + mNotifLog = notifLog; + mNotifInflater = notifInflater; + mNotifInflater.setInflationCallback(mInflationCallback); + } + + @Override + public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) { + notifCollection.addCollectionListener(mNotifCollectionListener); + notifListBuilder.addPreRenderFilter(mNotifInflationErrorFilter); + notifListBuilder.addPreRenderFilter(mNotifInflatingFilter); + } + + private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() { + @Override + public void onEntryAdded(NotificationEntry entry) { + inflateEntry(entry, "entryAdded"); + } + + @Override + public void onEntryUpdated(NotificationEntry entry) { + rebind(entry, "entryUpdated"); + } + + @Override + public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) { + abortInflation(entry, "entryRemoved reason=" + reason); + } + }; + + private final NotifFilter mNotifInflationErrorFilter = new NotifFilter( + TAG + "InflationError") { + /** + * Filters out notifications that threw an error when attempting to inflate. + */ + @Override + public boolean shouldFilterOut(NotificationEntry entry, long now) { + if (entry.hasInflationError()) { + mPendingNotifications.remove(entry); + return true; + } + return false; + } + }; + + private final NotifFilter mNotifInflatingFilter = new NotifFilter(TAG + "Inflating") { + /** + * Filters out notifications that haven't been inflated yet + */ + @Override + public boolean shouldFilterOut(NotificationEntry entry, long now) { + return mPendingNotifications.contains(entry); + } + }; + + private final NotifInflater.InflationCallback mInflationCallback = + new NotifInflater.InflationCallback() { + @Override + public void onInflationFinished(NotificationEntry entry) { + mNotifLog.log(NotifEvent.INFLATED, entry); + mPendingNotifications.remove(entry); + mNotifInflatingFilter.invalidateList(); + } + }; + + private void inflateEntry(NotificationEntry entry, String reason) { + abortInflation(entry, reason); + mPendingNotifications.add(entry); + mNotifInflater.inflateViews(entry); + } + + private void rebind(NotificationEntry entry, String reason) { + mNotifInflater.rebindViews(entry); + } + + private void abortInflation(NotificationEntry entry, String reason) { + mNotifLog.log(NotifEvent.INFLATION_ABORTED, reason); + entry.abortTask(); + mPendingNotifications.remove(entry); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java index 5e0bd4fdcf23..8d3d0ff43deb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java @@ -20,9 +20,12 @@ import android.util.Log; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.notification.collection.NotifCollection; +import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators; import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer; @@ -41,7 +44,9 @@ public class NewNotifPipeline implements Dumpable { private final NotifCollection mNotifCollection; private final NotifListBuilderImpl mNotifPipeline; private final NotifCoordinators mNotifPluggableCoordinators; + private final NotifInflaterImpl mNotifInflater; private final DumpController mDumpController; + private final FeatureFlags mFeatureFlags; private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer(); @@ -51,20 +56,30 @@ public class NewNotifPipeline implements Dumpable { NotifCollection notifCollection, NotifListBuilderImpl notifPipeline, NotifCoordinators notifCoordinators, - DumpController dumpController) { + NotifInflaterImpl notifInflater, + DumpController dumpController, + FeatureFlags featureFlags) { mGroupCoalescer = groupCoalescer; mNotifCollection = notifCollection; mNotifPipeline = notifPipeline; mNotifPluggableCoordinators = notifCoordinators; mDumpController = dumpController; + mNotifInflater = notifInflater; + mFeatureFlags = featureFlags; } /** Hooks the new pipeline up to NotificationManager */ public void initialize( - NotificationListener notificationService) { + NotificationListener notificationService, + NotificationRowBinderImpl rowBinder) { mDumpController.registerDumpable("NotifPipeline", this); + // Setup inflation + if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { + mNotifInflater.setRowBinder(rowBinder); + } + // Wire up coordinators mFakePipelineConsumer.attach(mNotifPipeline); mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java deleted file mode 100644 index 815e6f7eaa46..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java +++ /dev/null @@ -1,62 +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.statusbar.notification.collection.provider; -/** - * Caches a computed value until invalidate() is called - * @param <Parent> Object used to computeValue - * @param <Value> type of value to cache until invalidate is called - */ -public abstract class DerivedMember<Parent, Value> { - private Value mValue; - protected abstract Value computeValue(Parent parent); - - /** - * Gets the last cached value, else recomputes the value. - */ - public Value get(Parent parent) { - if (mValue == null) { - mValue = computeValue(parent); - } - return mValue; - } - - /** - * Resets the cached value. - * Next time "get" is called, the value is recomputed. - */ - public void invalidate() { - mValue = null; - } - - /** - * Called when a NotificationEntry's status bar notification has updated. - * Derived members can invalidate here. - */ - public void onSbnUpdated() {} - - /** - * Called when a NotificationEntry's Ranking has updated. - * Derived members can invalidate here. - */ - public void onRankingUpdated() {} - - /** - * Called when a ListEntry's grouping information (parent or children) has changed. - * Derived members can invalidate here. - */ - public void onGroupingUpdated() {} -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java new file mode 100644 index 000000000000..3cc5e62c0bda --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.provider; + +import android.app.Notification; +import android.app.NotificationManager; + +import com.android.systemui.statusbar.notification.collection.GroupEntry; +import com.android.systemui.statusbar.notification.collection.ListEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Determines whether a notification is considered 'high priority'. + * + * Notifications that are high priority are visible on the lock screen/status bar and in the top + * section in the shade. + */ +@Singleton +public class HighPriorityProvider { + private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; + + @Inject + public HighPriorityProvider(PeopleNotificationIdentifier peopleNotificationIdentifier) { + mPeopleNotificationIdentifier = peopleNotificationIdentifier; + } + + /** + * @return true if the ListEntry is high priority, else false + * + * A NotificationEntry is considered high priority if it: + * - has importance greater than or equal to IMPORTANCE_DEFAULT + * OR + * - their importance has NOT been set to a low priority option by the user AND the + * notification fulfills one of the following: + * - has a person associated with it + * - has a media session associated with it + * - has messaging style + * + * A GroupEntry is considered high priority if its representativeEntry (summary) or children are + * high priority + */ + public boolean isHighPriority(ListEntry entry) { + if (entry == null) { + return false; + } + + final NotificationEntry notifEntry = entry.getRepresentativeEntry(); + return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT + || hasHighPriorityCharacteristics(notifEntry) + || hasHighPriorityChild(entry); + } + + + private boolean hasHighPriorityChild(ListEntry entry) { + if (entry instanceof GroupEntry) { + for (NotificationEntry child : ((GroupEntry) entry).getChildren()) { + if (isHighPriority(child)) { + return true; + } + } + } + return false; + } + + private boolean hasHighPriorityCharacteristics(NotificationEntry entry) { + return !hasUserSetImportance(entry) + && (isImportantOngoing(entry) + || entry.getSbn().getNotification().hasMediaSession() + || isPeopleNotification(entry) + || isMessagingStyle(entry)); + } + + private boolean isImportantOngoing(NotificationEntry entry) { + return entry.getSbn().getNotification().isForegroundService() + && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW; + } + + private boolean isMessagingStyle(NotificationEntry entry) { + return Notification.MessagingStyle.class.equals( + entry.getSbn().getNotification().getNotificationStyle()); + } + + private boolean isPeopleNotification(NotificationEntry entry) { + return mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn()); + } + + private boolean hasUserSetImportance(NotificationEntry entry) { + return entry.getRanking().getChannel() != null + && entry.getRanking().getChannel().hasUserSetImportance(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java deleted file mode 100644 index 76e256b9be2d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java +++ /dev/null @@ -1,148 +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.statusbar.notification.collection.provider; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.Person; - -import com.android.systemui.Dependency; -import com.android.systemui.statusbar.notification.collection.GroupEntry; -import com.android.systemui.statusbar.notification.collection.ListEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.phone.NotificationGroupManager; - -import java.util.ArrayList; -import java.util.List; - -/** - * Whether the ListEntry is shown to the user as a high priority notification: visible on - * the lock screen/status bar and in the top section in the shade. - * - * A NotificationEntry is considered high priority if it: - * - has importance greater than or equal to IMPORTANCE_DEFAULT - * OR - * - their importance has NOT been set to a low priority option by the user AND the notification - * fulfills one of the following: - * - has a person associated with it - * - has a media session associated with it - * - has messaging style - * - * A GroupEntry is considered high priority if its representativeEntry (summary) or children are - * high priority - */ -public class IsHighPriorityProvider extends DerivedMember<ListEntry, Boolean> { - // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic - // replaced in GroupEntry and NotifListBuilderImpl - private final NotificationGroupManager mGroupManager; - - - public IsHighPriorityProvider() { - // TODO: (b/145659174) remove - mGroupManager = Dependency.get(NotificationGroupManager.class); - } - - @Override - protected Boolean computeValue(ListEntry entry) { - if (entry == null) { - return false; - } - - return isHighPriority(entry); - } - - private boolean isHighPriority(ListEntry listEntry) { - // requires groups have been set (AFTER PipelineState.STATE_TRANSFORMING) - final NotificationEntry notifEntry = listEntry.getRepresentativeEntry(); - return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT - || hasHighPriorityCharacteristics(notifEntry) - || hasHighPriorityChild(listEntry); - - } - - private boolean hasHighPriorityChild(ListEntry entry) { - // TODO: (b/145659174) remove - if (entry instanceof NotificationEntry) { - NotificationEntry notifEntry = (NotificationEntry) entry; - if (mGroupManager.isSummaryOfGroup(notifEntry.getSbn())) { - List<NotificationEntry> logicalChildren = - mGroupManager.getLogicalChildren(notifEntry.getSbn()); - for (NotificationEntry child : logicalChildren) { - if (child.isHighPriority()) { - return true; - } - } - } - } - - if (entry instanceof GroupEntry) { - for (NotificationEntry child : ((GroupEntry) entry).getChildren()) { - if (child.isHighPriority()) { - return true; - } - } - } - return false; - } - - private boolean hasHighPriorityCharacteristics(NotificationEntry entry) { - return !hasUserSetImportance(entry) - && (isImportantOngoing(entry) - || entry.getSbn().getNotification().hasMediaSession() - || hasPerson(entry) - || isMessagingStyle(entry)); - } - - private boolean isImportantOngoing(NotificationEntry entry) { - return entry.getSbn().getNotification().isForegroundService() - && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW; - } - - private boolean isMessagingStyle(NotificationEntry entry) { - return Notification.MessagingStyle.class.equals( - entry.getSbn().getNotification().getNotificationStyle()); - } - - private boolean hasPerson(NotificationEntry entry) { - // TODO: cache favorite and recent contacts to check contact affinity - Notification notification = entry.getSbn().getNotification(); - ArrayList<Person> people = notification.extras != null - ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST) - : new ArrayList<>(); - return people != null && !people.isEmpty(); - } - - private boolean hasUserSetImportance(NotificationEntry entry) { - return entry.getRanking().getChannel() != null - && entry.getRanking().getChannel().hasUserSetImportance(); - } - - @Override - public void onSbnUpdated() { - invalidate(); - } - - @Override - public void onRankingUpdated() { - invalidate(); - } - - @Override - public void onGroupingUpdated() { - invalidate(); - } -} 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 3e1b5bd0571e..89e5f5596291 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 @@ -294,6 +294,9 @@ public class NotificationLogger implements StateListener { } } + /** + * Logs Notification inflation error + */ private void logNotificationError( StatusBarNotification notification, Exception exception) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt index 2c0c9420a8c4..e81d361f58f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt @@ -37,7 +37,8 @@ data class PersonModel( val key: PersonKey, val name: CharSequence, val avatar: Drawable, - val clickIntent: PendingIntent + val clickIntent: PendingIntent, + val userId: Int ) /** Unique identifier for a Person in PeopleHub. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt index 784673e64ff8..88b41471a063 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt @@ -17,28 +17,27 @@ package com.android.systemui.statusbar.notification.people import android.app.Notification -import android.content.Context -import android.graphics.Canvas -import android.graphics.ColorFilter -import android.graphics.PixelFormat -import android.graphics.drawable.BitmapDrawable +import android.content.pm.UserInfo import android.graphics.drawable.Drawable -import android.os.UserHandle +import android.os.UserManager import android.service.notification.StatusBarNotification -import android.util.TypedValue +import android.util.SparseArray import android.view.View import android.view.ViewGroup import android.widget.ImageView import com.android.internal.statusbar.NotificationVisibility import com.android.internal.widget.MessagingGroup -import com.android.launcher3.icons.BaseIconFactory import com.android.systemui.R +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.NotificationPersonExtractorPlugin +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.notification.NotificationEntryListener import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.policy.ExtensionController import java.util.ArrayDeque +import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton @@ -52,8 +51,7 @@ interface NotificationPersonExtractor { @Singleton class NotificationPersonExtractorPluginBoundary @Inject constructor( - extensionController: ExtensionController, - private val context: Context + extensionController: ExtensionController ) : NotificationPersonExtractor { private var plugin: NotificationPersonExtractorPlugin? = null @@ -70,9 +68,8 @@ class NotificationPersonExtractorPluginBoundary @Inject constructor( } override fun extractPerson(sbn: StatusBarNotification) = - plugin?.extractPerson(sbn)?.let { data -> - val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user) - PersonModel(data.key, data.name, badged, data.clickIntent) + plugin?.extractPerson(sbn)?.run { + PersonModel(key, name, avatar, clickIntent, sbn.user.identifier) } override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn) @@ -84,11 +81,16 @@ class NotificationPersonExtractorPluginBoundary @Inject constructor( @Singleton class PeopleHubDataSourceImpl @Inject constructor( private val notificationEntryManager: NotificationEntryManager, - private val peopleHubManager: PeopleHubManager, - private val extractor: NotificationPersonExtractor + private val extractor: NotificationPersonExtractor, + private val userManager: UserManager, + @Background private val bgExecutor: Executor, + @Main private val mainExecutor: Executor, + private val notifLockscreenUserMgr: NotificationLockscreenUserManager ) : DataSource<PeopleHubModel> { + private var userChangeSubscription: Subscription? = null private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>() + private val peopleHubManagerForUser = SparseArray<PeopleHubManager>() private val notificationEntryListener = object : NotificationEntryListener { override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) = @@ -106,31 +108,56 @@ class PeopleHubDataSourceImpl @Inject constructor( } private fun removeVisibleEntry(entry: NotificationEntry) { - val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey() - if (key?.let(peopleHubManager::removeActivePerson) == true) { - updateUi() + (extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey())?.let { key -> + val userId = entry.sbn.user.identifier + bgExecutor.execute { + val parentId = userManager.getProfileParent(userId)?.id ?: userId + mainExecutor.execute { + if (peopleHubManagerForUser[parentId]?.removeActivePerson(key) == true) { + updateUi() + } + } + } } } private fun addVisibleEntry(entry: NotificationEntry) { - val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson() - if (personModel?.let(peopleHubManager::addActivePerson) == true) { - updateUi() + (extractor.extractPerson(entry.sbn) ?: entry.extractPerson())?.let { personModel -> + val userId = entry.sbn.user.identifier + bgExecutor.execute { + val parentId = userManager.getProfileParent(userId)?.id ?: userId + mainExecutor.execute { + val manager = peopleHubManagerForUser[parentId] + ?: PeopleHubManager().also { peopleHubManagerForUser.put(parentId, it) } + if (manager.addActivePerson(personModel)) { + updateUi() + } + } + } } } override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription { - val registerWithNotificationEntryManager = dataListeners.isEmpty() + val register = dataListeners.isEmpty() dataListeners.add(listener) - if (registerWithNotificationEntryManager) { + if (register) { + userChangeSubscription = notifLockscreenUserMgr.registerListener( + object : NotificationLockscreenUserManager.UserChangedListener { + override fun onUserChanged(userId: Int) = updateUi() + override fun onCurrentProfilesChanged( + currentProfiles: SparseArray<UserInfo>? + ) = updateUi() + }) notificationEntryManager.addNotificationEntryListener(notificationEntryListener) } else { - listener.onDataChanged(peopleHubManager.getPeopleHubModel()) + getPeopleHubModelForCurrentUser()?.let(listener::onDataChanged) } return object : Subscription { override fun unsubscribe() { dataListeners.remove(listener) if (dataListeners.isEmpty()) { + userChangeSubscription?.unsubscribe() + userChangeSubscription = null notificationEntryManager .removeNotificationEntryListener(notificationEntryListener) } @@ -138,16 +165,36 @@ class PeopleHubDataSourceImpl @Inject constructor( } } + private fun getPeopleHubModelForCurrentUser(): PeopleHubModel? { + val currentUserId = notifLockscreenUserMgr.currentUserId + val model = peopleHubManagerForUser[currentUserId]?.getPeopleHubModel() + ?: return null + val currentProfiles = notifLockscreenUserMgr.currentProfiles + return model.copy(people = model.people.filter { person -> + currentProfiles[person.userId]?.isQuietModeEnabled == false + }) + } + private fun updateUi() { - val model = peopleHubManager.getPeopleHubModel() + val model = getPeopleHubModelForCurrentUser() ?: return for (listener in dataListeners) { listener.onDataChanged(model) } } } -@Singleton -class PeopleHubManager @Inject constructor() { +private fun NotificationLockscreenUserManager.registerListener( + listener: NotificationLockscreenUserManager.UserChangedListener +): Subscription { + addUserChangedListener(listener) + return object : Subscription { + override fun unsubscribe() { + removeUserChangedListener(listener) + } + } +} + +class PeopleHubManager { private val activePeople = mutableMapOf<PersonKey, PersonModel>() private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE) @@ -157,7 +204,7 @@ class PeopleHubManager @Inject constructor() { if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) { inactivePeople.removeLast() } - inactivePeople.push(data) + inactivePeople.add(data) return true } return false @@ -190,63 +237,7 @@ private fun NotificationEntry.extractPerson(): PersonModel? { ?: extras.getString(Notification.EXTRA_TITLE) ?: return null val drawable = extractAvatarFromRow(this) ?: return null - val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user) - return PersonModel(key, name, badgedAvatar, clickIntent) -} - -private fun addBadgeToDrawable( - drawable: Drawable, - context: Context, - packageName: String, - user: UserHandle -): Drawable { - val pm = context.packageManager - val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user) - return object : Drawable() { - override fun draw(canvas: Canvas) { - val iconBounds = getBounds() - val factory = object : BaseIconFactory( - context, - 0 /* unused */, - iconBounds.width(), - true) {} - val badge = factory.createBadgedIconBitmap( - appInfo.loadIcon(pm), - user, - true, - appInfo.isInstantApp, - null) - val badgeDrawable = BitmapDrawable(context.resources, badge.icon) - .apply { - alpha = drawable.alpha - colorFilter = drawable.colorFilter - val badgeWidth = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - 15f, - context.resources.displayMetrics - ).toInt() - setBounds( - iconBounds.left + (iconBounds.width() - badgeWidth), - iconBounds.top + (iconBounds.height() - badgeWidth), - iconBounds.right, - iconBounds.bottom) - } - drawable.bounds = iconBounds - drawable.draw(canvas) - badgeDrawable.draw(canvas) - } - - override fun setAlpha(alpha: Int) { - drawable.alpha = alpha - } - - override fun setColorFilter(colorFilter: ColorFilter?) { - drawable.colorFilter = colorFilter - } - - @PixelFormat.Opacity - override fun getOpacity(): Int = PixelFormat.OPAQUE - } + return PersonModel(key, name, drawable, clickIntent, sbn.user.identifier) } fun extractAvatarFromRow(entry: NotificationEntry): Drawable? = @@ -272,4 +263,4 @@ private fun NotificationEntry.extractPersonKey(): PersonKey? = if (isMessagingNotification()) key else null private fun NotificationEntry.isMessagingNotification() = - sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
\ No newline at end of file + sbn.notification.notificationStyle == Notification.MessagingStyle::class.java diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 172b72e9b0fc..30f22ac5e161 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -397,7 +397,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder existingWrapper.onReinflated(); } } catch (Exception e) { - handleInflationError(runningInflations, e, row.getEntry().getSbn(), callback); + handleInflationError(runningInflations, e, row.getEntry(), callback); // Add a running inflation to make sure we don't trigger callbacks. // Safe to do because only happens in tests. runningInflations.put(inflationId, new CancellationSignal()); @@ -448,7 +448,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder onViewApplied(newView); } catch (Exception anotherException) { runningInflations.remove(inflationId); - handleInflationError(runningInflations, e, row.getEntry().getSbn(), + handleInflationError(runningInflations, e, row.getEntry(), callback); } } @@ -474,7 +474,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private static void handleInflationError( HashMap<Integer, CancellationSignal> runningInflations, Exception e, - StatusBarNotification notification, @Nullable InflationCallback callback) { + NotificationEntry notification, @Nullable InflationCallback callback) { Assert.isMainThread(); runningInflations.values().forEach(CancellationSignal::cancel); if (callback != null) { @@ -707,7 +707,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder + Integer.toHexString(sbn.getId()); Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e); if (mCallback != null) { - mCallback.handleInflationException(sbn, + mCallback.handleInflationException(mRow.getEntry(), new InflationException("Couldn't inflate contentViews" + e)); } } @@ -729,7 +729,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder } @Override - public void handleInflationException(StatusBarNotification notification, Exception e) { + public void handleInflationException(NotificationEntry entry, Exception e) { handleError(e); } 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 6f2abba128d6..779a224ecb62 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 @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; @@ -81,6 +82,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final Context mContext; private final VisualStabilityManager mVisualStabilityManager; private final AccessibilityManager mAccessibilityManager; + private final HighPriorityProvider mHighPriorityProvider; // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager = @@ -109,12 +111,14 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx @Inject public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager, Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler, - AccessibilityManager accessibilityManager) { + AccessibilityManager accessibilityManager, + HighPriorityProvider highPriorityProvider) { mContext = context; mVisualStabilityManager = visualStabilityManager; mStatusBarLazy = statusBarLazy; mMainHandler = mainHandler; mAccessibilityManager = accessibilityManager; + mHighPriorityProvider = highPriorityProvider; } public void setUpWithPresenter(NotificationPresenter presenter, @@ -331,8 +335,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx row.getIsNonblockable(), isForBlockingHelper, row.getEntry().getImportance(), - row.getEntry().isHighPriority()); - + mHighPriorityProvider.isHighPriority(row.getEntry())); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index b4ccb567504a..edfd1b4d3c85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -46,6 +46,7 @@ import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.AlphaOptimizedImageView; import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent; +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import java.util.ArrayList; @@ -269,7 +270,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } mAppOpsItem = createAppOpsItem(mContext); if (mIsUsingBidirectionalSwipe) { - mInfoItem = createInfoItem(mContext, !mParent.getEntry().isHighPriority()); + mInfoItem = createInfoItem(mContext, + mParent.getEntry().getBucket() == NotificationSectionsManager.BUCKET_SILENT); } else { mInfoItem = createInfoItem(mContext); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java index 2fe54c00bb06..9b95bff4921c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.row; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.service.notification.StatusBarNotification; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -138,10 +137,10 @@ public interface NotificationRowContentBinder { /** * Callback for when there is an inflation exception * - * @param notification notification which failed to inflate content + * @param entry notification which failed to inflate content * @param e exception */ - void handleInflationException(StatusBarNotification notification, Exception e); + void handleInflationException(NotificationEntry entry, Exception e); /** * Callback for after the content views finish inflating. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 352ba0f75a32..76fdfc6fbabc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -28,6 +28,7 @@ import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.metrics.LogMaker; import android.os.Handler; +import android.service.notification.StatusBarNotification; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -176,27 +177,30 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras .getParcelable(Notification.EXTRA_MEDIA_SESSION); - if (Utils.useQsMediaPlayer(mContext)) { + if (Utils.useQsMediaPlayer(mContext) && token != null) { final int[] compactActions = mRow.getEntry().getSbn().getNotification().extras .getIntArray(Notification.EXTRA_COMPACT_ACTIONS); int tintColor = getNotificationHeader().getOriginalIconColor(); StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class); QuickQSPanel panel = ctrl.getStatusBarView().findViewById( com.android.systemui.R.id.quick_qs_panel); + StatusBarNotification sbn = mRow.getEntry().getSbn(); + Notification notif = sbn.getNotification(); panel.getMediaPlayer().setMediaSession(token, - mRow.getEntry().getSbn().getNotification().getSmallIcon(), + notif.getSmallIcon(), tintColor, mBackgroundColor, mActions, - compactActions); + compactActions, + notif.contentIntent); QSPanel bigPanel = ctrl.getStatusBarView().findViewById( com.android.systemui.R.id.quick_settings_panel); bigPanel.addMediaSession(token, - mRow.getEntry().getSbn().getNotification().getSmallIcon(), + notif.getSmallIcon(), tintColor, mBackgroundColor, mActions, - mRow.getEntry().getSbn()); + sbn); } boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java index 2761689ec409..09c1fad423d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -187,18 +187,18 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section @Override public boolean beginsSection(@NonNull View view, @Nullable View previous) { boolean begin = false; - if (view instanceof ExpandableNotificationRow) { - if (previous instanceof ExpandableNotificationRow) { + if (view instanceof ActivatableNotificationView) { + if (previous instanceof ActivatableNotificationView) { // If we're drawing the first non-person notification, break out a section - ExpandableNotificationRow curr = (ExpandableNotificationRow) view; - ExpandableNotificationRow prev = (ExpandableNotificationRow) previous; + ActivatableNotificationView curr = (ActivatableNotificationView) view; + ActivatableNotificationView prev = (ActivatableNotificationView) previous; - begin = curr.getEntry().getBucket() != prev.getEntry().getBucket(); + begin = getBucket(curr) != getBucket(prev); } } if (!begin) { - begin = view == mGentleHeader || previous == mPeopleHubView; + begin = view == mGentleHeader || view == mPeopleHubView; } return begin; @@ -230,29 +230,42 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section return; } - int lastPersonIndex = -1; - int firstGentleNotifIndex = -1; + boolean peopleNotificationsPresent = false; + int firstNonHeadsUpIndex = -1; + int firstGentleIndex = -1; + int notifCount = 0; final int n = mParent.getChildCount(); for (int i = 0; i < n; i++) { View child = mParent.getChildAt(i); - if (child instanceof ExpandableNotificationRow - && child.getVisibility() != View.GONE) { + if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) { + notifCount++; ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (firstNonHeadsUpIndex == -1 && !row.isHeadsUp()) { + firstNonHeadsUpIndex = i; + } if (row.getEntry().getBucket() == BUCKET_PEOPLE) { - lastPersonIndex = i; + peopleNotificationsPresent = true; } if (row.getEntry().getBucket() == BUCKET_SILENT) { - firstGentleNotifIndex = i; + firstGentleIndex = i; break; } } } + if (firstNonHeadsUpIndex == -1) { + firstNonHeadsUpIndex = firstGentleIndex != -1 ? firstGentleIndex : notifCount; + } + // make room for peopleHub - firstGentleNotifIndex += adjustPeopleHubVisibilityAndPosition(lastPersonIndex); + int offset = adjustPeopleHubVisibilityAndPosition( + firstNonHeadsUpIndex, peopleNotificationsPresent); + if (firstGentleIndex != -1) { + firstGentleIndex += offset; + } - adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex); + adjustGentleHeaderVisibilityAndPosition(firstGentleIndex); mGentleHeader.setAreThereDismissableGentleNotifs( mParent.hasActiveClearableNotifications(ROWS_GENTLE)); @@ -294,13 +307,15 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section } } - private int adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) { - final boolean showPeopleHeader = mPeopleHubVisible - && mNumberOfSections > 2 - && mStatusBarStateController.getState() != StatusBarState.KEYGUARD; + private int adjustPeopleHubVisibilityAndPosition( + int targetIndex, boolean peopleNotificationsPresent) { + final boolean showPeopleHeader = mNumberOfSections > 2 + && mStatusBarStateController.getState() != StatusBarState.KEYGUARD + && (peopleNotificationsPresent || mPeopleHubVisible); final int currentHubIndex = mParent.indexOfChild(mPeopleHubView); final boolean currentlyVisible = currentHubIndex >= 0; - int targetIndex = lastPersonIndex + 1; + + mPeopleHubView.setCanSwipe(showPeopleHeader && !peopleNotificationsPresent); if (!showPeopleHeader) { if (currentlyVisible) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index 6d0fcc386281..4845ea16020b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -31,6 +31,7 @@ import com.android.systemui.SwipeHelper; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper { @@ -298,8 +299,8 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc @Override public Animator getViewTranslationAnimator(View v, float target, ValueAnimator.AnimatorUpdateListener listener) { - if (v instanceof SwipeableView) { - return ((SwipeableView) v).getTranslateViewAnimator(target, listener); + if (v instanceof ExpandableNotificationRow) { + return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener); } else { return superGetViewTranslationAnimator(v, target, listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt index a0796060e9d8..e5717aeefdcb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt @@ -16,38 +16,22 @@ package com.android.systemui.statusbar.notification.stack -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.ObjectAnimator -import android.animation.ValueAnimator import android.content.Context import android.util.AttributeSet -import android.util.FloatProperty import android.view.View import android.view.ViewGroup import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView import com.android.systemui.R import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin import com.android.systemui.statusbar.notification.people.DataListener import com.android.systemui.statusbar.notification.people.PersonViewModel import com.android.systemui.statusbar.notification.row.ActivatableNotificationView -private val TRANSLATE_CONTENT = object : FloatProperty<PeopleHubView>("translate") { - override fun setValue(view: PeopleHubView, value: Float) { - view.translation = value - } - - override fun get(view: PeopleHubView) = view.translation -} - class PeopleHubView(context: Context, attrs: AttributeSet) : ActivatableNotificationView(context, attrs), SwipeableView { private lateinit var contents: ViewGroup private lateinit var personControllers: List<PersonDataListenerImpl> - private var translateAnim: ObjectAnimator? = null val personViewAdapters: Sequence<DataListener<PersonViewModel?>> get() = personControllers.asSequence() @@ -56,9 +40,10 @@ class PeopleHubView(context: Context, attrs: AttributeSet) : super.onFinishInflate() contents = requireViewById(R.id.people_list) personControllers = (0 until contents.childCount) + .reversed() .asSequence() .mapNotNull { idx -> - (contents.getChildAt(idx) as? LinearLayout)?.let(::PersonDataListenerImpl) + (contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl) } .toList() } @@ -69,41 +54,32 @@ class PeopleHubView(context: Context, attrs: AttributeSet) : override fun createMenu(): NotificationMenuRowPlugin? = null - override fun getTranslateViewAnimator( - leftTarget: Float, - listener: ValueAnimator.AnimatorUpdateListener? - ): Animator = - ObjectAnimator - .ofFloat(this, TRANSLATE_CONTENT, leftTarget) - .apply { - listener?.let { addUpdateListener(listener) } - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(anim: Animator) { - translateAnim = null - } - }) - } - .also { - translateAnim?.cancel() - translateAnim = it - } - override fun resetTranslation() { - translateAnim?.cancel() translationX = 0f } - private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) : - DataListener<PersonViewModel?> { + override fun setTranslation(translation: Float) { + if (canSwipe) { + super.setTranslation(translation) + } + } - val nameView = viewGroup.requireViewById<TextView>(R.id.person_name) - val avatarView = viewGroup.requireViewById<ImageView>(R.id.person_icon) + var canSwipe: Boolean = true + set(value) { + if (field != value) { + if (field) { + resetTranslation() + } + field = value + } + } + + private inner class PersonDataListenerImpl(val avatarView: ImageView) : + DataListener<PersonViewModel?> { override fun onDataChanged(data: PersonViewModel?) { - viewGroup.visibility = data?.let { View.VISIBLE } ?: View.INVISIBLE - nameView.text = data?.name avatarView.setImageDrawable(data?.icon) - viewGroup.setOnClickListener { data?.onClick?.invoke() } + avatarView.setOnClickListener { data?.onClick?.invoke() } } } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java index 6c6ef61cfdaf..49e59a2e7200 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SwipeableView.java @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.notification.stack; -import android.animation.Animator; -import android.animation.ValueAnimator; - import androidx.annotation.Nullable; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -32,10 +29,6 @@ public interface SwipeableView { /** Optionally creates a menu for this view. */ @Nullable NotificationMenuRowPlugin createMenu(); - /** Animator for translating the view, simulating a swipe. */ - Animator getTranslateViewAnimator( - float leftTarget, ValueAnimator.AnimatorUpdateListener listener); - /** Sets the translation amount for an in-progress swipe. */ void setTranslation(float translate); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index f7d52b5f5ab0..ad1aa8370495 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -98,7 +98,12 @@ class KeyguardBypassController : Dumpable { bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0 } }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD) - lockscreenUserManager.addUserChangedListener { pendingUnlockType = null } + lockscreenUserManager.addUserChangedListener( + object : NotificationLockscreenUserManager.UserChangedListener { + override fun onUserChanged(userId: Int) { + pendingUnlockType = null + } + }) } /** 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 a3b1b5f8360b..a6842badc153 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -366,8 +366,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_USER_SWITCHED); - mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, Handler.getMain(), - UserHandle.ALL); + mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, + Handler.getMain(), UserHandle.ALL); notifyNavigationBarScreenOn(); mOverviewProxyService.addCallback(mOverviewProxyListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 9dc8fbf900b6..0f3af095f7be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -24,8 +24,6 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; -import com.android.internal.annotations.VisibleForTesting; - public class NotificationPanelView extends PanelView { private static final boolean DEBUG = false; @@ -38,14 +36,10 @@ public class NotificationPanelView extends PanelView { static final String COUNTER_PANEL_OPEN = "panel_open"; static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; - @VisibleForTesting - protected KeyguardAffordanceHelper mAffordanceHelper; - - private int mOldLayoutDirection; - private int mCurrentPanelAlpha; private final Paint mAlphaPaint = new Paint(); private boolean mDozing; + private RtlChangeListener mRtlChangeListener; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -57,9 +51,8 @@ public class NotificationPanelView extends PanelView { @Override public void onRtlPropertiesChanged(int layoutDirection) { - if (layoutDirection != mOldLayoutDirection) { - mAffordanceHelper.onRtlPropertiesChanged(); - mOldLayoutDirection = layoutDirection; + if (mRtlChangeListener != null) { + mRtlChangeListener.onRtlPropertielsChanged(layoutDirection); } } @@ -95,4 +88,11 @@ public class NotificationPanelView extends PanelView { return !mDozing; } + void setRtlChangeListener(RtlChangeListener listener) { + mRtlChangeListener = listener; + } + + interface RtlChangeListener { + void onRtlPropertielsChanged(int layoutDirection); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index bbde25bb482e..90ec2a076e87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -449,6 +449,7 @@ public class NotificationPanelViewController extends PanelViewController { private PluginManager mPluginManager; private FrameLayout mPluginFrame; private NPVPluginManager mNPVPluginManager; + private int mOldLayoutDirection; @Inject public NotificationPanelViewController(NotificationPanelView view, @@ -603,6 +604,13 @@ public class NotificationPanelViewController extends PanelViewController { } }, HomeControlsPlugin.class, false); + + mView.setRtlChangeListener(layoutDirection -> { + if (layoutDirection != mOldLayoutDirection) { + mAffordanceHelper.onRtlPropertiesChanged(); + mOldLayoutDirection = layoutDirection; + } + }); } @Override 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 5b34aa7781c1..00e38f814dff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -179,7 +179,7 @@ public class PhoneStatusBarPolicy filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); - broadcastDispatcher.registerReceiver(mIntentReceiver, filter, mHandler); + broadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); // listen for user / profile change. try { 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 277a7613aafb..ccc86b1f8c5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1272,7 +1272,11 @@ public class StatusBar extends SystemUI implements DemoMode, mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); - mEntryManager.setRowBinder(rowBinder); + if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { + mEntryManager.setRowBinder(rowBinder); + rowBinder.setInflationCallback(mEntryManager); + } + mRemoteInputUriController.attach(mEntryManager); rowBinder.setNotificationClicker(new NotificationClicker( @@ -1282,7 +1286,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationListController.bind(); if (mFeatureFlags.isNewNotifPipelineEnabled()) { - mNewNotifPipeline.get().initialize(mNotificationListener); + mNewNotifPipeline.get().initialize(mNotificationListener, rowBinder); } mEntryManager.attach(mNotificationListener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 0fd0dabbc3f2..12a65169e1df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -217,7 +217,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mEntryManager.addNotificationLifetimeExtenders( remoteInputManager.getLifetimeExtenders()); notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager, - mEntryManager, this); + this); mNotificationInterruptionStateProvider.setUpWithPresenter( this, mHeadsUpManager, this::canHeadsUp); mLockscreenUserManager.setUpWithPresenter(this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index ddacc3aedce0..4f0af9e166c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -188,7 +188,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C // NOTE: This receiver could run before this method returns, as it's not dispatching // on the main thread and BroadcastDispatcher may not need to register with Context. // The receiver will return immediately if the view does not have a Handler yet. - mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter, + mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL); Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS, StatusBarIconController.ICON_BLACKLIST); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java index 2e26711a3578..b4c154aa28cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java @@ -96,7 +96,7 @@ public class DateView extends TextView { filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(Intent.ACTION_LOCALE_CHANGED); - mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter, + mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, Dependency.get(Dependency.TIME_TICK_HANDLER)); updateClock(); 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 570f153a62c0..cb40d7752f53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -79,7 +79,8 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio IntentFilter filter = new IntentFilter(); filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); filter.addAction(LocationManager.MODE_CHANGED_ACTION); - mBroadcastDispatcher.registerReceiver(this, filter, new Handler(bgLooper), UserHandle.ALL); + mBroadcastDispatcher.registerReceiverWithHandler(this, filter, + new Handler(bgLooper), UserHandle.ALL); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mStatusBarManager 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 f640d039ad31..679fa7e2b016 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -326,7 +326,7 @@ public class NetworkControllerImpl extends BroadcastReceiver filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - mBroadcastDispatcher.registerReceiver(this, filter, mReceiverHandler); + mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler); mListening = true; updateMobileControllers(); 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 019ef3bca709..312c4ac75bfa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -126,7 +126,8 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi IntentFilter filter = new IntentFilter(); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); filter.addAction(Intent.ACTION_USER_UNLOCKED); - broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, bgHandler, UserHandle.ALL); + broadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, bgHandler, + UserHandle.ALL); // TODO: re-register network callback on user change. mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 7758aba52918..31b9952afe94 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -88,7 +88,7 @@ public class ThemeOverlayController extends SystemUI { final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); - mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() { + mBroadcastDispatcher.registerReceiverWithHandler(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a4ed31d95b2b..112ae6f3758a 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -1008,7 +1008,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mBroadcastDispatcher.registerReceiver(this, filter, mWorker); + mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mWorker); } public void destroy() { diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java new file mode 100644 index 000000000000..d413308d4573 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -0,0 +1,333 @@ +/* + * 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.wm; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.res.Configuration; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Slog; +import android.util.SparseArray; +import android.view.IDisplayWindowInsetsController; +import android.view.InsetsSource; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.WindowInsets; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; + +import com.android.systemui.dagger.qualifiers.Main; + +import java.util.ArrayList; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode. + */ +@Singleton +public class DisplayImeController implements DisplayWindowController.DisplayWindowListener { + private static final String TAG = "DisplayImeController"; + + static final int ANIMATION_DURATION_SHOW_MS = 275; + static final int ANIMATION_DURATION_HIDE_MS = 340; + static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + private static final int DIRECTION_NONE = 0; + private static final int DIRECTION_SHOW = 1; + private static final int DIRECTION_HIDE = 2; + + SystemWindows mSystemWindows; + final Handler mHandler; + + final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>(); + + final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>(); + + @Inject + DisplayImeController(SystemWindows syswin, DisplayWindowController displayController, + @Main Handler mainHandler) { + mHandler = mainHandler; + mSystemWindows = syswin; + displayController.addDisplayWindowListener(this); + } + + @Override + public void onDisplayAdded(int displayId) { + // Add's a system-ui window-manager specifically for ime. This type is special because + // WM will defer IME inset handling to it in multi-window scenarious. + PerDisplay pd = new PerDisplay(displayId, + mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()); + try { + mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to set insets controller on display " + displayId); + } + mImePerDisplay.put(displayId, pd); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + PerDisplay pd = mImePerDisplay.get(displayId); + if (pd == null) { + return; + } + if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation() + != pd.mRotation && isImeShowing(displayId)) { + pd.startAnimation(true); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + try { + mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to remove insets controller on display " + displayId); + } + mImePerDisplay.remove(displayId); + } + + private boolean isImeShowing(int displayId) { + PerDisplay pd = mImePerDisplay.get(displayId); + if (pd == null) { + return false; + } + final InsetsSource imeSource = pd.mInsetsState.getSource(InsetsState.ITYPE_IME); + return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible(); + } + + private void dispatchPositionChanged(int displayId, int imeTop, + SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImePositionChanged(displayId, imeTop, t); + } + } + } + + private void dispatchStartPositioning(int displayId, int imeTop, int finalImeTop, + boolean show, SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImeStartPositioning(displayId, imeTop, finalImeTop, show, t); + } + } + } + + private void dispatchEndPositioning(int displayId, int imeTop, boolean show, + SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImeEndPositioning(displayId, imeTop, show, t); + } + } + } + + /** + * Adds an {@link ImePositionProcessor} to be called during ime position updates. + */ + public void addPositionProcessor(ImePositionProcessor processor) { + synchronized (mPositionProcessors) { + if (mPositionProcessors.contains(processor)) { + return; + } + mPositionProcessors.add(processor); + } + } + + /** + * Removes an {@link ImePositionProcessor} to be called during ime position updates. + */ + public void removePositionProcessor(ImePositionProcessor processor) { + synchronized (mPositionProcessors) { + mPositionProcessors.remove(processor); + } + } + + class PerDisplay extends IDisplayWindowInsetsController.Stub { + final int mDisplayId; + final InsetsState mInsetsState = new InsetsState(); + InsetsSourceControl mImeSourceControl = null; + int mAnimationDirection = DIRECTION_NONE; + ValueAnimator mAnimation = null; + int mRotation = Surface.ROTATION_0; + + PerDisplay(int displayId, int initialRotation) { + mDisplayId = displayId; + mRotation = initialRotation; + } + + @Override + public void insetsChanged(InsetsState insetsState) { + if (mInsetsState.equals(insetsState)) { + return; + } + mInsetsState.set(insetsState, true /* copySources */); + } + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] activeControls) { + insetsChanged(insetsState); + if (activeControls != null) { + for (InsetsSourceControl activeControl : activeControls) { + if (activeControl == null) { + continue; + } + if (activeControl.getType() == InsetsState.ITYPE_IME) { + mImeSourceControl = activeControl; + } + } + } + } + + @Override + public void showInsets(int types, boolean fromIme) { + if ((types & WindowInsets.Type.ime()) == 0) { + return; + } + startAnimation(true /* show */); + } + + @Override + public void hideInsets(int types, boolean fromIme) { + if ((types & WindowInsets.Type.ime()) == 0) { + return; + } + startAnimation(false /* show */); + } + + /** + * Sends the local visibility state back to window manager. Needed for legacy adjustForIme. + */ + private void setVisibleDirectly(boolean visible) { + mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible); + try { + mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState); + } catch (RemoteException e) { + } + } + + private int imeTop(InsetsSource imeSource, float surfaceOffset) { + return imeSource.getFrame().top + (int) surfaceOffset; + } + + private void startAnimation(final boolean show) { + final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME); + if (imeSource == null || mImeSourceControl == null) { + return; + } + if ((mAnimationDirection == DIRECTION_SHOW && show) + || (mAnimationDirection == DIRECTION_HIDE && !show)) { + return; + } + if (mAnimationDirection != DIRECTION_NONE) { + mAnimation.end(); + mAnimationDirection = DIRECTION_NONE; + } + mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; + mHandler.post(() -> { + final float defaultY = mImeSourceControl.getSurfacePosition().y; + final float x = mImeSourceControl.getSurfacePosition().x; + final float startY = show ? defaultY + imeSource.getFrame().height() : defaultY; + final float endY = show ? defaultY : defaultY + imeSource.getFrame().height(); + mAnimation = ValueAnimator.ofFloat(startY, endY); + mAnimation.setDuration( + show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS); + + mAnimation.addUpdateListener(animation -> { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + float value = (float) animation.getAnimatedValue(); + t.setPosition(mImeSourceControl.getLeash(), x, value); + dispatchPositionChanged(mDisplayId, imeTop(imeSource, value), t); + t.apply(); + t.close(); + }); + mAnimation.setInterpolator(INTERPOLATOR); + mAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setPosition(mImeSourceControl.getLeash(), x, startY); + dispatchStartPositioning(mDisplayId, imeTop(imeSource, startY), + imeTop(imeSource, endY), mAnimationDirection == DIRECTION_SHOW, + t); + if (mAnimationDirection == DIRECTION_SHOW) { + t.show(mImeSourceControl.getLeash()); + } + t.apply(); + t.close(); + } + @Override + public void onAnimationEnd(Animator animation) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setPosition(mImeSourceControl.getLeash(), x, endY); + dispatchEndPositioning(mDisplayId, imeTop(imeSource, endY), + mAnimationDirection == DIRECTION_SHOW, t); + if (mAnimationDirection == DIRECTION_HIDE) { + t.hide(mImeSourceControl.getLeash()); + } + t.apply(); + t.close(); + + mAnimationDirection = DIRECTION_NONE; + mAnimation = null; + } + }); + if (!show) { + // When going away, queue up insets change first, otherwise any bounds changes + // can have a "flicker" of ime-provided insets. + setVisibleDirectly(false /* visible */); + } + mAnimation.start(); + if (show) { + // When showing away, queue up insets change last, otherwise any bounds changes + // can have a "flicker" of ime-provided insets. + setVisibleDirectly(true /* visible */); + } + }); + } + } + + /** + * Allows other things to synchronize with the ime position + */ + public interface ImePositionProcessor { + /** + * Called when the IME position is starting to animate. + */ + void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, boolean showing, + SurfaceControl.Transaction t); + + /** + * Called when the ime position changed. This is expected to be a synchronous call on the + * animation thread. Operations can be added to the transaction to be applied in sync. + */ + void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t); + + /** + * Called when the IME position is done animating. + */ + void onImeEndPositioning(int displayId, int imeTop, boolean showing, + SurfaceControl.Transaction t); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index b70fdbd4a2d8..eccf09633f16 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -19,7 +19,7 @@ package com.android.keyguard; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE; -import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE; +import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertFalse; @@ -83,14 +83,14 @@ public class CarrierTextControllerTest extends SysuiTestCase { private static final String TEST_CARRIER_2 = "TEST_CARRIER_2"; private static final int TEST_CARRIER_ID = 1; private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0, - TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null, TEST_CARRIER_ID, 0); private static final SubscriptionInfo TEST_SUBSCRIPTION_NULL = new SubscriptionInfo(0, "", 0, - TEST_CARRIER, null, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", DATA_ROAMING_DISABLE, + TEST_CARRIER, null, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, ""); private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0, - TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_ENABLE, null, null, null, null, false, null, ""); @Mock private WifiManager mWifiManager; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 40ea6ddbbe38..2e0fb3bc850d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -17,7 +17,7 @@ package com.android.keyguard; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; -import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE; +import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT; import static com.google.common.truth.Truth.assertThat; @@ -84,11 +84,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private static final int TEST_CARRIER_ID = 1; private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24"; private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0, - TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID, TEST_CARRIER_ID, 0); private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0, - TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", + TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID, TEST_CARRIER_ID, 0); @Mock @@ -150,10 +150,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testReceiversRegistered() { - verify(mBroadcastDispatcher, atLeastOnce()).registerReceiver( + verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler( eq(mKeyguardUpdateMonitor.mBroadcastReceiver), any(IntentFilter.class), any(Handler.class)); - verify(mBroadcastDispatcher, atLeastOnce()).registerReceiver( + verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler( eq(mKeyguardUpdateMonitor.mBroadcastAllReceiver), any(IntentFilter.class), any(Handler.class), eq(UserHandle.ALL)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt index 42fbf59fef44..22b18373e81d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt @@ -27,6 +27,8 @@ import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock import junit.framework.Assert.assertSame import org.junit.Before import org.junit.Test @@ -39,6 +41,7 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import java.util.concurrent.Executor @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper @@ -73,6 +76,8 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Mock private lateinit var mockHandler: Handler + private lateinit var executor: Executor + @Captor private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData> @@ -83,6 +88,7 @@ class BroadcastDispatcherTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) + executor = FakeExecutor(FakeSystemClock()) broadcastDispatcher = TestBroadcastDispatcher( mockContext, @@ -98,8 +104,9 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test fun testAddingReceiverToCorrectUBR() { - broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0) - broadcastDispatcher.registerReceiver( + broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, + mockHandler, user0) + broadcastDispatcher.registerReceiverWithHandler( broadcastReceiverOther, intentFilterOther, mockHandler, user1) testableLooper.processAllMessages() @@ -115,9 +122,29 @@ class BroadcastDispatcherTest : SysuiTestCase() { } @Test + fun testAddingReceiverToCorrectUBR_executor() { + broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, executor, user0) + broadcastDispatcher.registerReceiver( + broadcastReceiverOther, intentFilterOther, executor, user1) + + testableLooper.processAllMessages() + + verify(mockUBRUser0).registerReceiver(capture(argumentCaptor)) + + assertSame(broadcastReceiver, argumentCaptor.value.receiver) + assertSame(intentFilter, argumentCaptor.value.filter) + + verify(mockUBRUser1).registerReceiver(capture(argumentCaptor)) + assertSame(broadcastReceiverOther, argumentCaptor.value.receiver) + assertSame(intentFilterOther, argumentCaptor.value.filter) + } + + @Test fun testRemovingReceiversRemovesFromAllUBR() { - broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0) - broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1) + broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, + mockHandler, user0) + broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, + mockHandler, user1) broadcastDispatcher.unregisterReceiver(broadcastReceiver) @@ -129,8 +156,10 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test fun testRemoveReceiverFromUser() { - broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0) - broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1) + broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, + mockHandler, user0) + broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, + mockHandler, user1) broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user0) @@ -143,8 +172,8 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test fun testRegisterCurrentAsActualUser() { setUserMock(mockContext, user1) - broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, - UserHandle.CURRENT) + broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, + mockHandler, UserHandle.CURRENT) testableLooper.processAllMessages() diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt index 21ed15517752..7821ae29592e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt @@ -26,6 +26,8 @@ import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -69,8 +71,6 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { @Mock private lateinit var mockContext: Context @Mock - private lateinit var mockHandler: Handler - @Mock private lateinit var mPendingResult: BroadcastReceiver.PendingResult @Captor @@ -81,12 +81,14 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { private lateinit var intentFilter: IntentFilter private lateinit var intentFilterOther: IntentFilter private lateinit var handler: Handler + private lateinit var fakeExecutor: FakeExecutor @Before fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) handler = Handler(testableLooper.looper) + fakeExecutor = FakeExecutor(FakeSystemClock()) userBroadcastDispatcher = UserBroadcastDispatcher( mockContext, USER_ID, handler, testableLooper.looper) @@ -108,7 +110,7 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { intentFilter = IntentFilter(ACTION_1) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) testableLooper.processAllMessages() assertTrue(userBroadcastDispatcher.isRegistered()) @@ -128,7 +130,7 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { intentFilter = IntentFilter(ACTION_1) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) testableLooper.processAllMessages() reset(mockContext) @@ -151,9 +153,9 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { } userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiverOther, intentFilterOther, mockHandler, USER_HANDLE)) + ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE)) testableLooper.processAllMessages() assertTrue(userBroadcastDispatcher.isRegistered()) @@ -179,14 +181,15 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { intentFilterOther = IntentFilter(ACTION_2) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE)) + ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE)) val intent = Intent(ACTION_2) userBroadcastDispatcher.onReceive(mockContext, intent) testableLooper.processAllMessages() + fakeExecutor.runAllReady() verify(broadcastReceiver, never()).onReceive(any(), any()) verify(broadcastReceiverOther).onReceive(mockContext, intent) @@ -198,14 +201,15 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { intentFilterOther = IntentFilter(ACTION_2) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilterOther, handler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilterOther, fakeExecutor, USER_HANDLE)) val intent = Intent(ACTION_2) userBroadcastDispatcher.onReceive(mockContext, intent) testableLooper.processAllMessages() + fakeExecutor.runAllReady() verify(broadcastReceiver).onReceive(mockContext, intent) } @@ -218,14 +222,15 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { intentFilterOther.addCategory(CATEGORY_2) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE)) + ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE)) val intent = Intent(ACTION_1) userBroadcastDispatcher.onReceive(mockContext, intent) testableLooper.processAllMessages() + fakeExecutor.runAllReady() verify(broadcastReceiver).onReceive(mockContext, intent) verify(broadcastReceiverOther).onReceive(mockContext, intent) @@ -235,12 +240,13 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { fun testPendingResult() { intentFilter = IntentFilter(ACTION_1) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) val intent = Intent(ACTION_1) userBroadcastDispatcher.onReceive(mockContext, intent) testableLooper.processAllMessages() + fakeExecutor.runAllReady() verify(broadcastReceiver).onReceive(mockContext, intent) verify(broadcastReceiver).pendingResult = mPendingResult @@ -250,15 +256,16 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { fun testRemoveReceiverReferences() { intentFilter = IntentFilter(ACTION_1) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE)) + ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE)) intentFilterOther = IntentFilter(ACTION_1) intentFilterOther.addAction(ACTION_2) userBroadcastDispatcher.registerReceiver( - ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE)) + ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE)) userBroadcastDispatcher.unregisterReceiver(broadcastReceiver) testableLooper.processAllMessages() + fakeExecutor.runAllReady() assertFalse(userBroadcastDispatcher.isReceiverReferenceHeld(broadcastReceiver)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index 167f361b341a..548da8e1f2aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -110,7 +110,7 @@ public class PowerUITest extends SysuiTestCase { @Test public void testReceiverIsRegisteredToDispatcherOnStart() { mPowerUI.start(); - verify(mBroadcastDispatcher).registerReceiver( + verify(mBroadcastDispatcher).registerReceiverWithHandler( any(BroadcastReceiver.class), any(IntentFilter.class), any(Handler.class)); //PowerUI does not call with User diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index 40b0ba9bf633..77659df738c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -345,7 +345,7 @@ public class NotificationTestHelper { NotificationContentInflater.InflationCallback callback = new NotificationContentInflater.InflationCallback() { @Override - public void handleInflationException(StatusBarNotification notification, + public void handleInflationException(NotificationEntry entry, Exception e) { countDownLatch.countDown(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index f55ea4ff8ef2..cd33cf922482 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -65,6 +65,7 @@ import com.android.systemui.ForegroundServiceController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -82,6 +83,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; @@ -143,6 +145,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private RowInflaterTask mAsyncInflationTask; @Mock private NotificationRowBinder mMockedRowBinder; @Mock private NotifLog mNotifLog; + @Mock private FeatureFlags mFeatureFlags; private int mId; private NotificationEntry mEntry; @@ -219,6 +222,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntry.expandedIcon = mock(StatusBarIconView.class); + when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false); + when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mEntryManager = new TestableNotificationEntryManager( mNotifLog, mGroupManager, @@ -229,8 +234,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mock(NotificationFilter.class), mNotifLog, mock(NotificationSectionsFeatureManager.class), - mock(PeopleNotificationIdentifier.class)), - mEnvironment + mock(PeopleNotificationIdentifier.class), + mock(HighPriorityProvider.class)), + mEnvironment, + mFeatureFlags ); mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager); mEntryManager.addNotificationEntryListener(mEntryListener); @@ -242,7 +249,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mock(StatusBarStateController.class), mock(NotificationLogger.class)); notificationRowBinder.setUpWithPresenter( - mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback); + mPresenter, mListContainer, mHeadsUpManager, mBindCallback); + notificationRowBinder.setInflationCallback(mEntryManager); notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class)); mEntryManager.setRowBinder(notificationRowBinder); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt index 34beefe9843b..1afee120e495 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification +import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationRankingManager @@ -33,8 +34,9 @@ class TestableNotificationEntryManager( log: NotifLog, gm: NotificationGroupManager, rm: NotificationRankingManager, - ke: KeyguardEnvironment -) : NotificationEntryManager(log, gm, rm, ke) { + ke: KeyguardEnvironment, + ff: FeatureFlags +) : NotificationEntryManager(log, gm, rm, ke, ff) { public var countDownLatch: CountDownLatch = CountDownLatch(1) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java deleted file mode 100644 index a06d6c1e593b..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java +++ /dev/null @@ -1,148 +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.statusbar.notification.collection; - -import static android.app.NotificationManager.IMPORTANCE_HIGH; -import static android.app.NotificationManager.IMPORTANCE_MIN; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.testing.AndroidTestingRunner; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.RankingBuilder; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class GroupEntryTest extends SysuiTestCase { - @Test - public void testIsHighPriority_addChild() { - // GIVEN a GroupEntry with a lowPrioritySummary and no children - final GroupEntry parentEntry = new GroupEntry("test_group_key"); - final NotificationEntry lowPrioritySummary = createNotifEntry(false); - setSummary(parentEntry, lowPrioritySummary); - assertFalse(parentEntry.isHighPriority()); - - // WHEN we add a high priority child and invalidate derived members - addChild(parentEntry, createNotifEntry(true)); - parentEntry.invalidateDerivedMembers(); - - // THEN the GroupEntry's priority is updated to high even though the summary is still low - // priority - assertTrue(parentEntry.isHighPriority()); - assertFalse(lowPrioritySummary.isHighPriority()); - } - - @Test - public void testIsHighPriority_clearChildren() { - // GIVEN a GroupEntry with a lowPrioritySummary and high priority children - final GroupEntry parentEntry = new GroupEntry("test_group_key"); - setSummary(parentEntry, createNotifEntry(false)); - addChild(parentEntry, createNotifEntry(true)); - addChild(parentEntry, createNotifEntry(true)); - addChild(parentEntry, createNotifEntry(true)); - assertTrue(parentEntry.isHighPriority()); - - // WHEN we clear the children and invalidate derived members - parentEntry.clearChildren(); - parentEntry.invalidateDerivedMembers(); - - // THEN the parentEntry isn't high priority anymore - assertFalse(parentEntry.isHighPriority()); - } - - @Test - public void testIsHighPriority_summaryUpdated() { - // GIVEN a GroupEntry with a lowPrioritySummary and no children - final GroupEntry parentEntry = new GroupEntry("test_group_key"); - final NotificationEntry lowPrioritySummary = createNotifEntry(false); - setSummary(parentEntry, lowPrioritySummary); - assertFalse(parentEntry.isHighPriority()); - - // WHEN the summary changes to high priority and invalidates its derived members - lowPrioritySummary.setRanking( - new RankingBuilder() - .setKey(lowPrioritySummary.getKey()) - .setImportance(IMPORTANCE_HIGH) - .build()); - lowPrioritySummary.invalidateDerivedMembers(); - assertTrue(lowPrioritySummary.isHighPriority()); - - // THEN the GroupEntry's priority is updated to high - assertTrue(parentEntry.isHighPriority()); - } - - @Test - public void testIsHighPriority_checkChildrenToCalculatePriority() { - // GIVEN: - // GroupEntry = parentEntry, summary = lowPrioritySummary - // NotificationEntry = lowPriorityChild - // NotificationEntry = highPriorityChild - final GroupEntry parentEntry = new GroupEntry("test_group_key"); - setSummary(parentEntry, createNotifEntry(false)); - addChild(parentEntry, createNotifEntry(false)); - addChild(parentEntry, createNotifEntry(true)); - - // THEN the GroupEntry parentEntry is high priority since it has a high priority child - assertTrue(parentEntry.isHighPriority()); - } - - @Test - public void testIsHighPriority_childEntryRankingUpdated() { - // GIVEN: - // GroupEntry = parentEntry, summary = lowPrioritySummary - // NotificationEntry = lowPriorityChild - final GroupEntry parentEntry = new GroupEntry("test_group_key"); - final NotificationEntry lowPriorityChild = createNotifEntry(false); - setSummary(parentEntry, createNotifEntry(false)); - addChild(parentEntry, lowPriorityChild); - - // WHEN the child entry ranking changes to high priority and invalidates its derived members - lowPriorityChild.setRanking( - new RankingBuilder() - .setKey(lowPriorityChild.getKey()) - .setImportance(IMPORTANCE_HIGH) - .build()); - lowPriorityChild.invalidateDerivedMembers(); - - // THEN the parent entry's high priority value is updated - but not the parent's summary - assertTrue(parentEntry.isHighPriority()); - assertFalse(parentEntry.getSummary().isHighPriority()); - } - - private NotificationEntry createNotifEntry(boolean highPriority) { - return new NotificationEntryBuilder() - .setImportance(highPriority ? IMPORTANCE_HIGH : IMPORTANCE_MIN) - .build(); - } - - private void setSummary(GroupEntry parent, NotificationEntry summary) { - parent.setSummary(summary); - summary.setParent(parent); - } - - private void addChild(GroupEntry parent, NotificationEntry child) { - parent.addChild(child); - child.setParent(parent); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java new file mode 100644 index 000000000000..93909dc4d330 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.app.NotificationManager.IMPORTANCE_MIN; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.RankingBuilder; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class HighPriorityProviderTest extends SysuiTestCase { + @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private HighPriorityProvider mHighPriorityProvider; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mHighPriorityProvider = new HighPriorityProvider(mPeopleNotificationIdentifier); + } + + @Test + public void highImportance() { + // GIVEN notification has high importance + final NotificationEntry entry = new NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .build(); + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false); + + // THEN it has high priority + assertTrue(mHighPriorityProvider.isHighPriority(entry)); + } + + @Test + public void peopleNotification() { + // GIVEN notification is low importance and is a people notification + final Notification notification = new Notification.Builder(mContext, "test") + .build(); + final NotificationEntry entry = new NotificationEntryBuilder() + .setNotification(notification) + .setImportance(IMPORTANCE_LOW) + .build(); + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true); + + // THEN it has high priority + assertTrue(mHighPriorityProvider.isHighPriority(entry)); + } + + @Test + public void messagingStyle() { + // GIVEN notification is low importance but has messaging style + final Notification notification = new Notification.Builder(mContext, "test") + .setStyle(new Notification.MessagingStyle("")) + .build(); + final NotificationEntry entry = new NotificationEntryBuilder() + .setNotification(notification) + .build(); + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false); + + // THEN it has high priority + assertTrue(mHighPriorityProvider.isHighPriority(entry)); + } + + @Test + public void lowImportanceForeground() { + // GIVEN notification is low importance and is associated with a foreground service + final Notification notification = mock(Notification.class); + when(notification.isForegroundService()).thenReturn(true); + + final NotificationEntry entry = new NotificationEntryBuilder() + .setNotification(notification) + .setImportance(IMPORTANCE_LOW) + .build(); + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false); + + // THEN it has high priority + assertTrue(mHighPriorityProvider.isHighPriority(entry)); + } + + @Test + public void minImportanceForeground() { + // GIVEN notification is low importance and is associated with a foreground service + final Notification notification = mock(Notification.class); + when(notification.isForegroundService()).thenReturn(true); + + final NotificationEntry entry = new NotificationEntryBuilder() + .setNotification(notification) + .setImportance(IMPORTANCE_MIN) + .build(); + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false); + + // THEN it does NOT have high priority + assertFalse(mHighPriorityProvider.isHighPriority(entry)); + } + + @Test + public void userChangeTrumpsHighPriorityCharacteristics() { + // GIVEN notification has high priority characteristics but the user changed the importance + // to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN) + final Notification notification = new Notification.Builder(mContext, "test") + .setStyle(new Notification.MessagingStyle("")) + .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) + .build(); + final NotificationChannel channel = new NotificationChannel("a", "a", + IMPORTANCE_LOW); + channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); + + final NotificationEntry entry = new NotificationEntryBuilder() + .setNotification(notification) + .setChannel(channel) + .build(); + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true); + + // THEN it does NOT have high priority + assertFalse(mHighPriorityProvider.isHighPriority(entry)); + } + + @Test + public void testIsHighPriority_summaryUpdated() { + // GIVEN a GroupEntry with a lowPrioritySummary and no children + final GroupEntry parentEntry = new GroupEntry("test_group_key"); + final NotificationEntry lowPrioritySummary = createNotifEntry(false); + setSummary(parentEntry, lowPrioritySummary); + assertFalse(mHighPriorityProvider.isHighPriority(parentEntry)); + + // WHEN the summary changes to high priority + lowPrioritySummary.setRanking( + new RankingBuilder() + .setKey(lowPrioritySummary.getKey()) + .setImportance(IMPORTANCE_HIGH) + .build()); + assertTrue(mHighPriorityProvider.isHighPriority(lowPrioritySummary)); + + // THEN the GroupEntry's priority is updated to high + assertTrue(mHighPriorityProvider.isHighPriority(parentEntry)); + } + + @Test + public void testIsHighPriority_checkChildrenToCalculatePriority() { + // GIVEN: + // GroupEntry = parentEntry, summary = lowPrioritySummary + // NotificationEntry = lowPriorityChild + // NotificationEntry = highPriorityChild + final GroupEntry parentEntry = new GroupEntry("test_group_key"); + setSummary(parentEntry, createNotifEntry(false)); + addChild(parentEntry, createNotifEntry(false)); + addChild(parentEntry, createNotifEntry(true)); + + // THEN the GroupEntry parentEntry is high priority since it has a high priority child + assertTrue(mHighPriorityProvider.isHighPriority(parentEntry)); + } + + @Test + public void testIsHighPriority_childEntryRankingUpdated() { + // GIVEN: + // GroupEntry = parentEntry, summary = lowPrioritySummary + // NotificationEntry = lowPriorityChild + final GroupEntry parentEntry = new GroupEntry("test_group_key"); + final NotificationEntry lowPriorityChild = createNotifEntry(false); + setSummary(parentEntry, createNotifEntry(false)); + addChild(parentEntry, lowPriorityChild); + + // WHEN the child entry ranking changes to high priority + lowPriorityChild.setRanking( + new RankingBuilder() + .setKey(lowPriorityChild.getKey()) + .setImportance(IMPORTANCE_HIGH) + .build()); + + // THEN the parent entry's high priority value is updated - but not the parent's summary + assertTrue(mHighPriorityProvider.isHighPriority(parentEntry)); + assertFalse(mHighPriorityProvider.isHighPriority(parentEntry.getSummary())); + } + + private NotificationEntry createNotifEntry(boolean highPriority) { + return new NotificationEntryBuilder() + .setImportance(highPriority ? IMPORTANCE_HIGH : IMPORTANCE_MIN) + .build(); + } + + private void setSummary(GroupEntry parent, NotificationEntry summary) { + parent.setSummary(summary); + summary.setParent(parent); + } + + private void addChild(GroupEntry parent, NotificationEntry child) { + parent.addChild(child); + child.setParent(parent); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index 0837a42ae700..28feacac8c44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -24,6 +24,7 @@ import static com.android.systemui.statusbar.notification.collection.NotifCollec import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -261,9 +262,13 @@ public class NotifCollectionTest extends SysuiTestCase { .setRank(5) .setExplanation("baz buzz") .build(); + + // WHEN entry3's ranking update includes an update to its overrideGroupKey + final String newOverrideGroupKey = "newOverrideGroupKey"; Ranking newRanking3 = new RankingBuilder(notif3.ranking) .setRank(6) .setExplanation("Penguin pizza") + .setOverrideGroupKey(newOverrideGroupKey) .build(); mNoMan.setRanking(notif1.sbn.getKey(), newRanking1); @@ -275,6 +280,10 @@ public class NotifCollectionTest extends SysuiTestCase { assertEquals(newRanking1, entry1.getRanking()); assertEquals(newRanking2, entry2.getRanking()); assertEquals(newRanking3, entry3.getRanking()); + + // THEN the entry3's overrideGroupKey is updated along with its groupKey + assertEquals(newOverrideGroupKey, entry3.getSbn().getOverrideGroupKey()); + assertNotNull(entry3.getSbn().getGroupKey()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java index 39ae68a40291..5b0b66849027 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java @@ -21,8 +21,6 @@ import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.CATEGORY_EVENT; import static android.app.Notification.CATEGORY_MESSAGE; import static android.app.Notification.CATEGORY_REMINDER; -import static android.app.NotificationManager.IMPORTANCE_HIGH; -import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; @@ -92,44 +90,6 @@ public class NotificationEntryTest extends SysuiTestCase { } @Test - public void testIsHighPriority_notificationUpdates() { - // GIVEN a notification with high importance - final NotificationEntry entryHigh = new NotificationEntryBuilder() - .setImportance(IMPORTANCE_HIGH) - .build(); - - // WHEN we get the value for the high priority entry, we're caching the high priority value - assertTrue(entryHigh.isHighPriority()); - - // WHEN we change the ranking and derived members (high priority) are invalidated - entryHigh.setRanking( - new RankingBuilder() - .setKey(entryHigh.getKey()) - .setImportance(IMPORTANCE_MIN) - .build()); - entryHigh.invalidateDerivedMembers(); - - // THEN the priority is recalculated and is now low - assertFalse(entryHigh.isHighPriority()); - - // WHEN the sbn is updated to have messaging style (high priority characteristic) - // AND the entry invalidates its derived members - final Notification notification = - new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("")) - .build(); - final StatusBarNotification sbn = entryHigh.getSbn(); - entryHigh.setSbn(new StatusBarNotification( - sbn.getPackageName(), sbn.getPackageName(), - sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), - notification, sbn.getUser(), sbn.getOverrideGroupKey(), 0)); - entryHigh.invalidateDerivedMembers(); - - // THEN the priority is recalculated and is now high - assertTrue(entryHigh.isHighPriority()); - } - - @Test public void testIsExemptFromDndVisualSuppression_foreground() { mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index 10450fa82cde..e27319103525 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.logging.NotifLog import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING @@ -62,7 +63,8 @@ class NotificationRankingManagerTest : SysuiTestCase() { mock(NotificationFilter::class.java), mock(NotifLog::class.java), mock(NotificationSectionsFeatureManager::class.java), - personNotificationIdentifier + personNotificationIdentifier, + HighPriorityProvider(personNotificationIdentifier) ) } @@ -182,7 +184,8 @@ class NotificationRankingManagerTest : SysuiTestCase() { filter: NotificationFilter, notifLog: NotifLog, sectionsFeatureManager: NotificationSectionsFeatureManager, - peopleNotificationIdentifier: PeopleNotificationIdentifier + peopleNotificationIdentifier: PeopleNotificationIdentifier, + highPriorityProvider: HighPriorityProvider ) : NotificationRankingManager( mediaManager, groupManager, @@ -190,7 +193,8 @@ class NotificationRankingManagerTest : SysuiTestCase() { filter, notifLog, sectionsFeatureManager, - peopleNotificationIdentifier + peopleNotificationIdentifier, + highPriorityProvider ) { fun applyTestRankingMap(r: RankingMap) { rankingMap = r diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java index 979b8a906ef0..f921cf969e61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.notification.collection.NotifListBuilderIm import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -66,6 +67,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private StatusBarStateController mStatusBarStateController; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock private HighPriorityProvider mHighPriorityProvider; @Mock private NotifListBuilderImpl mNotifListBuilder; private NotificationEntry mEntry; @@ -78,7 +80,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { mKeyguardCoordinator = new KeyguardCoordinator( mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager, mBroadcastDispatcher, mStatusBarStateController, - mKeyguardUpdateMonitor); + mKeyguardUpdateMonitor, mHighPriorityProvider); mEntry = new NotificationEntryBuilder() .setUser(new UserHandle(NOTIF_USER_ID)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java deleted file mode 100644 index 6fa1a89515c3..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java +++ /dev/null @@ -1,184 +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.statusbar.notification.collection.provider; - -import static android.app.NotificationManager.IMPORTANCE_HIGH; -import static android.app.NotificationManager.IMPORTANCE_LOW; -import static android.app.NotificationManager.IMPORTANCE_MIN; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.Person; -import android.testing.AndroidTestingRunner; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class IsHighPriorityProviderTest extends SysuiTestCase { - private IsHighPriorityProvider mIsHighPriorityProvider; - - @Before - public void setup() { - mIsHighPriorityProvider = new IsHighPriorityProvider(); - } - - @Test - public void testCache() { - // GIVEN a notification with high importance - final NotificationEntry entryHigh = new NotificationEntryBuilder() - .setImportance(IMPORTANCE_HIGH) - .build(); - - // GIVEN notification with min importance - final NotificationEntry entryMin = new NotificationEntryBuilder() - .setImportance(IMPORTANCE_MIN) - .build(); - - // WHEN we get the value for the high priority entry - assertTrue(mIsHighPriorityProvider.get(entryHigh)); - - // THEN the value is cached, so even when passed an entryMin, we still get high priority - assertTrue(mIsHighPriorityProvider.get(entryMin)); - - // UNTIL the provider is invalidated - mIsHighPriorityProvider.invalidate(); - - // THEN the priority is recalculated - assertFalse(mIsHighPriorityProvider.get(entryMin)); - } - - @Test - public void highImportance() { - // GIVEN notification has high importance - final NotificationEntry entry = new NotificationEntryBuilder() - .setImportance(IMPORTANCE_HIGH) - .build(); - - // THEN it has high priority - assertTrue(mIsHighPriorityProvider.get(entry)); - } - - @Test - public void peopleNotification() { - // GIVEN notification is low importance but has a person associated with it - final Notification notification = new Notification.Builder(mContext, "test") - .addPerson( - new Person.Builder() - .setName("name") - .setKey("abc") - .setUri("uri") - .setBot(true) - .build()) - .build(); - - final NotificationEntry entry = new NotificationEntryBuilder() - .setNotification(notification) - .setImportance(IMPORTANCE_LOW) - .build(); - - // THEN it has high priority - assertTrue(mIsHighPriorityProvider.get(entry)); - } - - @Test - public void messagingStyle() { - // GIVEN notification is low importance but has messaging style - final Notification notification = new Notification.Builder(mContext, "test") - .setStyle(new Notification.MessagingStyle("")) - .build(); - - final NotificationEntry entry = new NotificationEntryBuilder() - .setNotification(notification) - .build(); - - // THEN it has high priority - assertTrue(mIsHighPriorityProvider.get(entry)); - } - - @Test - public void lowImportanceForeground() { - // GIVEN notification is low importance and is associated with a foreground service - final Notification notification = mock(Notification.class); - when(notification.isForegroundService()).thenReturn(true); - - final NotificationEntry entry = new NotificationEntryBuilder() - .setNotification(notification) - .setImportance(IMPORTANCE_LOW) - .build(); - - // THEN it has high priority - assertTrue(mIsHighPriorityProvider.get(entry)); - } - - @Test - public void minImportanceForeground() { - // GIVEN notification is low importance and is associated with a foreground service - final Notification notification = mock(Notification.class); - when(notification.isForegroundService()).thenReturn(true); - - final NotificationEntry entry = new NotificationEntryBuilder() - .setNotification(notification) - .setImportance(IMPORTANCE_MIN) - .build(); - - // THEN it does NOT have high priority - assertFalse(mIsHighPriorityProvider.get(entry)); - } - - @Test - public void userChangeTrumpsHighPriorityCharacteristics() { - // GIVEN notification has high priority characteristics but the user changed the importance - // to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN) - final Notification notification = new Notification.Builder(mContext, "test") - .addPerson( - new Person.Builder() - .setName("name") - .setKey("abc") - .setUri("uri") - .setBot(true) - .build()) - .setStyle(new Notification.MessagingStyle("")) - .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) - .build(); - - final NotificationChannel channel = new NotificationChannel("a", "a", - IMPORTANCE_LOW); - channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); - - final NotificationEntry entry = new NotificationEntryBuilder() - .setNotification(notification) - .setChannel(channel) - .build(); - - // THEN it does NOT have high priority - assertFalse(mIsHighPriorityProvider.get(entry)); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt index 0764d0cd4b88..867a9b97d622 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt @@ -178,9 +178,10 @@ private fun <T> castNull(): T = null as T private fun fakePersonModel( id: String, name: CharSequence, - clickIntent: PendingIntent + clickIntent: PendingIntent, + userId: Int = 0 ): PersonModel = - PersonModel(id, name, mock(Drawable::class.java), clickIntent) + PersonModel(id, name, mock(Drawable::class.java), clickIntent, userId) private fun fakePersonViewModel(name: CharSequence): PersonViewModel = PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass)) @@ -207,4 +208,4 @@ class FakeDataListener<T> : DataListener<T> { override fun onDataChanged(data: T) { lastSeen = Maybe.Just(data) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index dc4e4980e443..f916fe5ad7fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -33,7 +33,6 @@ import android.content.Context; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.util.ArrayMap; @@ -179,7 +178,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { true /* isNewView */, (v, p, r) -> true, new InflationCallback() { @Override - public void handleInflationException(StatusBarNotification notification, + public void handleInflationException(NotificationEntry entry, Exception e) { countDownLatch.countDown(); throw new RuntimeException("No Exception expected"); @@ -261,7 +260,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { inflater.setInflateSynchronously(true); InflationCallback callback = new InflationCallback() { @Override - public void handleInflationException(StatusBarNotification notification, + public void handleInflationException(NotificationEntry entry, Exception e) { if (!expectingException) { exceptionHolder.setException(e); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index ccc9496368e9..4e27770982e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.StatusBar; @@ -113,6 +114,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private StatusBar mStatusBar; @Mock private AccessibilityManager mAccessibilityManager; + @Mock private HighPriorityProvider mHighPriorityProvider; @Before public void setUp() { @@ -128,7 +130,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager, - () -> mStatusBar, mHandler, mAccessibilityManager); + () -> mStatusBar, mHandler, mAccessibilityManager, mHighPriorityProvider); mGutsManager.setUpWithPresenter(mPresenter, mStackScroller, mCheckSaveListener, mOnSettingsClickListener); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); @@ -391,6 +393,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { .build(); when(row.getIsNonblockable()).thenReturn(false); + when(mHighPriorityProvider.isHighPriority(entry)).thenReturn(true); StatusBarNotification statusBarNotification = entry.getSbn(); mGutsManager.initializeNotificationInfo(row, notificationInfoView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index 003d80376c40..518b6703391e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -267,8 +267,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); when(notifRow.getVisibility()).thenReturn(View.VISIBLE); - when(notifRow.getEntry().isHighPriority()) - .thenReturn(children[i] == ChildType.HIPRI); when(notifRow.getEntry().getBucket()).thenReturn( children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT); when(notifRow.getParent()).thenReturn(mNssl); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 77a6a2602d8e..ea8d4ee20aab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -53,6 +53,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.EmptyShadeView; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -70,6 +71,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; @@ -164,9 +166,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mock(NotificationFilter.class), mock(NotifLog.class), mock(NotificationSectionsFeatureManager.class), - mock(PeopleNotificationIdentifier.class) + mock(PeopleNotificationIdentifier.class), + mock(HighPriorityProvider.class) ), - mock(NotificationEntryManager.KeyguardEnvironment.class)); + mock(NotificationEntryManager.KeyguardEnvironment.class), + mock(FeatureFlags.class)); mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java index 39afbe0a1a23..8f645b6bd3c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java @@ -190,7 +190,7 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { mFragments.dispatchResume(); processAllMessages(); - verify(mBroadcastDispatcher).registerReceiver( + verify(mBroadcastDispatcher).registerReceiverWithHandler( any(BroadcastReceiver.class), any(IntentFilter.class), any(Handler.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index 2854665aedb1..80aa6f6c49bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -69,7 +69,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { @Test public void testRegisteredWithDispatcher() { - verify(mBroadcastDispatcher).registerReceiver(any(BroadcastReceiver.class), + verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class), any(IntentFilter.class), any(Handler.class)); // VolumeDialogControllerImpl does not call with user } diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index 0be853a81fb2..d297f3f84189 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -20,7 +20,7 @@ java_defaults { srcs: [ "src/**/*.java", ":framework-tethering-shared-srcs", - ":net-module-utils-srcs", + ":tethering-module-utils-srcs", ":services-tethering-shared-srcs", ], static_libs: [ @@ -123,4 +123,5 @@ android_app { use_embedded_native_libs: true, // The permission configuration *must* be included to ensure security of the device required: ["NetworkPermissionConfig"], + apex_available: ["com.android.tethering"], } diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml index 87a8c3f5c68a..e99c2c529bd2 100644 --- a/packages/Tethering/AndroidManifest.xml +++ b/packages/Tethering/AndroidManifest.xml @@ -33,6 +33,7 @@ <uses-permission android:name="android.permission.MANAGE_USB" /> <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> + <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 5785707cb9c5..264ce440f59f 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -47,6 +47,16 @@ java_library { libs: [ "android_system_stubs_current", ], + + hostdex: true, // for hiddenapi check + visibility: [ + "//frameworks/base/packages/Tethering:__subpackages__", + //TODO(b/147200698) remove below lines when the platform is built with stubs + "//frameworks/base", + "//frameworks/base/services", + "//frameworks/base/services/core", + ], + apex_available: ["com.android.tethering"], } filegroup { diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index a49ab85d2faf..11e57186c666 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -15,8 +15,6 @@ */ package android.net; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; - import android.annotation.NonNull; import android.content.Context; import android.net.ConnectivityManager.OnTetheringEventCallback; @@ -52,6 +50,103 @@ public class TetheringManager { private TetheringConfigurationParcel mTetheringConfiguration; private TetherStatesParcel mTetherStatesParcel; + /** + * Broadcast Action: A tetherable connection has come or gone. + * Uses {@code TetheringManager.EXTRA_AVAILABLE_TETHER}, + * {@code TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY}, + * {@code TetheringManager.EXTRA_ACTIVE_TETHER}, and + * {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate + * the current state of tethering. Each include a list of + * interface names in that state (may be empty). + */ + public static final String ACTION_TETHER_STATE_CHANGED = + "android.net.conn.TETHER_STATE_CHANGED"; + + /** + * gives a String[] listing all the interfaces configured for + * tethering and currently available for tethering. + */ + public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + + /** + * gives a String[] listing all the interfaces currently in local-only + * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding) + */ + public static final String EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray"; + + /** + * gives a String[] listing all the interfaces currently tethered + * (ie, has DHCPv4 support and packets potentially forwarded/NATed) + */ + public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + + /** + * gives a String[] listing all the interfaces we tried to tether and + * failed. Use {@link #getLastTetherError} to find the error code + * for any interfaces listed here. + */ + public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + + /** + * Invalid tethering type. + * @see #startTethering. + */ + public static final int TETHERING_INVALID = -1; + + /** + * Wifi tethering type. + * @see #startTethering. + */ + public static final int TETHERING_WIFI = 0; + + /** + * USB tethering type. + * @see #startTethering. + */ + public static final int TETHERING_USB = 1; + + /** + * Bluetooth tethering type. + * @see #startTethering. + */ + public static final int TETHERING_BLUETOOTH = 2; + + /** + * Wifi P2p tethering type. + * Wifi P2p tethering is set through events automatically, and don't + * need to start from #startTethering. + */ + public static final int TETHERING_WIFI_P2P = 3; + + /** + * Extra used for communicating with the TetherService. Includes the type of tethering to + * enable if any. + */ + public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; + + /** + * Extra used for communicating with the TetherService. Includes the type of tethering for + * which to cancel provisioning. + */ + public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; + + /** + * Extra used for communicating with the TetherService. True to schedule a recheck of tether + * provisioning. + */ + public static final String EXTRA_SET_ALARM = "extraSetAlarm"; + + /** + * Tells the TetherService to run a provision check now. + */ + public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; + + /** + * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} + * which will receive provisioning results. Can be left empty. + */ + public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + public static final int TETHER_ERROR_NO_ERROR = 0; public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; @@ -470,7 +565,7 @@ public class TetheringManager { * failed. Re-attempting to tether may cause them to reset to the Tethered * state. Alternatively, causing the interface to be destroyed and recreated * may cause them to reset to the available state. - * {@link ConnectivityManager#getLastTetherError} can be used to get more + * {@link TetheringManager#getLastTetherError} can be used to get more * information on the cause of the errors. * * @return an array of 0 or more String indicating the interface names diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt index d93531bac58e..c6efa41e580a 100644 --- a/packages/Tethering/jarjar-rules.txt +++ b/packages/Tethering/jarjar-rules.txt @@ -11,6 +11,7 @@ rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering. rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 +rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index abfb33c7af9e..6ac467e39a9d 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -24,25 +24,24 @@ import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.NetworkConstants.asByte; import static android.net.util.TetheringMessageBase.BASE_IPSERVER; -import android.net.ConnectivityManager; import android.net.INetd; import android.net.INetworkStackStatusCallback; import android.net.INetworkStatsService; -import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; +import android.net.TetheringManager; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcelExt; import android.net.dhcp.IDhcpServer; import android.net.ip.RouterAdvertisementDaemon.RaParams; +import android.net.shared.NetdUtils; +import android.net.shared.RouteUtils; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; -import android.net.util.NetdService; import android.net.util.SharedLog; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -119,7 +118,7 @@ public class IpServer extends StateMachine { * * @param who the calling instance of IpServer * @param state one of STATE_* - * @param lastError one of ConnectivityManager.TETHER_ERROR_* + * @param lastError one of TetheringManager.TETHER_ERROR_* */ public void updateInterfaceState(IpServer who, int state, int lastError) { } @@ -144,10 +143,6 @@ public class IpServer extends StateMachine { return InterfaceParams.getByName(ifName); } - public INetd getNetdService() { - return NetdService.getInstance(); - } - /** Create a DhcpServer instance to be used by IpServer. */ public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb); @@ -180,7 +175,6 @@ public class IpServer extends StateMachine { private final State mUnavailableState; private final SharedLog mLog; - private final INetworkManagementService mNMService; private final INetd mNetd; private final INetworkStatsService mStatsService; private final Callback mCallback; @@ -210,15 +204,15 @@ public class IpServer extends StateMachine { private int mDhcpServerStartIndex = 0; private IDhcpServer mDhcpServer; private RaParams mLastRaParams; + private LinkAddress mIpv4Address; public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetworkManagementService nMService, INetworkStatsService statsService, - Callback callback, boolean usingLegacyDhcp, Dependencies deps) { + INetd netd, INetworkStatsService statsService, Callback callback, + boolean usingLegacyDhcp, Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); - mNMService = nMService; - mNetd = deps.getNetdService(); + mNetd = netd; mStatsService = statsService; mCallback = callback; mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); @@ -228,7 +222,7 @@ public class IpServer extends StateMachine { mUsingLegacyDhcp = usingLegacyDhcp; mDeps = deps; resetLinkProperties(); - mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; + mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; mServingMode = STATE_AVAILABLE; mInitialState = new InitialState(); @@ -249,7 +243,7 @@ public class IpServer extends StateMachine { } /** - * Tethering downstream type. It would be one of ConnectivityManager#TETHERING_*. + * Tethering downstream type. It would be one of TetheringManager#TETHERING_*. */ public int interfaceType() { return mInterfaceType; @@ -347,13 +341,13 @@ public class IpServer extends StateMachine { } }); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw new IllegalStateException(e); } }); } private void handleError() { - mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR; + mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; transitionTo(mInitialState); } } @@ -388,14 +382,15 @@ public class IpServer extends StateMachine { public void callback(int statusCode) { if (statusCode != STATUS_SUCCESS) { mLog.e("Error stopping DHCP server: " + statusCode); - mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR; + mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; // Not much more we can do here } } }); mDhcpServer = null; } catch (RemoteException e) { - e.rethrowFromSystemServer(); + mLog.e("Error stopping DHCP", e); + // Not much more we can do here } } } @@ -414,85 +409,69 @@ public class IpServer extends StateMachine { // NOTE: All of configureIPv4() will be refactored out of existence // into calls to InterfaceController, shared with startIPv4(). mInterfaceCtrl.clearIPv4Address(); + mIpv4Address = null; } - // TODO: Refactor this in terms of calls to InterfaceController. private boolean configureIPv4(boolean enabled) { if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")"); // TODO: Replace this hard-coded information with dynamically selected // config passed down to us by a higher layer IP-coordinating element. - String ipAsString = null; + final Inet4Address srvAddr; int prefixLen = 0; - if (mInterfaceType == ConnectivityManager.TETHERING_USB) { - ipAsString = USB_NEAR_IFACE_ADDR; - prefixLen = USB_PREFIX_LENGTH; - } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { - ipAsString = getRandomWifiIPv4Address(); - prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH; - } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI_P2P) { - ipAsString = WIFI_P2P_IFACE_ADDR; - prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; - } else { - // BT configures the interface elsewhere: only start DHCP. - final Inet4Address srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR); - return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); - } - - final LinkAddress linkAddr; try { - final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName); - if (ifcg == null) { - mLog.e("Received null interface config"); - return false; - } - - InetAddress addr = parseNumericAddress(ipAsString); - linkAddr = new LinkAddress(addr, prefixLen); - ifcg.setLinkAddress(linkAddr); - if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { - // The WiFi stack has ownership of the interface up/down state. - // It is unclear whether the Bluetooth or USB stacks will manage their own - // state. - ifcg.ignoreInterfaceUpDownStatus(); + if (mInterfaceType == TetheringManager.TETHERING_USB) { + srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); + prefixLen = USB_PREFIX_LENGTH; + } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) { + srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address()); + prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH; + } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) { + srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR); + prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; } else { - if (enabled) { - ifcg.setInterfaceUp(); - } else { - ifcg.setInterfaceDown(); - } + // BT configures the interface elsewhere: only start DHCP. + // TODO: make all tethering types behave the same way, and delete the bluetooth + // code that calls into NetworkManagementService directly. + srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR); + mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); + return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); } - ifcg.clearFlag("running"); + mIpv4Address = new LinkAddress(srvAddr, prefixLen); + } catch (IllegalArgumentException e) { + mLog.e("Error selecting ipv4 address", e); + if (!enabled) stopDhcp(); + return false; + } - // TODO: this may throw if the interface is already gone. Do proper handling and - // simplify the DHCP server start/stop. - mNMService.setInterfaceConfig(mIfaceName, ifcg); + final Boolean setIfaceUp; + if (mInterfaceType == TetheringManager.TETHERING_WIFI) { + // The WiFi stack has ownership of the interface up/down state. + // It is unclear whether the Bluetooth or USB stacks will manage their own + // state. + setIfaceUp = null; + } else { + setIfaceUp = enabled; + } + if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) { + mLog.e("Error configuring interface"); + if (!enabled) stopDhcp(); + return false; + } - if (!configureDhcp(enabled, (Inet4Address) addr, prefixLen)) { - return false; - } - } catch (Exception e) { - mLog.e("Error configuring interface " + e); - if (!enabled) { - try { - // Calling stopDhcp several times is fine - stopDhcp(); - } catch (Exception dhcpError) { - mLog.e("Error stopping DHCP", dhcpError); - } - } + if (!configureDhcp(enabled, srvAddr, prefixLen)) { return false; } // Directly-connected route. - final IpPrefix ipv4Prefix = new IpPrefix(linkAddr.getAddress(), - linkAddr.getPrefixLength()); + final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), + mIpv4Address.getPrefixLength()); final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST); if (enabled) { - mLinkProperties.addLinkAddress(linkAddr); + mLinkProperties.addLinkAddress(mIpv4Address); mLinkProperties.addRoute(route); } else { - mLinkProperties.removeLinkAddress(linkAddr); + mLinkProperties.removeLinkAddress(mIpv4Address); mLinkProperties.removeRoute(route); } return true; @@ -584,14 +563,12 @@ public class IpServer extends StateMachine { if (!deprecatedPrefixes.isEmpty()) { final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(mIfaceName, deprecatedPrefixes); - try { - final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved); - if (removalFailures > 0) { - mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", - removalFailures)); - } - } catch (RemoteException e) { - mLog.e("Failed to remove IPv6 routes from local table: " + e); + // Remove routes from local network. + final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork( + mNetd, toBeRemoved); + if (removalFailures > 0) { + mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", + removalFailures)); } for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route); @@ -608,13 +585,18 @@ public class IpServer extends StateMachine { final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(mIfaceName, addedPrefixes); try { - // It's safe to call addInterfaceToLocalNetwork() even if - // the interface is already in the local_network. Note also - // that adding routes that already exist does not cause an - // error (EEXIST is silently ignored). - mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded); - } catch (Exception e) { - mLog.e("Failed to add IPv6 routes to local table: " + e); + // It's safe to call networkAddInterface() even if + // the interface is already in the local_network. + mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName); + try { + // Add routes from local network. Note that adding routes that + // already exist does not cause an error (EEXIST is silently ignored). + RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded); + } catch (IllegalStateException e) { + mLog.e("Failed to add IPv6 routes to local table: " + e); + } + } catch (ServiceSpecificException | RemoteException e) { + mLog.e("Failed to add " + mIfaceName + " to local table: ", e); } for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route); @@ -728,7 +710,7 @@ public class IpServer extends StateMachine { logMessage(this, message.what); switch (message.what) { case CMD_TETHER_REQUESTED: - mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; + mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; switch (message.arg1) { case STATE_LOCAL_ONLY: transitionTo(mLocalHotspotState); @@ -757,15 +739,17 @@ public class IpServer extends StateMachine { @Override public void enter() { if (!startIPv4()) { - mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR; + mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; return; } try { - mNMService.tetherInterface(mIfaceName); - } catch (Exception e) { + final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), + mIpv4Address.getPrefixLength()); + NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix); + } catch (RemoteException | ServiceSpecificException e) { mLog.e("Error Tethering: " + e); - mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; + mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; return; } @@ -784,9 +768,9 @@ public class IpServer extends StateMachine { stopIPv6(); try { - mNMService.untetherInterface(mIfaceName); - } catch (Exception e) { - mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; + NetdUtils.untetherInterface(mNetd, mIfaceName); + } catch (RemoteException | ServiceSpecificException e) { + mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; mLog.e("Failed to untether interface: " + e); } @@ -816,7 +800,7 @@ public class IpServer extends StateMachine { case CMD_START_TETHERING_ERROR: case CMD_STOP_TETHERING_ERROR: case CMD_SET_DNS_FORWARDERS_ERROR: - mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR; + mLastError = TetheringManager.TETHER_ERROR_MASTER_ERROR; transitionTo(mInitialState); break; default: @@ -835,7 +819,7 @@ public class IpServer extends StateMachine { @Override public void enter() { super.enter(); - if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) { transitionTo(mInitialState); } @@ -871,7 +855,7 @@ public class IpServer extends StateMachine { @Override public void enter() { super.enter(); - if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) { transitionTo(mInitialState); } @@ -901,17 +885,17 @@ public class IpServer extends StateMachine { // About to tear down NAT; gather remaining statistics. mStatsService.forceUpdate(); } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); + mLog.e("Exception in forceUpdate: " + e.toString()); } try { - mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString()); + mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString()); } try { - mNMService.disableNat(mIfaceName, upstreamIface); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); + mNetd.tetherRemoveForward(mIfaceName, upstreamIface); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Exception in disableNat: " + e.toString()); } } @@ -947,12 +931,12 @@ public class IpServer extends StateMachine { for (String ifname : added) { try { - mNMService.enableNat(mIfaceName, ifname); - mNMService.startInterfaceForwarding(mIfaceName, ifname); - } catch (Exception e) { - mLog.e("Exception enabling NAT: " + e); + mNetd.tetherAddForward(mIfaceName, ifname); + mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname); + } catch (RemoteException | ServiceSpecificException e) { + mLog.e("Exception enabling NAT: " + e.toString()); cleanupUpstream(); - mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; + mLastError = TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); return true; } @@ -997,7 +981,7 @@ public class IpServer extends StateMachine { class UnavailableState extends State { @Override public void enter() { - mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; + mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; sendInterfaceState(STATE_UNAVAILABLE); } } diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java index bba61d72d8d6..6f017dcb623f 100644 --- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java +++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java @@ -668,7 +668,7 @@ public class RouterAdvertisementDaemon { } private final class UnicastResponder extends Thread { - private final InetSocketAddress mSolicitor = new InetSocketAddress(); + private final InetSocketAddress mSolicitor = new InetSocketAddress(0); // The recycled buffer for receiving Router Solicitations from clients. // If the RS is larger than IPV6_MIN_MTU the packets are truncated. // This is fine since currently only byte 0 is examined anyway. diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index 7e685fbe97f1..4e2a2c1c7af7 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -16,16 +16,16 @@ package com.android.server.connectivity.tethering; -import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE; -import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK; -import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION; -import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; -import static android.net.ConnectivityManager.TETHERING_INVALID; -import static android.net.ConnectivityManager.TETHERING_USB; -import static android.net.ConnectivityManager.TETHERING_WIFI; -import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED; +import static android.net.TetheringManager.EXTRA_ADD_TETHER_TYPE; +import static android.net.TetheringManager.EXTRA_PROVISION_CALLBACK; +import static android.net.TetheringManager.EXTRA_RUN_PROVISION; +import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_INVALID; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; +import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; import static com.android.internal.R.string.config_wifi_tether_enable; @@ -59,7 +59,7 @@ import java.io.PrintWriter; /** * Re-check tethering provisioning for enabled downstream tether types. - * Reference ConnectivityManager.TETHERING_{@code *} for each tether type. + * Reference TetheringManager.TETHERING_{@code *} for each tether type. * * All methods of this class must be accessed from the thread of tethering * state machine. @@ -86,9 +86,9 @@ public class EntitlementManager { private static final int EVENT_GET_ENTITLEMENT_VALUE = 4; // The ArraySet contains enabled downstream types, ex: - // {@link ConnectivityManager.TETHERING_WIFI} - // {@link ConnectivityManager.TETHERING_USB} - // {@link ConnectivityManager.TETHERING_BLUETOOTH} + // {@link TetheringManager.TETHERING_WIFI} + // {@link TetheringManager.TETHERING_USB} + // {@link TetheringManager.TETHERING_BLUETOOTH} private final ArraySet<Integer> mCurrentTethers; private final Context mContext; private final int mPermissionChangeMessageCode; @@ -96,8 +96,8 @@ public class EntitlementManager { private final SparseIntArray mEntitlementCacheValue; private final EntitlementHandler mHandler; private final StateMachine mTetherMasterSM; - // Key: ConnectivityManager.TETHERING_*(downstream). - // Value: ConnectivityManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). + // Key: TetheringManager.TETHERING_*(downstream). + // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). private final SparseIntArray mCellularPermitted; private PendingIntent mProvisioningRecheckAlarm; private boolean mCellularUpstreamPermitted = true; @@ -133,7 +133,7 @@ public class EntitlementManager { /** * Ui entitlement check fails in |downstream|. * - * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}. + * @param downstream tethering type from TetheringManager.TETHERING_{@code *}. */ void onUiEntitlementFailed(int downstream); } @@ -169,7 +169,7 @@ public class EntitlementManager { * This is called when tethering starts. * Launch provisioning app if upstream is cellular. * - * @param downstreamType tethering type from ConnectivityManager.TETHERING_{@code *} + * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *} * @param showProvisioningUi a boolean indicating whether to show the * provisioning app UI if there is one. */ @@ -210,7 +210,7 @@ public class EntitlementManager { /** * Tell EntitlementManager that a given type of tethering has been disabled * - * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param type tethering type from TetheringManager.TETHERING_{@code *} */ public void stopProvisioningIfNeeded(int type) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0)); @@ -296,7 +296,7 @@ public class EntitlementManager { /** * Re-check tethering provisioning for all enabled tether types. - * Reference ConnectivityManager.TETHERING_{@code *} for each tether type. + * Reference TetheringManager.TETHERING_{@code *} for each tether type. * * @param config an object that encapsulates the various tethering configuration elements. * Note: this method is only called from TetherMaster on the handler thread. @@ -363,7 +363,7 @@ public class EntitlementManager { /** * Run no UI tethering provisioning check. - * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param type tethering type from TetheringManager.TETHERING_{@code *} * @param subId default data subscription ID. */ @VisibleForTesting @@ -390,7 +390,7 @@ public class EntitlementManager { /** * Run the UI-enabled tethering provisioning check. - * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param type tethering type from TetheringManager.TETHERING_{@code *} * @param subId default data subscription ID. * @param receiver to receive entitlement check result. */ @@ -398,7 +398,7 @@ public class EntitlementManager { protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { if (DBG) mLog.i("runUiTetherProvisioning: " + type); - Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING); + Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); intent.putExtra(EXTRA_SUBID, subId); @@ -461,7 +461,7 @@ public class EntitlementManager { * Add the mapping between provisioning result and tethering type. * Notify UpstreamNetworkMonitor if Cellular permission changes. * - * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param type tethering type from TetheringManager.TETHERING_{@code *} * @param resultCode Provisioning result */ protected void addDownstreamMapping(int type, int resultCode) { @@ -476,7 +476,7 @@ public class EntitlementManager { /** * Remove the mapping for input tethering type. - * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param type tethering type from TetheringManager.TETHERING_{@code *} */ protected void removeDownstreamMapping(int type) { mLog.i("removeDownstreamMapping: " + type); @@ -625,7 +625,7 @@ public class EntitlementManager { /** * Update the last entitlement value to internal cache * - * @param type tethering type from ConnectivityManager.TETHERING_{@code *} + * @param type tethering type from TetheringManager.TETHERING_{@code *} * @param resultCode last entitlement value * @return the last updated entitlement value */ diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java index 38fa91e7387e..ce7c2a669f0a 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java @@ -279,7 +279,7 @@ public class OffloadController { entry.iface = kv.getKey(); entry.rxBytes = value.rxBytes; entry.txBytes = value.txBytes; - stats.addValues(entry); + stats.addEntry(entry); } return stats; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 5b267046a851..d6abfb922a9c 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -20,23 +20,23 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; -import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; -import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY; -import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER; -import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER; -import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER; import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; -import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; -import static android.net.ConnectivityManager.TETHERING_INVALID; -import static android.net.ConnectivityManager.TETHERING_USB; -import static android.net.ConnectivityManager.TETHERING_WIFI; -import static android.net.ConnectivityManager.TETHERING_WIFI_P2P; -import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL; -import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE; -import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; +import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; +import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; +import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; +import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; +import static android.net.TetheringManager.EXTRA_ERRORED_TETHER; +import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_INVALID; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; +import static android.net.TetheringManager.TETHER_ERROR_MASTER_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; +import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE; +import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; import static android.net.util.TetheringMessageBase.BASE_MASTER; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; @@ -71,10 +71,10 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkInfo; -import android.net.NetworkUtils; import android.net.TetherStatesParcel; import android.net.TetheringConfigurationParcel; import android.net.ip.IpServer; +import android.net.shared.NetdUtils; import android.net.util.BaseNetdUnsolicitedEventListener; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; @@ -87,12 +87,12 @@ import android.net.wifi.p2p.WifiP2pManager; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceSpecificException; import android.os.UserHandle; import android.os.UserManager; import android.telephony.PhoneStateListener; @@ -102,6 +102,9 @@ import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -139,6 +142,8 @@ public class Tethering { }; private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(sMessageClasses); + // Keep in sync with NETID_UNSET in system/netd/include/netid_client.h + private static final int NETID_UNSET = 0; private static class TetherState { public final IpServer ipServer; @@ -172,8 +177,6 @@ public class Tethering { private final Context mContext; private final ArrayMap<String, TetherState> mTetherStates; private final BroadcastReceiver mStateReceiver; - // Stopship: replace mNMService before production. - private final INetworkManagementService mNMService; private final INetworkStatsService mStatsService; private final INetworkPolicyManager mPolicyManager; private final Looper mLooper; @@ -210,7 +213,6 @@ public class Tethering { mLog.mark("Tethering.constructed"); mDeps = deps; mContext = mDeps.getContext(); - mNMService = mDeps.getINetworkManagementService(); mStatsService = mDeps.getINetworkStatsService(); mPolicyManager = mDeps.getINetworkPolicyManager(); mNetd = mDeps.getINetd(mContext); @@ -225,10 +227,9 @@ public class Tethering { mHandler = mTetherMasterSM.getHandler(); mOffloadController = new OffloadController(mHandler, - mDeps.getOffloadHardwareInterface(mHandler, mLog), - mContext.getContentResolver(), mNMService, - mLog); - mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, + mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(), + mDeps.getINetworkManagementService(), mLog); + mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); @@ -289,13 +290,6 @@ public class Tethering { filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); mContext.registerReceiver(mStateReceiver, filter, null, handler); - - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_MEDIA_SHARED); - filter.addAction(Intent.ACTION_MEDIA_UNSHARED); - filter.addDataScheme("file"); - mContext.registerReceiver(mStateReceiver, filter, null, handler); - } private class TetheringThreadExecutor implements Executor { @@ -421,7 +415,6 @@ public class Tethering { } } - void interfaceRemoved(String iface) { if (VDBG) Log.d(TAG, "interfaceRemoved " + iface); synchronized (mPublicSync) { @@ -1022,8 +1015,8 @@ public class Tethering { String[] ifaces = null; try { - ifaces = mNMService.listInterfaces(); - } catch (Exception e) { + ifaces = mNetd.interfaceGetList(); + } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Error listing Interfaces", e); return; } @@ -1282,25 +1275,25 @@ public class Tethering { protected boolean turnOnMasterTetherSettings() { final TetheringConfiguration cfg = mConfig; try { - mNMService.setIpForwardingEnabled(true); - } catch (Exception e) { + mNetd.ipfwdEnableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { mLog.e(e); transitionTo(mSetIpForwardingEnabledErrorState); return false; } + // TODO: Randomize DHCPv4 ranges, especially in hotspot mode. // Legacy DHCP server is disabled if passed an empty ranges array final String[] dhcpRanges = cfg.enableLegacyDhcpServer - ? cfg.legacyDhcpRanges - : new String[0]; + ? cfg.legacyDhcpRanges : new String[0]; try { - // TODO: Find a more accurate method name (startDHCPv4()?). - mNMService.startTethering(dhcpRanges); - } catch (Exception e) { + NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); + } catch (RemoteException | ServiceSpecificException e) { try { - mNMService.stopTethering(); - mNMService.startTethering(dhcpRanges); - } catch (Exception ee) { + // Stop and retry. + mNetd.tetherStop(); + NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges); + } catch (RemoteException | ServiceSpecificException ee) { mLog.e(ee); transitionTo(mStartTetheringErrorState); return false; @@ -1312,15 +1305,15 @@ public class Tethering { protected boolean turnOffMasterTetherSettings() { try { - mNMService.stopTethering(); - } catch (Exception e) { + mNetd.tetherStop(); + } catch (RemoteException | ServiceSpecificException e) { mLog.e(e); transitionTo(mStopTetheringErrorState); return false; } try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { mLog.e(e); transitionTo(mSetIpForwardingDisabledErrorState); return false; @@ -1383,19 +1376,25 @@ public class Tethering { protected void setDnsForwarders(final Network network, final LinkProperties lp) { // TODO: Set v4 and/or v6 DNS per available connectivity. - String[] dnsServers = mConfig.defaultIPv4DNS; final Collection<InetAddress> dnses = lp.getDnsServers(); // TODO: Properly support the absence of DNS servers. + final String[] dnsServers; if (dnses != null && !dnses.isEmpty()) { - // TODO: remove this invocation of NetworkUtils.makeStrings(). - dnsServers = NetworkUtils.makeStrings(dnses); + dnsServers = new String[dnses.size()]; + int i = 0; + for (InetAddress dns : dnses) { + dnsServers[i++] = dns.getHostAddress(); + } + } else { + dnsServers = mConfig.defaultIPv4DNS; } + final int netId = (network != null) ? network.netId : NETID_UNSET; try { - mNMService.setDnsForwarders(network, dnsServers); + mNetd.tetherDnsSet(netId, dnsServers); mLog.log(String.format( "SET DNS forwarders: network=%s dnsServers=%s", network, Arrays.toString(dnsServers))); - } catch (Exception e) { + } catch (RemoteException | ServiceSpecificException e) { // TODO: Investigate how this can fail and what exactly // happens if/when such failures occur. mLog.e("setting DNS forwarders failed, " + e); @@ -1698,8 +1697,8 @@ public class Tethering { Log.e(TAG, "Error in startTethering"); notify(IpServer.CMD_START_TETHERING_ERROR); try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { } + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { } } } @@ -1709,8 +1708,8 @@ public class Tethering { Log.e(TAG, "Error in stopTethering"); notify(IpServer.CMD_STOP_TETHERING_ERROR); try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { } + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { } } } @@ -1720,11 +1719,11 @@ public class Tethering { Log.e(TAG, "Error in setDnsForwarders"); notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR); try { - mNMService.stopTethering(); - } catch (Exception e) { } + mNetd.tetherStop(); + } catch (RemoteException | ServiceSpecificException e) { } try { - mNMService.setIpForwardingEnabled(false); - } catch (Exception e) { } + mNetd.ipfwdDisableForwarding(TAG); + } catch (RemoteException | ServiceSpecificException e) { } } } @@ -1884,7 +1883,7 @@ public class Tethering { } } - void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // Binder.java closes the resource for us. @SuppressWarnings("resource") final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); @@ -2065,7 +2064,7 @@ public class Tethering { mLog.log("adding TetheringInterfaceStateMachine for: " + iface); final TetherState tetherState = new TetherState( - new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService, + new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService, makeControlCallback(), mConfig.enableLegacyDhcpServer, mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index 490614b03149..397ba8ada551 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -37,7 +37,6 @@ import static com.android.internal.R.string.config_mobile_hotspot_provision_app_ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; -import android.net.ConnectivityManager; import android.net.TetheringConfigurationParcel; import android.net.util.SharedLog; import android.provider.Settings; @@ -179,8 +178,8 @@ public class TetheringConfiguration { pw.print("chooseUpstreamAutomatically: "); pw.println(chooseUpstreamAutomatically); - dumpStringArray(pw, "preferredUpstreamIfaceTypes", - preferredUpstreamNames(preferredUpstreamIfaceTypes)); + pw.print("legacyPreredUpstreamIfaceTypes: "); + pw.println(Arrays.toString(toIntArray(preferredUpstreamIfaceTypes))); dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges); dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS); @@ -205,7 +204,7 @@ public class TetheringConfiguration { sj.add(String.format("isDunRequired:%s", isDunRequired)); sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically)); sj.add(String.format("preferredUpstreamIfaceTypes:%s", - makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes)))); + toIntArray(preferredUpstreamIfaceTypes))); sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi)); sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer)); @@ -234,21 +233,6 @@ public class TetheringConfiguration { return sj.toString(); } - private static String[] preferredUpstreamNames(Collection<Integer> upstreamTypes) { - String[] upstreamNames = null; - - if (upstreamTypes != null) { - upstreamNames = new String[upstreamTypes.size()]; - int i = 0; - for (Integer netType : upstreamTypes) { - upstreamNames[i] = ConnectivityManager.getNetworkTypeName(netType); - i++; - } - } - - return upstreamNames; - } - /** Check whether dun is required. */ public static boolean checkDunRequired(Context ctx) { final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); @@ -388,6 +372,15 @@ public class TetheringConfiguration { return false; } + private static int[] toIntArray(Collection<Integer> values) { + final int[] result = new int[values.size()]; + int index = 0; + for (Integer value : values) { + result[index++] = value; + } + return result; + } + /** * Convert this TetheringConfiguration to a TetheringConfigurationParcel. */ @@ -400,12 +393,7 @@ public class TetheringConfiguration { parcel.isDunRequired = isDunRequired; parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically; - int[] preferredTypes = new int[preferredUpstreamIfaceTypes.size()]; - int index = 0; - for (Integer type : preferredUpstreamIfaceTypes) { - preferredTypes[index++] = type; - } - parcel.preferredUpstreamIfaceTypes = preferredTypes; + parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes); parcel.legacyDhcpRanges = legacyDhcpRanges; parcel.defaultIPv4DNS = defaultIPv4DNS; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java index 6334c20c2acc..d5cdd8a004dc 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java @@ -22,14 +22,17 @@ import android.net.NetworkCapabilities; import android.net.RouteInfo; import android.net.util.InterfaceSet; -import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; /** * @hide */ public final class TetheringInterfaceUtils { + private static final InetAddress IN6ADDR_ANY = getByAddress( + new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + private static final InetAddress INADDR_ANY = getByAddress(new byte[] {0, 0, 0, 0}); + /** * Get upstream interfaces for tethering based on default routes for IPv4/IPv6. * @return null if there is no usable interface, or a set of at least one interface otherwise. @@ -40,7 +43,7 @@ public final class TetheringInterfaceUtils { } final LinkProperties lp = ns.linkProperties; - final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY); + final String if4 = getInterfaceForDestination(lp, INADDR_ANY); final String if6 = getIPv6Interface(ns); return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6); @@ -76,7 +79,7 @@ public final class TetheringInterfaceUtils { && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); return canTether - ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY) + ? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY) : null; } @@ -86,4 +89,12 @@ public final class TetheringInterfaceUtils { : null; return (ri != null) ? ri.getInterface() : null; } + + private static InetAddress getByAddress(final byte[] addr) { + try { + return InetAddress.getByAddress(null, addr); + } catch (UnknownHostException e) { + throw new AssertionError("illegal address length" + addr.length); + } + } } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index 775484eabfa3..e4e4a090603d 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -26,11 +26,11 @@ import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.net.ConnectivityManager; import android.net.IIntResultListener; import android.net.INetworkStackConnector; import android.net.ITetheringConnector; import android.net.ITetheringEventCallback; +import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -307,9 +307,15 @@ public class TetheringService extends Service { mDeps = new TetheringDependencies() { @Override public NetworkRequest getDefaultNetworkRequest() { - ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - return cm.getDefaultRequest(); + // TODO: b/147280869, add a proper system API to replace this. + final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + return trackDefaultRequest; } @Override diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index 22150f623a35..5692a6fc5c80 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -16,10 +16,12 @@ package com.android.server.connectivity.tethering; +import static android.net.ConnectivityManager.TYPE_BLUETOOTH; +import static android.net.ConnectivityManager.TYPE_ETHERNET; +import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_NONE; -import static android.net.ConnectivityManager.getNetworkTypeName; +import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -35,10 +37,11 @@ import android.net.NetworkRequest; import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.os.Handler; -import android.os.Process; import android.util.Log; +import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import com.android.internal.util.StateMachine; import java.util.HashMap; @@ -79,11 +82,25 @@ public class UpstreamNetworkMonitor { public static final int EVENT_ON_LINKPROPERTIES = 2; public static final int EVENT_ON_LOST = 3; public static final int NOTIFY_LOCAL_PREFIXES = 10; + // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default + // disabled. + @VisibleForTesting + public static final int TYPE_NONE = -1; private static final int CALLBACK_LISTEN_ALL = 1; private static final int CALLBACK_DEFAULT_INTERNET = 2; private static final int CALLBACK_MOBILE_REQUEST = 3; + private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray(); + static { + sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI); + sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH); + sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET); + } + private final Context mContext; private final SharedLog mLog; private final StateMachine mTarget; @@ -130,15 +147,15 @@ public class UpstreamNetworkMonitor { */ public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest, EntitlementManager entitle) { - // This is not really a "request", just a way of tracking the system default network. - // It's guaranteed not to actually bring up any networks because it's the same request - // as the ConnectivityService default request, and thus shares fate with it. We can't - // use registerDefaultNetworkCallback because it will not track the system default - // network if there is a VPN that applies to our UID. + + // defaultNetworkRequest is not really a "request", just a way of tracking the system + // default network. It's guaranteed not to actually bring up any networks because it's + // the should be the same request as the ConnectivityService default request, and thus + // shares fate with it. We can't use registerDefaultNetworkCallback because it will not + // track the system default network if there is a VPN that applies to our UID. if (mDefaultNetworkCallback == null) { - final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest); mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET); - cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler); + cm().requestNetwork(defaultNetworkRequest, mDefaultNetworkCallback, mHandler); } if (mEntitlementMgr == null) { mEntitlementMgr = entitle; @@ -204,7 +221,7 @@ public class UpstreamNetworkMonitor { final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI; final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder() - .setCapabilities(ConnectivityManager.networkCapabilitiesForType(legacyType)) + .setCapabilities(networkCapabilitiesForType(legacyType)) .build(); // The existing default network and DUN callbacks will be notified. @@ -239,7 +256,7 @@ public class UpstreamNetworkMonitor { final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType( mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted()); - mLog.log("preferred upstream type: " + getNetworkTypeName(typeStatePair.type)); + mLog.log("preferred upstream type: " + typeStatePair.type); switch (typeStatePair.type) { case TYPE_MOBILE_DUN: @@ -356,16 +373,6 @@ public class UpstreamNetworkMonitor { notifyTarget(EVENT_ON_LINKPROPERTIES, network); } - private void handleSuspended(Network network) { - if (!network.equals(mTetheringUpstreamNetwork)) return; - mLog.log("SUSPENDED current upstream: " + network); - } - - private void handleResumed(Network network) { - if (!network.equals(mTetheringUpstreamNetwork)) return; - mLog.log("RESUMED current upstream: " + network); - } - private void handleLost(Network network) { // There are few TODOs within ConnectivityService's rematching code // pertaining to spurious onLost() notifications. @@ -455,20 +462,6 @@ public class UpstreamNetworkMonitor { } @Override - public void onNetworkSuspended(Network network) { - if (mCallbackType == CALLBACK_LISTEN_ALL) { - handleSuspended(network); - } - } - - @Override - public void onNetworkResumed(Network network) { - if (mCallbackType == CALLBACK_LISTEN_ALL) { - handleResumed(network); - } - } - - @Override public void onLost(Network network) { if (mCallbackType == CALLBACK_DEFAULT_INTERNET) { mDefaultInternetNetwork = null; @@ -512,18 +505,15 @@ public class UpstreamNetworkMonitor { for (int type : preferredTypes) { NetworkCapabilities nc; try { - nc = ConnectivityManager.networkCapabilitiesForType(type); + nc = networkCapabilitiesForType(type); } catch (IllegalArgumentException iae) { - Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " - + ConnectivityManager.getNetworkTypeName(type)); + Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + type); continue; } if (!isCellularUpstreamPermitted && isCellular(nc)) { continue; } - nc.setSingleUid(Process.myUid()); - for (UpstreamNetworkState value : netStates) { if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) { continue; @@ -577,4 +567,28 @@ public class UpstreamNetworkMonitor { return null; } + + /** + * Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance. + * This function is used for deprecated legacy type and be disabled by default. + */ + @VisibleForTesting + public static NetworkCapabilities networkCapabilitiesForType(int type) { + final NetworkCapabilities nc = new NetworkCapabilities(); + + // Map from type to transports. + final int notFound = -1; + final int transport = sLegacyTypeToTransport.get(type, notFound); + Preconditions.checkArgument(transport != notFound, "unknown legacy type: " + type); + nc.addTransportType(transport); + + if (type == TYPE_MOBILE_DUN) { + nc.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); + // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES. + nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + } else { + nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + } + return nc; + } } diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt index 64fdebd92726..921fbed373b0 100644 --- a/packages/Tethering/tests/unit/jarjar-rules.txt +++ b/packages/Tethering/tests/unit/jarjar-rules.txt @@ -7,5 +7,6 @@ rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering. rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1 +rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1 rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index fd2f708aea30..65a0ac13a84b 100644 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -16,13 +16,14 @@ package android.net.ip; -import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; -import static android.net.ConnectivityManager.TETHERING_USB; -import static android.net.ConnectivityManager.TETHERING_WIFI; -import static android.net.ConnectivityManager.TETHERING_WIFI_P2P; -import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; +import static android.net.INetd.IF_STATE_UP; +import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; +import static android.net.TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; import static android.net.ip.IpServer.STATE_AVAILABLE; import static android.net.ip.IpServer.STATE_LOCAL_ONLY; @@ -52,7 +53,7 @@ import static org.mockito.Mockito.when; import android.net.INetd; import android.net.INetworkStatsService; -import android.net.InterfaceConfiguration; +import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -64,7 +65,6 @@ import android.net.dhcp.IDhcpServerCallbacks; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; -import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.test.TestLooper; import android.text.TextUtils; @@ -89,6 +89,8 @@ public class IpServerTest { private static final String IFACE_NAME = "testnet1"; private static final String UPSTREAM_IFACE = "upstream0"; private static final String UPSTREAM_IFACE2 = "upstream1"; + private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; + private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int DHCP_LEASE_TIME_SECS = 3600; private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( @@ -96,11 +98,9 @@ public class IpServerTest { private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; - @Mock private INetworkManagementService mNMService; @Mock private INetd mNetd; @Mock private INetworkStatsService mStatsService; @Mock private IpServer.Callback mCallback; - @Mock private InterfaceConfiguration mInterfaceConfiguration; @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @Mock private RouterAdvertisementDaemon mRaDaemon; @@ -112,6 +112,7 @@ public class IpServerTest { private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor = ArgumentCaptor.forClass(LinkProperties.class); private IpServer mIpServer; + private InterfaceConfigurationParcel mInterfaceConfiguration; private void initStateMachine(int interfaceType) throws Exception { initStateMachine(interfaceType, false /* usingLegacyDhcp */); @@ -131,17 +132,20 @@ public class IpServerTest { }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); - when(mDependencies.getNetdService()).thenReturn(mNetd); - + mInterfaceConfiguration = new InterfaceConfigurationParcel(); + mInterfaceConfiguration.flags = new String[0]; + if (interfaceType == TETHERING_BLUETOOTH) { + mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR; + mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; + } mIpServer = new IpServer( - IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, - mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies); + IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService, + mCallback, usingLegacyDhcp, mDependencies); mIpServer.start(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); - reset(mNMService, mStatsService, mCallback); - when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); + reset(mNetd, mStatsService, mCallback); when(mRaDaemon.start()).thenReturn(true); } @@ -158,8 +162,7 @@ public class IpServerTest { if (upstreamIface != null) { dispatchTetherConnectionChanged(upstreamIface); } - reset(mNMService, mStatsService, mCallback); - when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); + reset(mNetd, mStatsService, mCallback); } @Before public void setUp() throws Exception { @@ -169,15 +172,14 @@ public class IpServerTest { @Test public void startsOutAvailable() { - mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), - TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback, - false /* usingLegacyDhcp */, mDependencies); + mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, + mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies); mIpServer.start(); mLooper.dispatchAll(); verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mCallback, mNMService, mStatsService); + verifyNoMoreInteractions(mCallback, mNetd, mStatsService); } @Test @@ -196,7 +198,7 @@ public class IpServerTest { // None of these commands should trigger us to request action from // the rest of the system. dispatchCommand(command); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } } @@ -208,7 +210,7 @@ public class IpServerTest { verify(mCallback).updateInterfaceState( mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -216,13 +218,17 @@ public class IpServerTest { initStateMachine(TETHERING_BLUETOOTH); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNMService); - inOrder.verify(mNMService).tetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mCallback, mNetd); + inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); + // One for ipv4 route, one for ipv6 link local route. + inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), + any(), any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -230,14 +236,16 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, null); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback); - inOrder.verify(mNMService).untetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); + inOrder.verify(mNetd).tetherApplyDnsInterfaces(); + inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); + inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -245,16 +253,19 @@ public class IpServerTest { initStateMachine(TETHERING_USB); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNMService); - inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); - inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); - inOrder.verify(mNMService).tetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mCallback, mNetd); + inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> + IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); + inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); + inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), + any(), any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -262,16 +273,19 @@ public class IpServerTest { initStateMachine(TETHERING_WIFI_P2P); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - InOrder inOrder = inOrder(mCallback, mNMService); - inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); - inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); - inOrder.verify(mNMService).tetherInterface(IFACE_NAME); + InOrder inOrder = inOrder(mCallback, mNetd); + inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> + IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); + inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); + inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), + any(), any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -281,10 +295,10 @@ public class IpServerTest { // Telling the state machine about its upstream interface triggers // a little more configuration. dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder inOrder = inOrder(mNMService); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + InOrder inOrder = inOrder(mNetd); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -292,49 +306,49 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNMService, mStatsService); + InOrder inOrder = inOrder(mNetd, mStatsService); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test public void handlesChangingUpstreamNatFailure() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - doThrow(RemoteException.class).when(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); + doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNMService, mStatsService); + InOrder inOrder = inOrder(mNetd, mStatsService); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @Test public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - doThrow(RemoteException.class).when(mNMService).startInterfaceForwarding( + doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward( IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNMService, mStatsService); + InOrder inOrder = inOrder(mNetd, mStatsService); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @Test @@ -342,17 +356,19 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback); + InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); inOrder.verify(mStatsService).forceUpdate(); - inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); - inOrder.verify(mNMService).untetherInterface(IFACE_NAME); + inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNetd).tetherApplyDnsInterfaces(); + inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); + inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } @Test @@ -361,13 +377,14 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_USB, null); if (shouldThrow) { - doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME); + doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME); } dispatchCommand(IpServer.CMD_INTERFACE_DOWN); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); - usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); - usbTeardownOrder.verify(mNMService).setInterfaceConfig( - IFACE_NAME, mInterfaceConfiguration); + InOrder usbTeardownOrder = inOrder(mNetd, mCallback); + // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down + // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address. + usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); usbTeardownOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); usbTeardownOrder.verify(mCallback).updateLinkProperties( @@ -380,12 +397,15 @@ public class IpServerTest { public void usbShouldBeTornDownOnTetherError() throws Exception { initStateMachine(TETHERING_USB); - doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME); + doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); - usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); - usbTeardownOrder.verify(mNMService).setInterfaceConfig( - IFACE_NAME, mInterfaceConfiguration); + InOrder usbTeardownOrder = inOrder(mNetd, mCallback); + usbTeardownOrder.verify(mNetd).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); + usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); + + usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); usbTeardownOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR); usbTeardownOrder.verify(mCallback).updateLinkProperties( @@ -397,11 +417,13 @@ public class IpServerTest { public void shouldTearDownUsbOnUpstreamError() throws Exception { initTetheredStateMachine(TETHERING_USB, null); - doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString()); + doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString()); dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); - usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); - usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); + InOrder usbTeardownOrder = inOrder(mNetd, mCallback); + usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); + + usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg( + argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); usbTeardownOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); usbTeardownOrder.verify(mCallback).updateLinkProperties( @@ -413,11 +435,11 @@ public class IpServerTest { public void ignoresDuplicateUpstreamNotifications() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); for (int i = 0; i < 5; i++) { dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mStatsService, mCallback); } } @@ -525,4 +547,12 @@ public class IpServerTest { // never see an empty interface name in any LinkProperties update. assertFalse(TextUtils.isEmpty(lp.getInterfaceName())); } + + private boolean assertContainsFlag(String[] flags, String match) { + for (String flag : flags) { + if (flag.equals(match)) return true; + } + fail("Missing flag: " + match); + return false; + } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 99cf9e90d912..66eba9ae3b7a 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -16,12 +16,12 @@ package com.android.server.connectivity.tethering; -import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; -import static android.net.ConnectivityManager.TETHERING_USB; -import static android.net.ConnectivityManager.TETHERING_WIFI; -import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED; +import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; +import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 04b2eb411c9d..7af48a89d87c 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -19,16 +19,15 @@ package com.android.server.connectivity.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; -import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; -import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY; -import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER; -import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER; -import static android.net.ConnectivityManager.TETHERING_USB; -import static android.net.ConnectivityManager.TETHERING_WIFI; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; -import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.RouteInfo.RTN_UNICAST; +import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; +import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; +import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; +import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; +import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; @@ -50,7 +49,6 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -74,14 +72,13 @@ import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.ITetheringEventCallback; import android.net.InetAddresses; -import android.net.InterfaceConfiguration; +import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.TetherStatesParcel; @@ -147,6 +144,7 @@ public class TetheringTest { private static final String TEST_USB_IFNAME = "test_rndis0"; private static final String TEST_WLAN_IFNAME = "test_wlan0"; private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; + private static final String TETHERING_NAME = "Tethering"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; @@ -185,6 +183,7 @@ public class TetheringTest { private BroadcastReceiver mBroadcastReceiver; private Tethering mTethering; private PhoneStateListener mPhoneStateListener; + private InterfaceConfigurationParcel mInterfaceConfiguration; private class TestContext extends BroadcastInterceptingContext { TestContext(Context base) { @@ -248,11 +247,6 @@ public class TetheringTest { } @Override - public INetd getNetdService() { - return mNetd; - } - - @Override public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb) { new Thread(() -> { @@ -429,11 +423,11 @@ public class TetheringTest { .thenReturn(new int[0]); when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) .thenReturn(false); - when(mNMService.listInterfaces()) + when(mNetd.interfaceGetList()) .thenReturn(new String[] { TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); - when(mNMService.getInterfaceConfig(anyString())) - .thenReturn(new InterfaceConfiguration()); + mInterfaceConfiguration = new InterfaceConfigurationParcel(); + mInterfaceConfiguration.flags = new String[0]; when(mRouterAdvertisementDaemon.start()) .thenReturn(true); @@ -495,15 +489,12 @@ public class TetheringTest { p2pInfo.groupFormed = isGroupFormed; p2pInfo.isGroupOwner = isGroupOwner; - NetworkInfo networkInfo = new NetworkInfo(TYPE_WIFI_P2P, 0, null, null); - WifiP2pGroup group = new WifiP2pGroup(); group.setIsGroupOwner(isGroupOwner); group.setInterface(ifname); final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo); - intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, networkInfo); intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group); mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL, P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); @@ -523,10 +514,11 @@ public class TetheringTest { } private void verifyInterfaceServingModeStarted(String ifname) throws Exception { - verify(mNMService, times(1)).getInterfaceConfig(ifname); - verify(mNMService, times(1)) - .setInterfaceConfig(eq(ifname), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).tetherInterface(ifname); + verify(mNetd, times(1)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherInterfaceAdd(ifname); + verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, ifname); + verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname), + anyString(), anyString()); } private void verifyTetheringBroadcast(String ifname, String whichExtra) { @@ -558,7 +550,7 @@ public class TetheringTest { verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); } - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); } @@ -581,14 +573,14 @@ public class TetheringTest { prepareUsbTethering(upstreamState); // This should produce no activity of any kind. - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Pretend we then receive USB configured broadcast. sendUsbBroadcast(true, true, true); mLooper.dispatchAll(); // Now we should see the start of tethering mechanics (in this case: // tetherMatchingInterfaces() which starts by fetching all interfaces). - verify(mNMService, times(1)).listInterfaces(); + verify(mNetd, times(1)).interfaceGetList(); // UpstreamNetworkMonitor should receive selected upstream verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); @@ -618,9 +610,9 @@ public class TetheringTest { verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNMService, times(1)).setIpForwardingEnabled(true); - verify(mNMService, times(1)).startTethering(any(String[].class)); - verifyNoMoreInteractions(mNMService); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, times(1)).tetherStartWithConfiguration(any()); + verifyNoMoreInteractions(mNetd); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); verify(mWifiManager).updateInterfaceIpState( @@ -638,16 +630,16 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_WLAN_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); - // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. - verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); - verify(mNMService, times(2)) - .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).stopTethering(); - verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); + // interfaceSetCfg() called once for enabling and twice disabling IPv4. + verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherStop(); + verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); verify(mWifiManager, times(3)).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. @@ -684,8 +676,8 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); @@ -708,8 +700,8 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); @@ -721,8 +713,8 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mRouterAdvertisementDaemon, times(1)).start(); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); @@ -736,12 +728,11 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, - TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); @@ -754,9 +745,9 @@ public class TetheringTest { UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState(); runUsbTethering(upstreamState); - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); // Then 464xlat comes up upstreamState = buildMobile464xlatUpstreamState(); @@ -772,12 +763,11 @@ public class TetheringTest { mLooper.dispatchAll(); // Forwarding is added for 464xlat - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, - TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); // Forwarding was not re-added for v6 (still times(1)) - verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); - verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); // DHCP not restarted on downstream (still times(1)) verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); } @@ -820,7 +810,7 @@ public class TetheringTest { mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that @@ -833,7 +823,7 @@ public class TetheringTest { verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); } @@ -847,7 +837,7 @@ public class TetheringTest { mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that @@ -858,9 +848,11 @@ public class TetheringTest { verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNMService, times(1)).setIpForwardingEnabled(true); - verify(mNMService, times(1)).startTethering(any(String[].class)); - verifyNoMoreInteractions(mNMService); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, times(1)).tetherStartWithConfiguration(any()); + verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), + anyString(), anyString()); + verifyNoMoreInteractions(mNetd); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); verify(mWifiManager).updateInterfaceIpState( @@ -878,8 +870,8 @@ public class TetheringTest { ///// // We do not currently emulate any upstream being found. // - // This is why there are no calls to verify mNMService.enableNat() or - // mNMService.startInterfaceForwarding(). + // This is why there are no calls to verify mNetd.tetherAddForward() or + // mNetd.ipfwdAddInterfaceForward(). ///// // Emulate pressing the WiFi tethering button. @@ -887,7 +879,7 @@ public class TetheringTest { mLooper.dispatchAll(); verify(mWifiManager, times(1)).stopSoftAp(); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, when tethering mode // is being torn down. @@ -895,16 +887,16 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_WLAN_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); - // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. - verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME); - verify(mNMService, atLeastOnce()) - .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).stopTethering(); - verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); + // interfaceSetCfg() called once for enabling and twice for disabling IPv4. + verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherStop(); + verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); verify(mWifiManager, times(3)).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); verifyNoMoreInteractions(mWifiManager); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. @@ -915,14 +907,14 @@ public class TetheringTest { @Test public void failureEnablingIpForwarding() throws Exception { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); - doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); + doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); // Emulate pressing the WiFi tethering button. mTethering.startTethering(TETHERING_WIFI, null, false); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that @@ -931,15 +923,15 @@ public class TetheringTest { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); - // We verify get/set called thrice here: twice for setup (on NMService) and once during - // teardown (on Netd) because all events happen over the course of the single + // We verify get/set called three times here: twice for setup and once during + // teardown because all events happen over the course of the single // dispatchAll() above. Note that once the IpServer IPv4 address config // code is refactored the two calls during shutdown will revert to one. - verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); - verify(mNMService, times(2)) - .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); - verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); - verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME); + verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName))); + verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); + verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME), + anyString(), anyString()); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED); verify(mWifiManager).updateInterfaceIpState( @@ -949,18 +941,20 @@ public class TetheringTest { assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); // This is called, but will throw. - verify(mNMService, times(1)).setIpForwardingEnabled(true); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); // This never gets called because of the exception thrown above. - verify(mNMService, times(0)).startTethering(any(String[].class)); + verify(mNetd, times(0)).tetherStartWithConfiguration(any()); // When the master state machine transitions to an error state it tells // downstream interfaces, which causes us to tell Wi-Fi about the error // so it can take down AP mode. - verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME); verify(mWifiManager).updateInterfaceIpState( TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); verifyNoMoreInteractions(mWifiManager); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); } private void runUserRestrictionsChange( @@ -1232,9 +1226,9 @@ public class TetheringTest { verifyInterfaceServingModeStarted(TEST_P2P_IFNAME); verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER); - verify(mNMService, times(1)).setIpForwardingEnabled(true); - verify(mNMService, times(1)).startTethering(any(String[].class)); - verifyNoMoreInteractions(mNMService); + verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, times(1)).tetherStartWithConfiguration(any()); + verifyNoMoreInteractions(mNetd); verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY); verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks(); // This will be called twice, one is on entering IpServer.STATE_AVAILABLE, @@ -1249,16 +1243,16 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(TEST_P2P_IFNAME); - // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4. - verify(mNMService, times(2)).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, times(2)) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).stopTethering(); - verify(mNMService, times(1)).setIpForwardingEnabled(false); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + verify(mNetd, times(1)).tetherInterfaceRemove(TEST_P2P_IFNAME); + verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + // interfaceSetCfg() called once for enabling and twice for disabling IPv4. + verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, times(1)).tetherStop(); + verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME); verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream(); verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any()); - verifyNoMoreInteractions(mNMService); + verifyNoMoreInteractions(mNetd); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); @@ -1272,12 +1266,11 @@ public class TetheringTest { sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, never()) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME); - verify(mNMService, never()).setIpForwardingEnabled(true); - verify(mNMService, never()).startTethering(any(String[].class)); + verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); + verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, never()).tetherStartWithConfiguration(any()); // Emulate externally-visible WifiP2pManager effects, when wifi p2p group // is being removed. @@ -1285,13 +1278,13 @@ public class TetheringTest { mTethering.interfaceRemoved(TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, never()).untetherInterface(TEST_P2P_IFNAME); - verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, never()) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, never()).stopTethering(); - verify(mNMService, never()).setIpForwardingEnabled(false); - verifyNoMoreInteractions(mNMService); + verify(mNetd, never()).tetherApplyDnsInterfaces(); + verify(mNetd, never()).tetherInterfaceRemove(TEST_P2P_IFNAME); + verify(mNetd, never()).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, never()).tetherStop(); + verify(mNetd, never()).ipfwdDisableForwarding(TETHERING_NAME); + verifyNoMoreInteractions(mNetd); // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); @@ -1321,12 +1314,11 @@ public class TetheringTest { sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); mLooper.dispatchAll(); - verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME); - verify(mNMService, never()) - .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class)); - verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME); - verify(mNMService, never()).setIpForwardingEnabled(true); - verify(mNMService, never()).startTethering(any(String[].class)); + verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); + verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); + verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME); + verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME); + verify(mNetd, never()).tetherStartWithConfiguration(any()); assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME)); } @Test diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index c90abbbedb5f..5ed75bf26f8b 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -18,13 +18,14 @@ package com.android.server.connectivity.tethering; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; -import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static com.android.server.connectivity.tethering.UpstreamNetworkMonitor.TYPE_NONE; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -538,13 +539,15 @@ public class UpstreamNetworkMonitorTest { mUNM.selectPreferredUpstreamType(preferredTypes)); verify(mEntitleMgr, times(1)).maybeRunProvisioning(); } + private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) { if (legacyType == TYPE_NONE) { assertTrue(ns == null); return; } - final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType); + final NetworkCapabilities nc = + UpstreamNetworkMonitor.networkCapabilitiesForType(legacyType); assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities)); } diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java index b7e05d9c984c..7b5514b8a0d1 100644 --- a/rs/java/android/renderscript/BaseObj.java +++ b/rs/java/android/renderscript/BaseObj.java @@ -16,8 +16,10 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import dalvik.system.CloseGuard; + import java.util.concurrent.locks.ReentrantReadWriteLock; /** diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java index b8eb3a1d7a40..0941907d35f8 100644 --- a/rs/java/android/renderscript/Element.java +++ b/rs/java/android/renderscript/Element.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * <p>An Element represents one item within an {@link diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java index 9a6b0bcd4544..7cc2825ae565 100644 --- a/rs/java/android/renderscript/FileA3D.java +++ b/rs/java/android/renderscript/FileA3D.java @@ -16,13 +16,13 @@ package android.renderscript; -import java.io.File; -import java.io.InputStream; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.content.res.Resources; +import java.io.File; +import java.io.InputStream; + /** * @hide * @deprecated in API 16 diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java index 583350e91795..df9d8019f28d 100644 --- a/rs/java/android/renderscript/Font.java +++ b/rs/java/android/renderscript/Font.java @@ -16,17 +16,16 @@ package android.renderscript; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.os.Environment; + import java.io.File; import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import android.os.Environment; - -import android.annotation.UnsupportedAppUsage; -import android.content.res.AssetManager; -import android.content.res.Resources; - /** * @hide * @deprecated in API 16 diff --git a/rs/java/android/renderscript/Matrix4f.java b/rs/java/android/renderscript/Matrix4f.java index 026c9fbd7d5e..a9469c979494 100644 --- a/rs/java/android/renderscript/Matrix4f.java +++ b/rs/java/android/renderscript/Matrix4f.java @@ -16,8 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; -import java.lang.Math; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java index 5321dcb957dc..826225a70d86 100644 --- a/rs/java/android/renderscript/Mesh.java +++ b/rs/java/android/renderscript/Mesh.java @@ -16,7 +16,8 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.util.Vector; /** diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java index e28d646f5f1c..ff072183e927 100644 --- a/rs/java/android/renderscript/Program.java +++ b/rs/java/android/renderscript/Program.java @@ -17,14 +17,14 @@ package android.renderscript; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.res.Resources; +import android.util.Log; + import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -import android.annotation.UnsupportedAppUsage; -import android.content.res.Resources; -import android.util.Log; - /** * @hide diff --git a/rs/java/android/renderscript/ProgramFragment.java b/rs/java/android/renderscript/ProgramFragment.java index 3dde9b6d6400..880531207b4d 100644 --- a/rs/java/android/renderscript/ProgramFragment.java +++ b/rs/java/android/renderscript/ProgramFragment.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java index d05d41da8b6f..c741ce6e77ed 100644 --- a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java +++ b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramRaster.java b/rs/java/android/renderscript/ProgramRaster.java index 33000acb4eb0..a21696c82161 100644 --- a/rs/java/android/renderscript/ProgramRaster.java +++ b/rs/java/android/renderscript/ProgramRaster.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramStore.java b/rs/java/android/renderscript/ProgramStore.java index 622fe21be47a..7e61347ee218 100644 --- a/rs/java/android/renderscript/ProgramStore.java +++ b/rs/java/android/renderscript/ProgramStore.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramVertex.java b/rs/java/android/renderscript/ProgramVertex.java index 83d9ea7be645..9257234de42c 100644 --- a/rs/java/android/renderscript/ProgramVertex.java +++ b/rs/java/android/renderscript/ProgramVertex.java @@ -38,7 +38,7 @@ **/ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java index 579d3bb507e8..03c2eaf91242 100644 --- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java +++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/RSSurfaceView.java b/rs/java/android/renderscript/RSSurfaceView.java index 561373cef625..6bdde387b334 100644 --- a/rs/java/android/renderscript/RSSurfaceView.java +++ b/rs/java/android/renderscript/RSSurfaceView.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.util.AttributeSet; import android.view.SurfaceHolder; diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index f4c27771c846..39efe731ce8a 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Bitmap; @@ -447,44 +447,33 @@ public class RenderScript { validate(); return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer); } - native long rsnAllocationCreateFromBitmap(long con, long type, int mip, long bitmapHandle, + + native long rsnAllocationCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage); synchronized long nAllocationCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) { validate(); - return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(), usage); + return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp, usage); } - native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, long bitmapHandle, + native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, Bitmap bmp, int usage); synchronized long nAllocationCreateBitmapBackedAllocation(long type, int mip, Bitmap bmp, int usage) { validate(); - return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp.getNativeInstance(), - usage); + return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp, usage); } - native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, long bitmapHandle, + native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage); synchronized long nAllocationCubeCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) { validate(); - return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(), - usage); - } - native long rsnAllocationCreateBitmapRef(long con, long type, long bitmapHandle); - synchronized long nAllocationCreateBitmapRef(long type, Bitmap bmp) { - validate(); - return rsnAllocationCreateBitmapRef(mContext, type, bmp.getNativeInstance()); - } - native long rsnAllocationCreateFromAssetStream(long con, int mips, int assetStream, int usage); - synchronized long nAllocationCreateFromAssetStream(int mips, int assetStream, int usage) { - validate(); - return rsnAllocationCreateFromAssetStream(mContext, mips, assetStream, usage); + return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp, usage); } - native void rsnAllocationCopyToBitmap(long con, long alloc, long bitmapHandle); + native void rsnAllocationCopyToBitmap(long con, long alloc, Bitmap bmp); synchronized void nAllocationCopyToBitmap(long alloc, Bitmap bmp) { validate(); - rsnAllocationCopyToBitmap(mContext, alloc, bmp.getNativeInstance()); + rsnAllocationCopyToBitmap(mContext, alloc, bmp); } native void rsnAllocationSyncAll(long con, long alloc, int src); @@ -537,10 +526,10 @@ public class RenderScript { validate(); rsnAllocationGenerateMipmaps(mContext, alloc); } - native void rsnAllocationCopyFromBitmap(long con, long alloc, long bitmapHandle); + native void rsnAllocationCopyFromBitmap(long con, long alloc, Bitmap bmp); synchronized void nAllocationCopyFromBitmap(long alloc, Bitmap bmp) { validate(); - rsnAllocationCopyFromBitmap(mContext, alloc, bmp.getNativeInstance()); + rsnAllocationCopyFromBitmap(mContext, alloc, bmp); } diff --git a/rs/java/android/renderscript/RenderScriptCacheDir.java b/rs/java/android/renderscript/RenderScriptCacheDir.java index 1797bef4be8d..862d032d6987 100644 --- a/rs/java/android/renderscript/RenderScriptCacheDir.java +++ b/rs/java/android/renderscript/RenderScriptCacheDir.java @@ -16,7 +16,8 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.io.File; /** diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java index 6fac83e8c4a8..dafaf367364d 100644 --- a/rs/java/android/renderscript/RenderScriptGL.java +++ b/rs/java/android/renderscript/RenderScriptGL.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.SurfaceTexture; import android.view.Surface; diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java index 9ad9aea9d7aa..d1d3a7642382 100644 --- a/rs/java/android/renderscript/Script.java +++ b/rs/java/android/renderscript/Script.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.util.SparseArray; /** diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index 0854b9582187..f9ef0b7b8e9d 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -12,7 +12,6 @@ LOCAL_SHARED_LIBRARIES := \ libRS \ libcutils \ liblog \ - libhwui \ libutils \ libui \ libgui \ diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index dfee96182a48..5ae895dbbce6 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -32,10 +32,10 @@ #include "jni.h" #include <nativehelper/JNIHelp.h> +#include <android/graphics/bitmap.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "android_runtime/android_util_AssetManager.h" -#include "android/graphics/GraphicsJNI.h" #include "android/native_window.h" #include "android/native_window_jni.h" @@ -1319,27 +1319,28 @@ nAllocationGenerateMipmaps(JNIEnv *_env, jobject _this, jlong con, jlong alloc) rsAllocationGenerateMipmaps((RsContext)con, (RsAllocation)alloc); } +static size_t computeByteSize(const android::graphics::Bitmap& bitmap) { + AndroidBitmapInfo info = bitmap.getInfo(); + return info.height * info.stride; +} + static jlong nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip, - jlong bitmapPtr, jint usage) + jobject jbitmap, jint usage) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.computeByteSize(), usage); + ptr, computeByteSize(bitmap), usage); return id; } static jlong nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, jlong type, - jint mip, jlong bitmapPtr, jint usage) + jint mip, jobject jbitmap, jint usage) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, @@ -1349,40 +1350,35 @@ nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, static jlong nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip, - jlong bitmapPtr, jint usage) + jobject jbitmap, jint usage) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.computeByteSize(), usage); + ptr, computeByteSize(bitmap), usage); return id; } static void -nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr) +nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - int w = bitmap.width(); - int h = bitmap.height(); + android::graphics::Bitmap bitmap(_env, jbitmap); + int w = bitmap.getInfo().width; + int h = bitmap.getInfo().height; const void* ptr = bitmap.getPixels(); rsAllocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0, 0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, - w, h, ptr, bitmap.computeByteSize(), 0); + w, h, ptr, computeByteSize(bitmap), 0); } static void -nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr) +nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); void* ptr = bitmap.getPixels(); - rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.computeByteSize()); + rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, computeByteSize(bitmap)); bitmap.notifyPixelsChanged(); } @@ -2867,12 +2863,12 @@ static const JNINativeMethod methods[] = { {"rsnTypeGetNativeData", "(JJ[J)V", (void*)nTypeGetNativeData }, {"rsnAllocationCreateTyped", "(JJIIJ)J", (void*)nAllocationCreateTyped }, -{"rsnAllocationCreateFromBitmap", "(JJIJI)J", (void*)nAllocationCreateFromBitmap }, -{"rsnAllocationCreateBitmapBackedAllocation", "(JJIJI)J", (void*)nAllocationCreateBitmapBackedAllocation }, -{"rsnAllocationCubeCreateFromBitmap","(JJIJI)J", (void*)nAllocationCubeCreateFromBitmap }, +{"rsnAllocationCreateFromBitmap", "(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCreateFromBitmap }, +{"rsnAllocationCreateBitmapBackedAllocation", "(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCreateBitmapBackedAllocation }, +{"rsnAllocationCubeCreateFromBitmap","(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCubeCreateFromBitmap }, -{"rsnAllocationCopyFromBitmap", "(JJJ)V", (void*)nAllocationCopyFromBitmap }, -{"rsnAllocationCopyToBitmap", "(JJJ)V", (void*)nAllocationCopyToBitmap }, +{"rsnAllocationCopyFromBitmap", "(JJLandroid/graphics/Bitmap;)V", (void*)nAllocationCopyFromBitmap }, +{"rsnAllocationCopyToBitmap", "(JJLandroid/graphics/Bitmap;)V", (void*)nAllocationCopyToBitmap }, {"rsnAllocationSyncAll", "(JJI)V", (void*)nAllocationSyncAll }, {"rsnAllocationSetupBufferQueue", "(JJI)V", (void*)nAllocationSetupBufferQueue }, diff --git a/services/Android.bp b/services/Android.bp index 1e119363f545..5afed6c4fd19 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -113,7 +113,7 @@ droidstubs { srcs: [":services-sources"], installable: false, // TODO: remove the --hide options below - args: " --show-single-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES,process=android.annotation.SystemApi.Process.SYSTEM_SERVER\\)" + + args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES,process=android.annotation.SystemApi.Process.SYSTEM_SERVER\\)" + " --hide-annotation android.annotation.Hide" + " --hide-package com.google.android.startop.iorap" + " --hide ReferencesHidden" + diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 6a6e2b2f3467..58d3489a8cc8 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -35,6 +35,7 @@ import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.AlertDialog; import android.app.PendingIntent; +import android.app.RemoteAction; import android.appwidget.AppWidgetManagerInternal; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -148,6 +149,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // their capabilities are ready. private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000; + static final String FUNCTION_REGISTER_SYSTEM_ACTION = "registerSystemAction"; + static final String FUNCTION_UNREGISTER_SYSTEM_ACTION = "unregisterSystemAction"; private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE = "registerUiTestAutomationService"; @@ -253,6 +256,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + @VisibleForTesting + AccessibilityManagerService( + Context context, + PackageManager packageManager, + AccessibilitySecurityPolicy securityPolicy, + SystemActionPerformer systemActionPerformer, + AccessibilityWindowManager a11yWindowManager, + AccessibilityDisplayListener a11yDisplayListener) { + mContext = context; + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); + mMainHandler = new MainHandler(mContext.getMainLooper()); + mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); + mPackageManager = packageManager; + mSecurityPolicy = securityPolicy; + mSystemActionPerformer = systemActionPerformer; + mA11yWindowManager = a11yWindowManager; + mA11yDisplayListener = a11yDisplayListener; + init(); + } + /** * Creates a new instance. * @@ -260,21 +284,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ public AccessibilityManagerService(Context context) { mContext = context; - mPackageManager = mContext.getPackageManager(); - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); - mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this); mMainHandler = new MainHandler(mContext.getMainLooper()); + mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); + mPackageManager = mContext.getPackageManager(); + mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this); mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService); mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler, mWindowManagerService, this, mSecurityPolicy, this); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); - mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager); - mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); + init(); + } + private void init() { + mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager); registerBroadcastReceivers(); new AccessibilityContentObserver(mMainHandler).register( - context.getContentResolver()); + mContext.getContentResolver()); } @Override @@ -623,6 +650,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub event.recycle(); } + /** + * This is the implementation of AccessibilityManager system API. + * System UI calls into this method through AccessibilityManager system API to register a + * system action. + */ + @Override + public void registerSystemAction(RemoteAction action, int actionId) { + mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY, + FUNCTION_REGISTER_SYSTEM_ACTION); + mSystemActionPerformer.registerSystemAction(actionId, action); + } + + /** + * This is the implementation of AccessibilityManager system API. + * System UI calls into this method through AccessibilityManager system API to unregister a + * system action. + */ + @Override + public void unregisterSystemAction(int actionId) { + mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY, + FUNCTION_UNREGISTER_SYSTEM_ACTION); + mSystemActionPerformer.unregisterSystemAction(actionId); + } + @Override public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) { synchronized (mLock) { diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java index 17549268503e..11dcfefd7e3b 100644 --- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java @@ -157,8 +157,13 @@ public class SystemActionPerformer { /** * This method is called to register a system action. If a system action is already registered * with the given id, the existing system action will be overwritten. + * + * This method is supposed to be package internal since this class is meant to be used by + * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public + * to be mocked. */ - void registerSystemAction(int id, RemoteAction action) { + @VisibleForTesting + public void registerSystemAction(int id, RemoteAction action) { synchronized (mSystemActionLock) { mRegisteredSystemActions.put(id, action); } @@ -170,8 +175,13 @@ public class SystemActionPerformer { /** * This method is called to unregister a system action previously registered through * registerSystemAction. + * + * This method is supposed to be package internal since this class is meant to be used by + * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public + * to be mocked. */ - void unregisterSystemAction(int id) { + @VisibleForTesting + public void unregisterSystemAction(int id) { synchronized (mSystemActionLock) { mRegisteredSystemActions.remove(id); } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 95cd8fc47944..f34b5e71ad7b 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -112,7 +112,6 @@ import com.android.server.autofill.ui.PendingUi; import com.android.server.inputmethod.InputMethodManagerInternal; import java.io.PrintWriter; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -311,20 +310,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull private final InputMethodManagerInternal mInputMethodManagerInternal; - @GuardedBy("mLock") - @Nullable - private CompletableFuture<InlineSuggestionsRequest> mSuggestionsRequestFuture; - - @GuardedBy("mLock") - @Nullable - private CompletableFuture<IInlineSuggestionsResponseCallback> - mInlineSuggestionsResponseCallbackFuture; - @Nullable private InlineSuggestionsRequestCallbackImpl mInlineSuggestionsRequestCallback; - private static final int INLINE_REQUEST_TIMEOUT_MS = 1000; - /** * Receiver of assist data from the app's {@link Activity}. */ @@ -336,7 +324,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly); return; } - if (mCurrentViewId == null) { + // Keeps to prevent it is cleared on multiple threads. + final AutofillId currentViewId = mCurrentViewId; + if (currentViewId == null) { Slog.w(TAG, "No current view id - session might have finished"); return; } @@ -410,7 +400,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mContexts == null) { mContexts = new ArrayList<>(1); } - mContexts.add(new FillContext(requestId, structure, mCurrentViewId)); + mContexts.add(new FillContext(requestId, structure, currentViewId)); cancelCurrentRequestLocked(); @@ -422,7 +412,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ false); - final InlineSuggestionsRequest suggestionsRequest = getInlineSuggestionsRequest(); + final InlineSuggestionsRequest suggestionsRequest = + mInlineSuggestionsRequestCallback != null + ? mInlineSuggestionsRequestCallback.getRequest() : null; request = new FillRequest(requestId, contexts, mClientState, flags, suggestionsRequest); @@ -620,13 +612,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState, int newState, int flags) { if (isInlineSuggestionsEnabled()) { - mSuggestionsRequestFuture = new CompletableFuture<>(); - mInlineSuggestionsResponseCallbackFuture = new CompletableFuture<>(); - - if (mInlineSuggestionsRequestCallback == null) { - mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl(this); - } - + mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl(); mInputMethodManagerInternal.onCreateInlineSuggestionsRequest( mComponentName, mCurrentViewId, mInlineSuggestionsRequestCallback); } @@ -636,10 +622,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private static final class InlineSuggestionsRequestCallbackImpl extends IInlineSuggestionsRequestCallback.Stub { - private final WeakReference<Session> mSession; + private static final int INLINE_REQUEST_TIMEOUT_MS = 1000; + + private final CompletableFuture<InlineSuggestionsRequest> mRequest; + private final CompletableFuture<IInlineSuggestionsResponseCallback> mResponseCallback; - private InlineSuggestionsRequestCallbackImpl(Session session) { - mSession = new WeakReference<>(session); + private InlineSuggestionsRequestCallbackImpl() { + mRequest = new CompletableFuture<>(); + mResponseCallback = new CompletableFuture<>(); } @Override @@ -648,13 +638,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Log.d(TAG, "inline suggestions request unsupported, " + "falling back to regular autofill"); } - final Session session = mSession.get(); - if (session != null) { - synchronized (session.mLock) { - session.mSuggestionsRequestFuture.cancel(true); - session.mInlineSuggestionsResponseCallbackFuture.cancel(true); - } - } + mRequest.cancel(true); + mResponseCallback.cancel(true); } @Override @@ -663,13 +648,36 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) { Log.d(TAG, "onInlineSuggestionsRequest() received: " + request); } - final Session session = mSession.get(); - if (session != null) { - synchronized (session.mLock) { - session.mSuggestionsRequestFuture.complete(request); - session.mInlineSuggestionsResponseCallbackFuture.complete(callback); - } + mRequest.complete(request); + mResponseCallback.complete(callback); + } + + @Nullable + private InlineSuggestionsRequest getRequest() { + try { + return mRequest.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + Log.w(TAG, "Exception getting inline suggestions request in time: " + e); + } catch (CancellationException e) { + Log.w(TAG, "Inline suggestions request cancelled"); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + return null; + } + + @Nullable + private IInlineSuggestionsResponseCallback getResponseCallback() { + try { + return mResponseCallback.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + Log.w(TAG, "Exception getting inline suggestions callback in time: " + e); + } catch (CancellationException e) { + Log.w(TAG, "Inline suggestions callback cancelled"); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); } + return null; } } @@ -2678,22 +2686,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Returns whether we made a request to show inline suggestions. */ private boolean requestShowInlineSuggestions(FillResponse response) { - IInlineSuggestionsResponseCallback inlineContentCallback = null; - synchronized (mLock) { - if (mInlineSuggestionsResponseCallbackFuture != null) { - try { - inlineContentCallback = mInlineSuggestionsResponseCallbackFuture.get( - INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - Log.w(TAG, "Exception getting inline suggestions callback in time: " + e); - } catch (CancellationException e) { - Log.w(TAG, "Inline suggestions callback cancelled"); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - } - + final IInlineSuggestionsResponseCallback inlineContentCallback = + mInlineSuggestionsRequestCallback != null + ? mInlineSuggestionsRequestCallback.getResponseCallback() : null; if (inlineContentCallback == null) { Log.w(TAG, "Session input method callback is not set yet"); return false; @@ -3015,10 +3010,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId); - final InlineSuggestionsRequest inlineSuggestionsRequest = getInlineSuggestionsRequest(); + final InlineSuggestionsRequest inlineSuggestionsRequest = + mInlineSuggestionsRequestCallback != null + ? mInlineSuggestionsRequestCallback.getRequest() : null; final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback = - getInlineSuggestionsResponseCallback(); - + mInlineSuggestionsRequestCallback != null + ? mInlineSuggestionsRequestCallback.getResponseCallback() : null; remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId, currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback); @@ -3028,40 +3025,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return mAugmentedAutofillDestroyer; } - @Nullable - private InlineSuggestionsRequest getInlineSuggestionsRequest() { - if (mSuggestionsRequestFuture != null) { - try { - return mSuggestionsRequestFuture.get( - INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - Log.w(TAG, "Exception getting inline suggestions request in time: " + e); - } catch (CancellationException e) { - Log.w(TAG, "Inline suggestions request cancelled"); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - return null; - } - - @Nullable - private IInlineSuggestionsResponseCallback getInlineSuggestionsResponseCallback() { - if (mInlineSuggestionsResponseCallbackFuture != null) { - try { - return mInlineSuggestionsResponseCallbackFuture.get( - INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - Log.w(TAG, "Exception getting inline suggestions callback in time: " + e); - } catch (CancellationException e) { - Log.w(TAG, "Inline suggestions callback cancelled"); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - return null; - } - @GuardedBy("mLock") private void cancelAugmentedAutofillLocked() { final RemoteAugmentedAutofillService remoteService = mService diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 3bce322a7655..d45a54e0ff28 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1452,6 +1452,11 @@ public class BackupManagerService extends IBackupManager.Stub { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) { return; } + dumpWithoutCheckingPermission(fd, pw, args); + } + + @VisibleForTesting + void dumpWithoutCheckingPermission(FileDescriptor fd, PrintWriter pw, String[] args) { int userId = binderGetCallingUserId(); if (!isUserReadyForBackup(userId)) { pw.println("Inactive"); @@ -1460,7 +1465,16 @@ public class BackupManagerService extends IBackupManager.Stub { if (args != null) { for (String arg : args) { - if ("users".equals(arg.toLowerCase())) { + if ("-h".equals(arg)) { + pw.println("'dumpsys backup' optional arguments:"); + pw.println(" -h : this help text"); + pw.println(" a[gents] : dump information about defined backup agents"); + pw.println(" transportclients : dump information about transport clients"); + pw.println(" transportstats : dump transport statts"); + pw.println(" users : dump the list of users for which backup service " + + "is running"); + return; + } else if ("users".equals(arg.toLowerCase())) { pw.print(DUMP_RUNNING_USERS_MESSAGE); for (int i = 0; i < mUserServices.size(); i++) { pw.print(" " + mUserServices.keyAt(i)); @@ -1471,11 +1485,12 @@ public class BackupManagerService extends IBackupManager.Stub { } } - UserBackupManagerService userBackupManagerService = - getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()"); - - if (userBackupManagerService != null) { - userBackupManagerService.dump(fd, pw, args); + for (int i = 0; i < mUserServices.size(); i++) { + UserBackupManagerService userBackupManagerService = + getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i), "dump()"); + if (userBackupManagerService != null) { + userBackupManagerService.dump(fd, pw, args); + } } } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 064cd060528d..7b95ab526b41 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -3545,14 +3545,7 @@ public class UserBackupManagerService { try { if (args != null) { for (String arg : args) { - if ("-h".equals(arg)) { - pw.println("'dumpsys backup' optional arguments:"); - pw.println(" -h : this help text"); - pw.println(" a[gents] : dump information about defined backup agents"); - pw.println(" users : dump the list of users for which backup service " - + "is running"); - return; - } else if ("agents".startsWith(arg)) { + if ("agents".startsWith(arg)) { dumpAgents(pw); return; } else if ("transportclients".equals(arg.toLowerCase())) { @@ -3583,8 +3576,10 @@ public class UserBackupManagerService { } private void dumpInternal(PrintWriter pw) { + // Add prefix for only non-system users so that system user dumpsys is the same as before + String userPrefix = mUserId == UserHandle.USER_SYSTEM ? "" : "User " + mUserId + ":"; synchronized (mQueueLock) { - pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") + pw.println(userPrefix + "Backup Manager is " + (mEnabled ? "enabled" : "disabled") + " / " + (!mSetupComplete ? "not " : "") + "setup complete / " + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); @@ -3594,13 +3589,13 @@ public class UserBackupManagerService { + " (now = " + System.currentTimeMillis() + ')'); pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId)); - pw.println("Transport whitelist:"); + pw.println(userPrefix + "Transport whitelist:"); for (ComponentName transport : mTransportManager.getTransportWhitelist()) { pw.print(" "); pw.println(transport.flattenToShortString()); } - pw.println("Available transports:"); + pw.println(userPrefix + "Available transports:"); final String[] transports = listAllTransports(); if (transports != null) { for (String t : transports) { @@ -3626,18 +3621,18 @@ public class UserBackupManagerService { mTransportManager.dumpTransportClients(pw); - pw.println("Pending init: " + mPendingInits.size()); + pw.println(userPrefix + "Pending init: " + mPendingInits.size()); for (String s : mPendingInits) { pw.println(" " + s); } - pw.print("Ancestral: "); + pw.print(userPrefix + "Ancestral: "); pw.println(Long.toHexString(mAncestralToken)); - pw.print("Current: "); + pw.print(userPrefix + "Current: "); pw.println(Long.toHexString(mCurrentToken)); int numPackages = mBackupParticipants.size(); - pw.println("Participants:"); + pw.println(userPrefix + "Participants:"); for (int i = 0; i < numPackages; i++) { int uid = mBackupParticipants.keyAt(i); pw.print(" uid: "); @@ -3648,7 +3643,7 @@ public class UserBackupManagerService { } } - pw.println("Ancestral packages: " + pw.println(userPrefix + "Ancestral packages: " + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); if (mAncestralPackages != null) { for (String pkg : mAncestralPackages) { @@ -3657,17 +3652,17 @@ public class UserBackupManagerService { } Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy(); - pw.println("Ever backed up: " + processedPackages.size()); + pw.println(userPrefix + "Ever backed up: " + processedPackages.size()); for (String pkg : processedPackages) { pw.println(" " + pkg); } - pw.println("Pending key/value backup: " + mPendingBackups.size()); + pw.println(userPrefix + "Pending key/value backup: " + mPendingBackups.size()); for (BackupRequest req : mPendingBackups.values()) { pw.println(" " + req); } - pw.println("Full backup queue:" + mFullBackupQueue.size()); + pw.println(userPrefix + "Full backup queue:" + mFullBackupQueue.size()); for (FullBackupEntry entry : mFullBackupQueue) { pw.print(" "); pw.print(entry.lastBackup); diff --git a/services/core/Android.bp b/services/core/Android.bp index a1f57cb51188..b2fba730fac1 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -165,9 +165,3 @@ prebuilt_etc { name: "protolog.conf.json.gz", src: ":services.core.json.gz", } - -platform_compat_config { - name: "services-core-platform-compat-config", - src: ":services.core.unboosted", -} - diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index bd8a3618733f..c2e32d332f50 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -92,6 +92,7 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkMisc; import android.net.NetworkMonitorManager; import android.net.NetworkPolicyManager; +import android.net.NetworkProvider; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkScore; @@ -219,6 +220,7 @@ import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicInteger; /** * @hide @@ -595,6 +597,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // sequence number of NetworkRequests private int mNextNetworkRequestId = 1; + // Sequence number for NetworkProvider IDs. + private final AtomicInteger mNextNetworkProviderId = new AtomicInteger( + NetworkProvider.FIRST_PROVIDER_ID); + // NetworkRequest activity String log entries. private static final int MAX_NETWORK_REQUEST_LOGS = 20; private final LocalLog mNetworkRequestInfoLogs = new LocalLog(MAX_NETWORK_REQUEST_LOGS); @@ -3029,25 +3035,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { if (VDBG) log("NetworkFactory connected"); // Finish setting up the full connection - mNetworkFactoryInfos.get(msg.replyTo).asyncChannel.sendMessage( - AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); - // A network factory has connected. Send it all current NetworkRequests. - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - if (nri.request.isListen()) continue; - ensureRunningOnConnectivityServiceThread(); - NetworkAgentInfo nai = nri.mSatisfier; - final int score; - final int serial; - if (nai != null) { - score = nai.getCurrentScore(); - serial = nai.factorySerialNumber; - } else { - score = 0; - serial = NetworkFactory.SerialNumber.NONE; - } - ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, serial, - nri.request); - } + NetworkFactoryInfo nfi = mNetworkFactoryInfos.get(msg.replyTo); + nfi.completeConnection(); + sendAllRequestsToFactory(nfi); } else { loge("Error connecting NetworkFactory"); mNetworkFactoryInfos.remove(msg.obj); @@ -3430,8 +3420,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, - nri.request); + nfi.cancelRequest(nri.request); } } else { // listens don't have a singular affectedNetwork. Check all networks to see @@ -4927,15 +4916,70 @@ public class ConnectivityService extends IConnectivityManager.Stub private static class NetworkFactoryInfo { public final String name; public final Messenger messenger; - public final AsyncChannel asyncChannel; + private final AsyncChannel mAsyncChannel; + private final IBinder.DeathRecipient mDeathRecipient; public final int factorySerialNumber; NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel, - int factorySerialNumber) { + int factorySerialNumber, IBinder.DeathRecipient deathRecipient) { this.name = name; this.messenger = messenger; - this.asyncChannel = asyncChannel; this.factorySerialNumber = factorySerialNumber; + mAsyncChannel = asyncChannel; + mDeathRecipient = deathRecipient; + + if ((mAsyncChannel == null) == (mDeathRecipient == null)) { + throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient"); + } + } + + boolean isLegacyNetworkFactory() { + return mAsyncChannel != null; + } + + void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) { + try { + messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj)); + } catch (RemoteException e) { + // Remote process died. Ignore; the death recipient will remove this + // NetworkFactoryInfo from mNetworkFactoryInfos. + } + } + + void requestNetwork(NetworkRequest request, int score, int servingSerialNumber) { + if (isLegacyNetworkFactory()) { + mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, + servingSerialNumber, request); + } else { + sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score, + servingSerialNumber, request); + } + } + + void cancelRequest(NetworkRequest request) { + if (isLegacyNetworkFactory()) { + mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request); + } else { + sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request); + } + } + + void connect(Context context, Handler handler) { + if (isLegacyNetworkFactory()) { + mAsyncChannel.connect(context, handler, messenger); + } else { + try { + messenger.getBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + mDeathRecipient.binderDied(); + } + } + } + + void completeConnection() { + if (isLegacyNetworkFactory()) { + mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + } } } @@ -5306,6 +5350,11 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); } + /** Returns the next Network provider ID. */ + public final int nextNetworkProviderId() { + return mNextNetworkProviderId.getAndIncrement(); + } + @Override public void releaseNetworkRequest(NetworkRequest networkRequest) { ensureNetworkRequestHasType(networkRequest); @@ -5317,23 +5366,51 @@ public class ConnectivityService extends IConnectivityManager.Stub public int registerNetworkFactory(Messenger messenger, String name) { enforceNetworkFactoryPermission(); NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel(), - NetworkFactory.SerialNumber.nextSerialNumber()); + nextNetworkProviderId(), null /* deathRecipient */); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi)); return nfi.factorySerialNumber; } private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) { + if (mNetworkFactoryInfos.containsKey(nfi.messenger)) { + // Avoid creating duplicates. even if an app makes a direct AIDL call. + // This will never happen if an app calls ConnectivityManager#registerNetworkProvider, + // as that will throw if a duplicate provider is registered. + Slog.e(TAG, "Attempt to register existing NetworkFactoryInfo " + + mNetworkFactoryInfos.get(nfi.messenger).name); + return; + } + if (DBG) log("Got NetworkFactory Messenger for " + nfi.name); mNetworkFactoryInfos.put(nfi.messenger, nfi); - nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger); + nfi.connect(mContext, mTrackerHandler); + if (!nfi.isLegacyNetworkFactory()) { + // Legacy NetworkFactories get their requests when their AsyncChannel connects. + sendAllRequestsToFactory(nfi); + } } @Override - public void unregisterNetworkFactory(Messenger messenger) { + public int registerNetworkProvider(Messenger messenger, String name) { + enforceNetworkFactoryPermission(); + NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, + null /* asyncChannel */, nextNetworkProviderId(), + () -> unregisterNetworkProvider(messenger)); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi)); + return nfi.factorySerialNumber; + } + + @Override + public void unregisterNetworkProvider(Messenger messenger) { enforceNetworkFactoryPermission(); mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger)); } + @Override + public void unregisterNetworkFactory(Messenger messenger) { + unregisterNetworkProvider(messenger); + } + private void handleUnregisterNetworkFactory(Messenger messenger) { NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger); if (nfi == null) { @@ -5343,6 +5420,12 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("unregisterNetworkFactory for " + nfi.name); } + @Override + public void declareNetworkRequestUnfulfillable(NetworkRequest request) { + enforceNetworkFactoryPermission(); + mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true)); + } + // NOTE: Accessed on multiple threads, must be synchronized on itself. @GuardedBy("mNetworkForNetId") private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>(); @@ -5961,8 +6044,26 @@ public class ConnectivityService extends IConnectivityManager.Stub log("sending new Min Network Score(" + score + "): " + networkRequest.toString()); } for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, - serial, networkRequest); + nfi.requestNetwork(networkRequest, score, serial); + } + } + + /** Sends all current NetworkRequests to the specified factory. */ + private void sendAllRequestsToFactory(NetworkFactoryInfo nfi) { + ensureRunningOnConnectivityServiceThread(); + for (NetworkRequestInfo nri : mNetworkRequests.values()) { + if (nri.request.isListen()) continue; + NetworkAgentInfo nai = nri.mSatisfier; + final int score; + final int serial; + if (nai != null) { + score = nai.getCurrentScore(); + serial = nai.factorySerialNumber; + } else { + score = 0; + serial = NetworkFactory.SerialNumber.NONE; + } + nfi.requestNetwork(nri.request, score, serial); } } diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java index bbcfdc63f3f1..32cdc41472c9 100644 --- a/services/core/java/com/android/server/GnssManagerService.java +++ b/services/core/java/com/android/server/GnssManagerService.java @@ -47,7 +47,6 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocationManagerServiceUtils.LinkedListener; import com.android.server.LocationManagerServiceUtils.LinkedListenerBase; -import com.android.server.location.AbstractLocationProvider; import com.android.server.location.CallerIdentity; import com.android.server.location.GnssBatchingProvider; import com.android.server.location.GnssCapabilitiesProvider; @@ -116,11 +115,9 @@ public class GnssManagerService { private final Handler mHandler; public GnssManagerService(LocationManagerService locationManagerService, - Context context, - AbstractLocationProvider.LocationProviderManager gnssProviderManager, - LocationUsageLogger locationUsageLogger) { - this(locationManagerService, context, new GnssLocationProvider(context, gnssProviderManager, - FgThread.getHandler().getLooper()), locationUsageLogger); + Context context, LocationUsageLogger locationUsageLogger) { + this(locationManagerService, context, + new GnssLocationProvider(context, FgThread.getHandler()), locationUsageLogger); } // Can use this constructor to inject GnssLocationProvider for testing diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index c5f1923b0b98..32128d5f26f8 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -23,8 +23,6 @@ import static android.location.LocationManager.NETWORK_PROVIDER; import static android.location.LocationManager.PASSIVE_PROVIDER; import static android.os.PowerManager.locationPowerSaveModeToString; -import static com.android.internal.util.Preconditions.checkState; - import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -74,7 +72,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.os.WorkSource.WorkChain; -import android.provider.Settings; import android.stats.location.LocationStatsEnums; import android.text.TextUtils; import android.util.EventLog; @@ -92,6 +89,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.location.AbstractLocationProvider; +import com.android.server.location.AbstractLocationProvider.State; import com.android.server.location.ActivityRecognitionProxy; import com.android.server.location.CallerIdentity; import com.android.server.location.GeocoderProxy; @@ -105,6 +103,7 @@ import com.android.server.location.LocationRequestStatistics.PackageStatistics; import com.android.server.location.LocationSettingsStore; import com.android.server.location.LocationUsageLogger; import com.android.server.location.MockProvider; +import com.android.server.location.MockableLocationProvider; import com.android.server.location.PassiveProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -121,6 +120,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; /** @@ -196,6 +197,8 @@ public class LocationManagerService extends ILocationManager.Stub { private final LocationSettingsStore mSettingsStore; private final LocationUsageLogger mLocationUsageLogger; + private final PassiveLocationProviderManager mPassiveManager; + private AppOpsManager mAppOps; private PackageManager mPackageManager; private PowerManager mPowerManager; @@ -205,21 +208,17 @@ public class LocationManagerService extends ILocationManager.Stub { private GeofenceManager mGeofenceManager; private LocationFudger mLocationFudger; private GeocoderProxy mGeocodeProvider; - @Nullable - private GnssManagerService mGnssManagerService; - private PassiveProvider mPassiveProvider; // track passive provider for special cases + @Nullable private GnssManagerService mGnssManagerService; + @GuardedBy("mLock") private String mExtraLocationControllerPackage; - private boolean mExtraLocationControllerPackageEnabled; - - // list of currently active providers @GuardedBy("mLock") - private final ArrayList<LocationProviderManager> mProviders = new ArrayList<>(); + private boolean mExtraLocationControllerPackageEnabled; - // list of non-mock providers, so that when mock providers replace real providers, they can be - // later re-replaced - @GuardedBy("mLock") - private final ArrayList<LocationProviderManager> mRealProviders = new ArrayList<>(); + // @GuardedBy("mLock") + // hold lock for write or to prevent write, no lock for read + private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = + new CopyOnWriteArrayList<>(); @GuardedBy("mLock") private final HashMap<Object, Receiver> mReceivers = new HashMap<>(); @@ -238,9 +237,9 @@ public class LocationManagerService extends ILocationManager.Stub { private final HashMap<String, Location> mLastLocationCoarseInterval = new HashMap<>(); - // current active user on the device - other users are denied location data - private int mCurrentUserId = UserHandle.USER_SYSTEM; - private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM}; + // current active user on the device + private int mCurrentUserId; + private int[] mCurrentUserProfiles; @GuardedBy("mLock") @PowerManager.LocationPowerSaveMode @@ -252,6 +251,17 @@ public class LocationManagerService extends ILocationManager.Stub { mSettingsStore = new LocationSettingsStore(mContext, mHandler); mLocationUsageLogger = new LocationUsageLogger(); + mCurrentUserId = UserHandle.USER_NULL; + mCurrentUserProfiles = new int[]{UserHandle.USER_NULL}; + + // set up passive provider - we do this early because it has no dependencies on system + // services or external code that isn't ready yet, and because this allows the variable to + // be final. other more complex providers are initialized later, when system services are + // ready + mPassiveManager = new PassiveLocationProviderManager(); + mProviderManagers.add(mPassiveManager); + mPassiveManager.setRealProvider(new PassiveProvider(mContext)); + // Let the package manager query which are the default location // providers as they get certain permissions granted by default. PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( @@ -415,15 +425,15 @@ public class LocationManagerService extends ILocationManager.Stub { for (Receiver receiver : mReceivers.values()) { receiver.updateMonitoring(true); } - for (LocationProviderManager p : mProviders) { - applyRequirementsLocked(p); + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); } } @GuardedBy("mLock") private void onPermissionsChangedLocked() { - for (LocationProviderManager p : mProviders) { - applyRequirementsLocked(p); + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); } } @@ -442,16 +452,16 @@ public class LocationManagerService extends ILocationManager.Stub { mBatterySaverMode = newLocationMode; - for (LocationProviderManager p : mProviders) { - applyRequirementsLocked(p); + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); } } @GuardedBy("mLock") private void onScreenStateChangedLocked() { if (mBatterySaverMode == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) { - for (LocationProviderManager p : mProviders) { - applyRequirementsLocked(p); + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); } } } @@ -466,8 +476,8 @@ public class LocationManagerService extends ILocationManager.Stub { intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId)); mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); - for (LocationProviderManager p : mProviders) { - p.onUseableChangedLocked(userId); + for (LocationProviderManager manager : mProviderManagers) { + manager.onUseableChangedLocked(userId); } } @@ -521,22 +531,22 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private void onBackgroundThrottleIntervalChangedLocked() { - for (LocationProviderManager provider : mProviders) { - applyRequirementsLocked(provider); + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); } } @GuardedBy("mLock") private void onBackgroundThrottleWhitelistChangedLocked() { - for (LocationProviderManager p : mProviders) { - applyRequirementsLocked(p); + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); } } @GuardedBy("lock") private void onIgnoreSettingsWhitelistChangedLocked() { - for (LocationProviderManager p : mProviders) { - applyRequirementsLocked(p); + for (LocationProviderManager manager : mProviderManagers) { + applyRequirementsLocked(manager); } } @@ -623,22 +633,11 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private void initializeProvidersLocked() { - // create a passive location provider, which is always enabled - LocationProviderManager passiveProviderManager = new LocationProviderManager( - PASSIVE_PROVIDER); - addProviderLocked(passiveProviderManager); - mPassiveProvider = new PassiveProvider(mContext, passiveProviderManager); - passiveProviderManager.attachLocked(mPassiveProvider); - if (GnssManagerService.isGnssSupported()) { - // Create a gps location provider manager - LocationProviderManager gnssProviderManager = new LocationProviderManager(GPS_PROVIDER); - mRealProviders.add(gnssProviderManager); - addProviderLocked(gnssProviderManager); - - mGnssManagerService = new GnssManagerService(this, mContext, gnssProviderManager, - mLocationUsageLogger); - gnssProviderManager.attachLocked(mGnssManagerService.getGnssLocationProvider()); + mGnssManagerService = new GnssManagerService(this, mContext, mLocationUsageLogger); + LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER); + mProviderManagers.add(gnssManager); + gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider()); } /* @@ -662,37 +661,31 @@ public class LocationManagerService extends ILocationManager.Stub { ensureFallbackFusedProviderPresentLocked(pkgs); - // bind to network provider - LocationProviderManager networkProviderManager = new LocationProviderManager( - NETWORK_PROVIDER); LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind( mContext, - networkProviderManager, NETWORK_LOCATION_SERVICE_ACTION, com.android.internal.R.bool.config_enableNetworkLocationOverlay, com.android.internal.R.string.config_networkLocationProviderPackageName, com.android.internal.R.array.config_locationProviderPackageNames); if (networkProvider != null) { - mRealProviders.add(networkProviderManager); - addProviderLocked(networkProviderManager); - networkProviderManager.attachLocked(networkProvider); + LocationProviderManager networkManager = new LocationProviderManager(NETWORK_PROVIDER); + mProviderManagers.add(networkManager); + networkManager.setRealProvider(networkProvider); } else { Slog.w(TAG, "no network location provider found"); } // bind to fused provider - LocationProviderManager fusedProviderManager = new LocationProviderManager(FUSED_PROVIDER); LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind( mContext, - fusedProviderManager, FUSED_LOCATION_SERVICE_ACTION, com.android.internal.R.bool.config_enableFusedLocationOverlay, com.android.internal.R.string.config_fusedLocationProviderPackageName, com.android.internal.R.array.config_locationProviderPackageNames); if (fusedProvider != null) { - mRealProviders.add(fusedProviderManager); - addProviderLocked(fusedProviderManager); - fusedProviderManager.attachLocked(fusedProvider); + LocationProviderManager fusedManager = new LocationProviderManager(FUSED_PROVIDER); + mProviderManagers.add(fusedManager); + fusedManager.setRealProvider(fusedProvider); } else { Slog.e(TAG, "no fused location provider found", new IllegalStateException("Location service needs a fused location provider")); @@ -754,10 +747,7 @@ public class LocationManagerService extends ILocationManager.Stub { Boolean.parseBoolean(fragments[7]) /* supportsBearing */, Integer.parseInt(fragments[8]) /* powerRequirement */, Integer.parseInt(fragments[9]) /* accuracy */); - LocationProviderManager testProviderManager = new LocationProviderManager(name); - addProviderLocked(testProviderManager); - testProviderManager.attachLocked( - new MockProvider(mContext, testProviderManager, properties)); + addTestProvider(name, properties, mContext.getOpPackageName()); } } @@ -771,231 +761,202 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "foreground user is changing to " + userId); } - int oldUserId = userId; + int oldUserId = mCurrentUserId; mCurrentUserId = userId; onUserProfilesChangedLocked(); // let providers know the current user has changed - for (LocationProviderManager p : mProviders) { - p.onUseableChangedLocked(oldUserId); - p.onUseableChangedLocked(mCurrentUserId); + for (LocationProviderManager manager : mProviderManagers) { + // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility + mSettingsStore.setLocationProviderAllowed(manager.getName(), + manager.isUseable(mCurrentUserId), mCurrentUserId); + + manager.onUseableChangedLocked(oldUserId); + manager.onUseableChangedLocked(mCurrentUserId); } } /** * Location provider manager, manages a LocationProvider. */ - class LocationProviderManager implements AbstractLocationProvider.LocationProviderManager { + class LocationProviderManager implements MockableLocationProvider.Listener { private final String mName; - // remember to clear binder identity before invoking any provider operation - @GuardedBy("mLock") - @Nullable - protected AbstractLocationProvider mProvider; + // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary + protected final MockableLocationProvider mProvider; @GuardedBy("mLock") - private SparseArray<Boolean> mUseable; // combined state for each user id - @GuardedBy("mLock") - private boolean mEnabled; // state of provider - - @GuardedBy("mLock") - @Nullable - private ProviderProperties mProperties; + private final SparseArray<Boolean> mUseable; // combined state for each user id private LocationProviderManager(String name) { mName = name; - - mProvider = null; mUseable = new SparseArray<>(1); - mEnabled = false; - mProperties = null; - - // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility - Settings.Secure.putStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "-" + mName, - mCurrentUserId); - } - - @GuardedBy("mLock") - public void attachLocked(AbstractLocationProvider provider) { - Objects.requireNonNull(provider); - checkState(mProvider == null); - if (D) { - Log.d(TAG, mName + " provider attached"); - } - - mProvider = provider; - - // it would be more correct to call this for all users, but we know this can only - // affect the current user since providers are disabled for non-current users - onUseableChangedLocked(mCurrentUserId); + // initialize last since this lets our reference escape + mProvider = new MockableLocationProvider(mContext, mLock, this); } public String getName() { return mName; } - @GuardedBy("mLock") - public List<String> getPackagesLocked() { - if (mProvider == null) { - return Collections.emptyList(); - } else { - // safe to not clear binder context since this doesn't call into the real provider - return mProvider.getProviderPackages(); - } + public boolean hasProvider() { + return mProvider.getProvider() != null; } - public boolean isMock() { - return false; + public void setRealProvider(AbstractLocationProvider provider) { + mProvider.setRealProvider(provider); } - @GuardedBy("mLock") - public boolean isPassiveLocked() { - return mProvider == mPassiveProvider; + public void setMockProvider(@Nullable MockProvider provider) { + mProvider.setMockProvider(provider); + } + + public Set<String> getPackages() { + return mProvider.getState().providerPackageNames; } - @GuardedBy("mLock") @Nullable - public ProviderProperties getPropertiesLocked() { - return mProperties; + public ProviderProperties getProperties() { + return mProvider.getState().properties; } - public void setRequest(ProviderRequest request, WorkSource workSource) { - // move calls going to providers onto a different thread to avoid deadlock - mHandler.post(() -> { - synchronized (mLock) { - if (mProvider != null) { - mProvider.onSetRequest(request, workSource); - } + public void setMockProviderEnabled(boolean enabled) { + synchronized (mLock) { + if (!mProvider.isMock()) { + throw new IllegalArgumentException(mName + " provider is not a test provider"); } - }); + + mProvider.setMockProviderEnabled(enabled); + } } - public void sendExtraCommand(String command, Bundle extras) { - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); + public void setMockProviderLocation(Location location) { + synchronized (mLock) { + if (!mProvider.isMock()) { + throw new IllegalArgumentException(mName + " provider is not a test provider"); + } - // move calls going to providers onto a different thread to avoid deadlock - mHandler.post(() -> { - synchronized (mLock) { - if (mProvider != null) { - mProvider.onSendExtraCommand(uid, pid, command, extras); - } + String locationProvider = location.getProvider(); + if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) { + // The location has an explicit provider that is different from the mock + // provider name. The caller may be trying to fool us via b/33091107. + EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), + mName + "!=" + locationProvider); } - }); - } - @GuardedBy("mLock") - public void dumpLocked(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - pw.print(mName + " provider"); - if (isMock()) { - pw.print(" [mock]"); + mProvider.setMockProviderLocation(location); } - pw.println(":"); + } - pw.increaseIndent(); + public List<LocationRequest> getMockProviderRequests() { + synchronized (mLock) { + if (!mProvider.isMock()) { + throw new IllegalArgumentException(mName + " provider is not a test provider"); + } - pw.println("useable=" + isUseableLocked(mCurrentUserId)); - if (!isUseableLocked(mCurrentUserId)) { - pw.println("attached=" + (mProvider != null)); - pw.println("enabled=" + mEnabled); + return mProvider.getCurrentRequest().locationRequests; } + } - pw.println("properties=" + mProperties); + public void setRequest(ProviderRequest request) { + mProvider.setRequest(request); + } - if (mProvider != null) { - // in order to be consistent with other provider APIs, this should be run on the - // location thread... but this likely isn't worth it just for dumping info. - long identity = Binder.clearCallingIdentity(); - try { - mProvider.dump(fd, pw, args); - } finally { - Binder.restoreCallingIdentity(identity); + public void sendExtraCommand(int uid, int pid, String command, Bundle extras) { + mProvider.sendExtraCommand(uid, pid, command, extras); + } + + public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { + synchronized (mLock) { + pw.print(mName + " provider"); + if (mProvider.isMock()) { + pw.print(" [mock]"); + } + pw.println(":"); + + pw.increaseIndent(); + + pw.println("useable=" + isUseable(mCurrentUserId)); + if (!isUseable(mCurrentUserId)) { + pw.println("enabled=" + mProvider.getState().enabled); } + + pw.println("properties=" + mProvider.getState().properties); } + mProvider.dump(fd, pw, args); + pw.decreaseIndent(); } + @GuardedBy("mLock") @Override public void onReportLocation(Location location) { - // likelihood of a 0,0 bug is far greater than this being a valid location - if (!isMock() && location.getLatitude() == 0 && location.getLongitude() == 0) { - Slog.w(TAG, "blocking 0,0 location from " + mName + " provider"); - return; + // don't validate mock locations + if (!location.isFromMockProvider()) { + if (location.getLatitude() == 0 && location.getLongitude() == 0) { + Slog.w(TAG, "blocking 0,0 location from " + mName + " provider"); + return; + } } - synchronized (mLock) { - handleLocationChangedLocked(location, this); - } + handleLocationChangedLocked(location, this); } + @GuardedBy("mLock") @Override public void onReportLocation(List<Location> locations) { if (mGnssManagerService == null) { return; } - synchronized (mLock) { - LocationProviderManager gpsProvider = getLocationProviderLocked(GPS_PROVIDER); - if (gpsProvider == null || !gpsProvider.isUseableLocked()) { - Slog.w(TAG, "reportLocationBatch() called without user permission"); - return; - } - mGnssManagerService.onReportLocation(locations); + if (!GPS_PROVIDER.equals(mName) || !isUseable()) { + Slog.w(TAG, "reportLocationBatch() called without user permission"); + return; } - } - @Override - public void onSetEnabled(boolean enabled) { - synchronized (mLock) { - if (enabled == mEnabled) { - return; - } - - if (D) { - Log.d(TAG, mName + " provider enabled is now " + mEnabled); - } - - mEnabled = enabled; - - // it would be more correct to call this for all users, but we know this can only - // affect the current user since providers are disabled for non-current users - onUseableChangedLocked(mCurrentUserId); - } + mGnssManagerService.onReportLocation(locations); } + @GuardedBy("mLock") @Override - public void onSetProperties(ProviderProperties properties) { - synchronized (mLock) { - mProperties = properties; + public void onStateChanged(State oldState, State newState) { + if (oldState.enabled != newState.enabled) { + // it would be more correct to call this for all users, but we know this can + // only affect the current user since providers are disabled for non-current + // users + onUseableChangedLocked(mCurrentUserId); } } @GuardedBy("mLock") - public boolean isUseableLocked() { - return isUseableLocked(mCurrentUserId); + public boolean isUseable() { + return isUseable(mCurrentUserId); } @GuardedBy("mLock") - public boolean isUseableLocked(int userId) { - return mUseable.get(userId, Boolean.FALSE); + public boolean isUseable(int userId) { + synchronized (mLock) { + return mUseable.get(userId, Boolean.FALSE); + } } @GuardedBy("mLock") public void onUseableChangedLocked(int userId) { + if (userId == UserHandle.USER_NULL) { + // only used during initialization - we don't care about the null user + return; + } + // if any property that contributes to "useability" here changes state, it MUST result // in a direct or indrect call to onUseableChangedLocked. this allows the provider to // guarantee that it will always eventually reach the correct state. - boolean useable = mProvider != null && mProviders.contains(this) - && isCurrentProfileLocked(userId) && isLocationEnabledForUser(userId) - && mEnabled; + boolean useable = isCurrentProfileLocked(userId) + && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().enabled; - if (useable == isUseableLocked(userId)) { + if (useable == isUseable(userId)) { return; } mUseable.put(userId, useable); @@ -1007,11 +968,7 @@ public class LocationManagerService extends ILocationManager.Stub { // fused and passive provider never get public updates for legacy reasons if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) { // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility - Settings.Secure.putStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - (useable ? "+" : "-") + mName, - userId); + mSettingsStore.setLocationProviderAllowed(mName, useable, userId); Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION); intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName); @@ -1031,53 +988,38 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private class MockLocationProvider extends LocationProviderManager { + class PassiveLocationProviderManager extends LocationProviderManager { - private ProviderRequest mCurrentRequest; - - private MockLocationProvider(String name) { - super(name); + private PassiveLocationProviderManager() { + super(PASSIVE_PROVIDER); } @Override - public void attachLocked(AbstractLocationProvider provider) { - checkState(provider instanceof MockProvider); - super.attachLocked(provider); - } - - public boolean isMock() { - return true; + public void setRealProvider(AbstractLocationProvider provider) { + Preconditions.checkArgument(provider instanceof PassiveProvider); + super.setRealProvider(provider); } - @GuardedBy("mLock") - public void setEnabledLocked(boolean enabled) { - if (mProvider != null) { - long identity = Binder.clearCallingIdentity(); - try { - ((MockProvider) mProvider).setEnabled(enabled); - } finally { - Binder.restoreCallingIdentity(identity); - } + @Override + public void setMockProvider(@Nullable MockProvider provider) { + if (provider != null) { + throw new IllegalArgumentException("Cannot mock the passive provider"); } } - @GuardedBy("mLock") - public void setLocationLocked(Location location) { - if (mProvider != null) { + public void updateLocation(Location location) { + synchronized (mLock) { + PassiveProvider passiveProvider = (PassiveProvider) mProvider.getProvider(); + Preconditions.checkState(passiveProvider != null); + long identity = Binder.clearCallingIdentity(); try { - ((MockProvider) mProvider).setLocation(location); + passiveProvider.updateLocation(location); } finally { Binder.restoreCallingIdentity(identity); } } } - - @Override - public void setRequest(ProviderRequest request, WorkSource workSource) { - super.setRequest(request, workSource); - mCurrentRequest = request; - } } /** @@ -1181,17 +1123,17 @@ public class LocationManagerService extends ILocationManager.Stub { // See if receiver has any enabled update records. Also note if any update records // are high power (has a high power provider with an interval under a threshold). for (UpdateRecord updateRecord : mUpdateRecords.values()) { - LocationProviderManager provider = getLocationProviderLocked( + LocationProviderManager manager = getLocationProviderManager( updateRecord.mProvider); - if (provider == null) { + if (manager == null) { continue; } - if (!provider.isUseableLocked() && !isSettingsExemptLocked(updateRecord)) { + if (!manager.isUseable() && !isSettingsExemptLocked(updateRecord)) { continue; } requestingLocation = true; - ProviderProperties properties = provider.getPropertiesLocked(); + ProviderProperties properties = manager.getProperties(); if (properties != null && properties.mPowerRequirement == Criteria.POWER_HIGH && updateRecord.mRequest.getInterval() < HIGH_POWER_INTERVAL_MS) { @@ -1432,7 +1374,7 @@ public class LocationManagerService extends ILocationManager.Stub { String featureId, String listenerIdentifier) { Objects.requireNonNull(listenerIdentifier); - return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback( + return mGnssManagerService != null && mGnssManagerService.addGnssBatchingCallback( callback, packageName, featureId, listenerIdentifier); } @@ -1443,7 +1385,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) { - return mGnssManagerService == null ? false : mGnssManagerService.startGnssBatch(periodNanos, + return mGnssManagerService != null && mGnssManagerService.startGnssBatch(periodNanos, wakeOnFifoFull, packageName); } @@ -1454,35 +1396,14 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean stopGnssBatch() { - return mGnssManagerService == null ? false : mGnssManagerService.stopGnssBatch(); + return mGnssManagerService != null && mGnssManagerService.stopGnssBatch(); } - @GuardedBy("mLock") - private void addProviderLocked(LocationProviderManager provider) { - Preconditions.checkState(getLocationProviderLocked(provider.getName()) == null); - - mProviders.add(provider); - - // it would be more correct to call this for all users, but we know this can only - // affect the current user since providers are disabled for non-current users - provider.onUseableChangedLocked(mCurrentUserId); - } - - @GuardedBy("mLock") - private void removeProviderLocked(LocationProviderManager provider) { - if (mProviders.remove(provider)) { - // it would be more correct to call this for all users, but we know this can only - // affect the current user since providers are disabled for non-current users - provider.onUseableChangedLocked(mCurrentUserId); - } - } - - @GuardedBy("mLock") @Nullable - private LocationProviderManager getLocationProviderLocked(String providerName) { - for (LocationProviderManager provider : mProviders) { - if (providerName.equals(provider.getName())) { - return provider; + private LocationProviderManager getLocationProviderManager(String providerName) { + for (LocationProviderManager manager : mProviderManagers) { + if (providerName.equals(manager.getName())) { + return manager; } } @@ -1531,12 +1452,12 @@ public class LocationManagerService extends ILocationManager.Stub { // network and fused providers are ok with COARSE or FINE return RESOLUTION_LEVEL_COARSE; } else { - for (LocationProviderManager lp : mProviders) { + for (LocationProviderManager lp : mProviderManagers) { if (!lp.getName().equals(provider)) { continue; } - ProviderProperties properties = lp.getPropertiesLocked(); + ProviderProperties properties = lp.getProperties(); if (properties != null) { if (properties.mRequiresSatellite) { // provider requiring satellites require FINE permission @@ -1587,11 +1508,9 @@ public class LocationManagerService extends ILocationManager.Stub { case RESOLUTION_LEVEL_COARSE: return AppOpsManager.OPSTR_COARSE_LOCATION; case RESOLUTION_LEVEL_FINE: - return AppOpsManager.OPSTR_FINE_LOCATION; + // fall through case RESOLUTION_LEVEL_NONE: - // The client is not allowed to get any location, so both FINE and COARSE ops will - // be denied. Pick the most restrictive one to be safe. - return AppOpsManager.OPSTR_FINE_LOCATION; + // fall through default: // Use the most restrictive ops if not sure. return AppOpsManager.OPSTR_FINE_LOCATION; @@ -1629,17 +1548,14 @@ public class LocationManagerService extends ILocationManager.Stub { */ @Override public List<String> getAllProviders() { - synchronized (mLock) { - ArrayList<String> providers = new ArrayList<>(mProviders.size()); - for (LocationProviderManager provider : mProviders) { - String name = provider.getName(); - if (FUSED_PROVIDER.equals(name)) { - continue; - } - providers.add(name); + ArrayList<String> providers = new ArrayList<>(mProviderManagers.size()); + for (LocationProviderManager manager : mProviderManagers) { + if (FUSED_PROVIDER.equals(manager.getName())) { + continue; } - return providers; + providers.add(manager.getName()); } + return providers; } /** @@ -1651,21 +1567,21 @@ public class LocationManagerService extends ILocationManager.Stub { public List<String> getProviders(Criteria criteria, boolean enabledOnly) { int allowedResolutionLevel = getCallerAllowedResolutionLevel(); synchronized (mLock) { - ArrayList<String> providers = new ArrayList<>(mProviders.size()); - for (LocationProviderManager provider : mProviders) { - String name = provider.getName(); + ArrayList<String> providers = new ArrayList<>(mProviderManagers.size()); + for (LocationProviderManager manager : mProviderManagers) { + String name = manager.getName(); if (FUSED_PROVIDER.equals(name)) { continue; } if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUseLocked(name)) { continue; } - if (enabledOnly && !provider.isUseableLocked()) { + if (enabledOnly && !manager.isUseable()) { continue; } if (criteria != null && !android.location.LocationProvider.propertiesMeetCriteria( - name, provider.getPropertiesLocked(), criteria)) { + name, manager.getProperties(), criteria)) { continue; } providers.add(name); @@ -1702,12 +1618,12 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void updateProviderUseableLocked(LocationProviderManager provider) { - boolean useable = provider.isUseableLocked(); + private void updateProviderUseableLocked(LocationProviderManager manager) { + boolean useable = manager.isUseable(); ArrayList<Receiver> deadReceivers = null; - ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName()); + ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); if (records != null) { for (UpdateRecord record : records) { if (!isCurrentProfileLocked( @@ -1721,7 +1637,7 @@ public class LocationManagerService extends ILocationManager.Stub { } // Sends a notification message to the receiver - if (!record.mReceiver.callProviderEnabledLocked(provider.getName(), useable)) { + if (!record.mReceiver.callProviderEnabledLocked(manager.getName(), useable)) { if (deadReceivers == null) { deadReceivers = new ArrayList<>(); } @@ -1736,26 +1652,25 @@ public class LocationManagerService extends ILocationManager.Stub { } } - applyRequirementsLocked(provider); + applyRequirementsLocked(manager); } @GuardedBy("mLock") private void applyRequirementsLocked(String providerName) { - LocationProviderManager provider = getLocationProviderLocked(providerName); - if (provider != null) { - applyRequirementsLocked(provider); + LocationProviderManager manager = getLocationProviderManager(providerName); + if (manager != null) { + applyRequirementsLocked(manager); } } @GuardedBy("mLock") - private void applyRequirementsLocked(LocationProviderManager provider) { - ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName()); - WorkSource worksource = new WorkSource(); - ProviderRequest providerRequest = new ProviderRequest(); + private void applyRequirementsLocked(LocationProviderManager manager) { + ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); + ProviderRequest.Builder providerRequest = new ProviderRequest.Builder(); // if provider is not active, it should not respond to requests - if (mProviders.contains(provider) && records != null && !records.isEmpty()) { + if (mProviderManagers.contains(manager) && records != null && !records.isEmpty()) { long backgroundThrottleInterval; long identity = Binder.clearCallingIdentity(); @@ -1765,6 +1680,8 @@ public class LocationManagerService extends ILocationManager.Stub { Binder.restoreCallingIdentity(identity); } + ArrayList<LocationRequest> requests = new ArrayList<>(records.size()); + final boolean isForegroundOnlyMode = mBatterySaverMode == PowerManager.LOCATION_MODE_FOREGROUND_ONLY; final boolean shouldThrottleRequests = @@ -1772,7 +1689,7 @@ public class LocationManagerService extends ILocationManager.Stub { == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF && !mPowerManager.isInteractive(); // initialize the low power mode to true and set to false if any of the records requires - providerRequest.lowPowerMode = true; + providerRequest.setLowPowerMode(true); for (UpdateRecord record : records) { if (!isCurrentProfileLocked( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { @@ -1787,10 +1704,10 @@ public class LocationManagerService extends ILocationManager.Stub { } final boolean isBatterySaverDisablingLocation = shouldThrottleRequests || (isForegroundOnlyMode && !record.mIsForegroundUid); - if (!provider.isUseableLocked() || isBatterySaverDisablingLocation) { + if (!manager.isUseable() || isBatterySaverDisablingLocation) { if (isSettingsExemptLocked(record)) { - providerRequest.locationSettingsIgnored = true; - providerRequest.lowPowerMode = false; + providerRequest.setLocationSettingsIgnored(true); + providerRequest.setLowPowerMode(false); } else { continue; } @@ -1801,7 +1718,7 @@ public class LocationManagerService extends ILocationManager.Stub { // if we're forcing location, don't apply any throttling - if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked( + if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExemptLocked( record.mReceiver.mCallerIdentity)) { if (!record.mIsForegroundUid) { interval = Math.max(interval, backgroundThrottleInterval); @@ -1813,23 +1730,25 @@ public class LocationManagerService extends ILocationManager.Stub { } record.mRequest = locationRequest; - providerRequest.locationRequests.add(locationRequest); + requests.add(locationRequest); if (!locationRequest.isLowPowerMode()) { - providerRequest.lowPowerMode = false; + providerRequest.setLowPowerMode(false); } - if (interval < providerRequest.interval) { - providerRequest.reportLocation = true; - providerRequest.interval = interval; + if (interval < providerRequest.getInterval()) { + providerRequest.setInterval(interval); } } - if (providerRequest.reportLocation) { + providerRequest.setLocationRequests(requests); + + if (providerRequest.getInterval() < Long.MAX_VALUE) { // calculate who to blame for power // This is somewhat arbitrary. We pick a threshold interval // that is slightly higher that the minimum interval, and // spread the blame across all applications with a request // under that threshold. - long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2; + // TODO: overflow + long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2; for (UpdateRecord record : records) { if (isCurrentProfileLocked( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { @@ -1837,18 +1756,18 @@ public class LocationManagerService extends ILocationManager.Stub { // Don't assign battery blame for update records whose // client has no permission to receive location data. - if (!providerRequest.locationRequests.contains(locationRequest)) { + if (!providerRequest.getLocationRequests().contains(locationRequest)) { continue; } if (locationRequest.getInterval() <= thresholdInterval) { if (record.mReceiver.mWorkSource != null && isValidWorkSource(record.mReceiver.mWorkSource)) { - worksource.add(record.mReceiver.mWorkSource); + providerRequest.getWorkSource().add(record.mReceiver.mWorkSource); } else { // Assign blame to caller if there's no WorkSource associated with // the request or if it's invalid. - worksource.add( + providerRequest.getWorkSource().add( record.mReceiver.mCallerIdentity.mUid, record.mReceiver.mCallerIdentity.mPackageName); } @@ -1858,7 +1777,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - provider.setRequest(providerRequest, worksource); + manager.setRequest(providerRequest.build()); } /** @@ -2198,8 +2117,8 @@ public class LocationManagerService extends ILocationManager.Stub { throw new IllegalArgumentException("provider name must not be null"); } - LocationProviderManager provider = getLocationProviderLocked(name); - if (provider == null) { + LocationProviderManager manager = getLocationProviderManager(name); + if (manager == null) { throw new IllegalArgumentException("provider doesn't exist: " + name); } @@ -2217,7 +2136,7 @@ public class LocationManagerService extends ILocationManager.Stub { oldRecord.disposeLocked(false); } - if (!provider.isUseableLocked() && !isSettingsExemptLocked(record)) { + if (!manager.isUseable() && !isSettingsExemptLocked(record)) { // Notify the listener that updates are currently disabled - but only if the request // does not ignore location settings receiver.callProviderEnabledLocked(name, false); @@ -2320,8 +2239,8 @@ public class LocationManagerService extends ILocationManager.Stub { // or use the fused provider String name = request.getProvider(); if (name == null) name = LocationManager.FUSED_PROVIDER; - LocationProviderManager provider = getLocationProviderLocked(name); - if (provider == null) return null; + LocationProviderManager manager = getLocationProviderManager(name); + if (manager == null) return null; // only the current user or location providers may get location this way if (!isCurrentProfileLocked(UserHandle.getUserId(uid)) && !isProviderPackage( @@ -2329,7 +2248,7 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - if (!provider.isUseableLocked()) { + if (!manager.isUseable()) { return null; } @@ -2450,19 +2369,19 @@ public class LocationManagerService extends ILocationManager.Stub { "Access Fine Location permission not granted to inject Location"); synchronized (mLock) { - LocationProviderManager provider = getLocationProviderLocked(location.getProvider()); - if (provider == null || !provider.isUseableLocked()) { + LocationProviderManager manager = getLocationProviderManager(location.getProvider()); + if (manager == null || !manager.isUseable()) { return false; } // NOTE: If last location is already available, location is not injected. If // provider's normal source (like a GPS chipset) have already provided an output // there is no need to inject this location. - if (mLastLocation.get(provider.getName()) != null) { + if (mLastLocation.get(manager.getName()) != null) { return false; } - updateLastLocationLocked(location, provider.getName()); + updateLastLocationLocked(location, manager.getName()); return true; } } @@ -2511,7 +2430,7 @@ public class LocationManagerService extends ILocationManager.Stub { packageName, request, /* hasListener= */ false, - intent != null, + true, geofence, mActivityManager.getPackageImportance(packageName)); } @@ -2542,7 +2461,7 @@ public class LocationManagerService extends ILocationManager.Stub { packageName, /* LocationRequest= */ null, /* hasListener= */ false, - intent != null, + true, geofence, mActivityManager.getPackageImportance(packageName)); } @@ -2555,7 +2474,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName, String featureId) { - return mGnssManagerService == null ? false : mGnssManagerService.registerGnssStatusCallback( + return mGnssManagerService != null && mGnssManagerService.registerGnssStatusCallback( listener, packageName, featureId); } @@ -2569,9 +2488,8 @@ public class LocationManagerService extends ILocationManager.Stub { String packageName, String featureId, String listenerIdentifier) { Objects.requireNonNull(listenerIdentifier); - return mGnssManagerService == null ? false - : mGnssManagerService.addGnssMeasurementsListener(listener, packageName, featureId, - listenerIdentifier); + return mGnssManagerService != null && mGnssManagerService.addGnssMeasurementsListener( + listener, packageName, featureId, listenerIdentifier); } @Override @@ -2586,8 +2504,8 @@ public class LocationManagerService extends ILocationManager.Stub { public void injectGnssMeasurementCorrections( GnssMeasurementCorrections measurementCorrections, String packageName) { if (mGnssManagerService != null) { - mGnssManagerService.injectGnssMeasurementCorrections( - measurementCorrections, packageName); + mGnssManagerService.injectGnssMeasurementCorrections(measurementCorrections, + packageName); } } @@ -2602,9 +2520,8 @@ public class LocationManagerService extends ILocationManager.Stub { String packageName, String featureId, String listenerIdentifier) { Objects.requireNonNull(listenerIdentifier); - return mGnssManagerService == null ? false - : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName, - featureId, listenerIdentifier); + return mGnssManagerService != null && mGnssManagerService.addGnssNavigationMessageListener( + listener, packageName, featureId, listenerIdentifier); } @Override @@ -2634,9 +2551,10 @@ public class LocationManagerService extends ILocationManager.Stub { LocationStatsEnums.API_SEND_EXTRA_COMMAND, providerName); - LocationProviderManager provider = getLocationProviderLocked(providerName); - if (provider != null) { - provider.sendExtraCommand(command, extras); + LocationProviderManager manager = getLocationProviderManager(providerName); + if (manager != null) { + manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command, + extras); } mLocationUsageLogger.logLocationApiUsage( @@ -2650,43 +2568,37 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean sendNiResponse(int notifId, int userResponse) { - return mGnssManagerService == null ? false : mGnssManagerService.sendNiResponse(notifId, + return mGnssManagerService != null && mGnssManagerService.sendNiResponse(notifId, userResponse); } @Override public ProviderProperties getProviderProperties(String providerName) { - synchronized (mLock) { - LocationProviderManager provider = getLocationProviderLocked(providerName); - if (provider == null) { - return null; - } - return provider.getPropertiesLocked(); + LocationProviderManager manager = getLocationProviderManager(providerName); + if (manager == null) { + return null; } + return manager.getProperties(); } @Override public boolean isProviderPackage(String packageName) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, Manifest.permission.READ_DEVICE_CONFIG + " permission required"); - synchronized (mLock) { - for (LocationProviderManager provider : mProviders) { - if (provider.getPackagesLocked().contains(packageName)) { - return true; - } + for (LocationProviderManager manager : mProviderManagers) { + if (manager.getPackages().contains(packageName)) { + return true; } - return false; } + return false; } @Override public List<String> getProviderPackages(String providerName) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, Manifest.permission.READ_DEVICE_CONFIG + " permission required"); - synchronized (mLock) { - LocationProviderManager provider = getLocationProviderLocked(providerName); - return provider == null ? Collections.emptyList() : provider.getPackagesLocked(); - } + LocationProviderManager manager = getLocationProviderManager(providerName); + return manager == null ? Collections.emptyList() : new ArrayList<>(manager.getPackages()); } @Override @@ -2753,8 +2665,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (FUSED_PROVIDER.equals(providerName)) return false; synchronized (mLock) { - LocationProviderManager provider = getLocationProviderLocked(providerName); - return provider != null && provider.isUseableLocked(userId); + LocationProviderManager manager = getLocationProviderManager(providerName); + return manager != null && manager.isUseable(userId); } } @@ -2792,37 +2704,39 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void handleLocationChangedLocked(Location location, LocationProviderManager provider) { - if (!mProviders.contains(provider)) { + private void handleLocationChangedLocked(Location location, LocationProviderManager manager) { + if (!mProviderManagers.contains(manager)) { + Log.w(TAG, "received location from unknown provider: " + manager.getName()); return; } if (!location.isComplete()) { - Log.w(TAG, "Dropping incomplete location: " + location); + Log.w(TAG, "dropping incomplete location from " + manager.getName() + " provider: " + + location); return; } - // only notify passive provider and update last location for locations that come from - // useable providers - if (provider.isUseableLocked()) { - if (!provider.isPassiveLocked()) { - mPassiveProvider.updateLocation(location); - } + // notify passive provider + if (manager != mPassiveManager) { + mPassiveManager.updateLocation(new Location(location)); } if (D) Log.d(TAG, "incoming location: " + location); long now = SystemClock.elapsedRealtime(); - if (provider.isUseableLocked()) { - updateLastLocationLocked(location, provider.getName()); + + + // only update last location for locations that come from useable providers + if (manager.isUseable()) { + updateLastLocationLocked(location, manager.getName()); } // Update last known coarse interval location if enough time has passed. Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get( - provider.getName()); + manager.getName()); if (lastLocationCoarseInterval == null) { lastLocationCoarseInterval = new Location(location); - if (provider.isUseableLocked()) { - mLastLocationCoarseInterval.put(provider.getName(), lastLocationCoarseInterval); + if (manager.isUseable()) { + mLastLocationCoarseInterval.put(manager.getName(), lastLocationCoarseInterval); } } long timeDeltaMs = TimeUnit.NANOSECONDS.toMillis(location.getElapsedRealtimeNanos() @@ -2837,7 +2751,7 @@ public class LocationManagerService extends ILocationManager.Stub { lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); // Skip if there are no UpdateRecords for this provider. - ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName()); + ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); if (records == null || records.size() == 0) return; // Fetch coarse location @@ -2854,7 +2768,7 @@ public class LocationManagerService extends ILocationManager.Stub { Receiver receiver = r.mReceiver; boolean receiverDead = false; - if (!provider.isUseableLocked() && !isSettingsExemptLocked(r)) { + if (!manager.isUseable() && !isSettingsExemptLocked(r)) { continue; } @@ -2949,7 +2863,7 @@ public class LocationManagerService extends ILocationManager.Stub { for (UpdateRecord r : deadUpdateRecords) { r.disposeLocked(true); } - applyRequirementsLocked(provider); + applyRequirementsLocked(manager); } } @@ -3006,143 +2920,99 @@ public class LocationManagerService extends ILocationManager.Stub { // Mock Providers - private boolean canCallerAccessMockLocation(String opPackageName) { - return mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), - opPackageName) == AppOpsManager.MODE_ALLOWED; - } - @Override - public void addTestProvider(String name, ProviderProperties properties, String opPackageName) { - if (!canCallerAccessMockLocation(opPackageName)) { + public void addTestProvider(String provider, ProviderProperties properties, + String packageName) { + if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) + != AppOpsManager.MODE_ALLOWED) { return; } - if (PASSIVE_PROVIDER.equals(name)) { - throw new IllegalArgumentException("Cannot mock the passive location provider"); - } - synchronized (mLock) { - long identity = Binder.clearCallingIdentity(); - try { - LocationProviderManager oldProvider = getLocationProviderLocked(name); - if (oldProvider != null) { - removeProviderLocked(oldProvider); - } - - MockLocationProvider mockProviderManager = new MockLocationProvider(name); - addProviderLocked(mockProviderManager); - mockProviderManager.attachLocked( - new MockProvider(mContext, mockProviderManager, properties)); - } finally { - Binder.restoreCallingIdentity(identity); + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + manager = new LocationProviderManager(provider); + mProviderManagers.add(manager); } + + manager.setMockProvider(new MockProvider(mContext, properties)); } } @Override - public void removeTestProvider(String name, String opPackageName) { - if (!canCallerAccessMockLocation(opPackageName)) { + public void removeTestProvider(String provider, String packageName) { + if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) + != AppOpsManager.MODE_ALLOWED) { return; } synchronized (mLock) { - long identity = Binder.clearCallingIdentity(); - try { - LocationProviderManager testProvider = getLocationProviderLocked(name); - if (testProvider == null || !testProvider.isMock()) { - return; - } - - removeProviderLocked(testProvider); - - // reinstate real provider if available - LocationProviderManager realProvider = null; - for (LocationProviderManager provider : mRealProviders) { - if (name.equals(provider.getName())) { - realProvider = provider; - break; - } - } + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + return; + } - if (realProvider != null) { - addProviderLocked(realProvider); - } - } finally { - Binder.restoreCallingIdentity(identity); + manager.setMockProvider(null); + if (!manager.hasProvider()) { + mProviderManagers.remove(manager); + mLastLocation.remove(manager.getName()); + mLastLocationCoarseInterval.remove(manager.getName()); } } } @Override - public void setTestProviderLocation(String providerName, Location location, - String opPackageName) { - if (!canCallerAccessMockLocation(opPackageName)) { + public void setTestProviderLocation(String provider, Location location, String packageName) { + if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) + != AppOpsManager.MODE_ALLOWED) { return; } - synchronized (mLock) { - LocationProviderManager testProvider = getLocationProviderLocked(providerName); - if (testProvider == null || !testProvider.isMock()) { - throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown"); - } - - String locationProvider = location.getProvider(); - if (!TextUtils.isEmpty(locationProvider) && !providerName.equals(locationProvider)) { - // The location has an explicit provider that is different from the mock - // provider name. The caller may be trying to fool us via b/33091107. - EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), - providerName + "!=" + location.getProvider()); - } - - ((MockLocationProvider) testProvider).setLocationLocked(location); + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + throw new IllegalArgumentException("provider doesn't exist: " + provider); } + + manager.setMockProviderLocation(location); } @Override - public void setTestProviderEnabled(String providerName, boolean enabled, String opPackageName) { - if (!canCallerAccessMockLocation(opPackageName)) { + public void setTestProviderEnabled(String provider, boolean enabled, String packageName) { + if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) + != AppOpsManager.MODE_ALLOWED) { return; } - synchronized (mLock) { - LocationProviderManager testProvider = getLocationProviderLocked(providerName); - if (testProvider == null || !testProvider.isMock()) { - throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown"); - } - - ((MockLocationProvider) testProvider).setEnabledLocked(enabled); + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + throw new IllegalArgumentException("provider doesn't exist: " + provider); } + + manager.setMockProviderEnabled(enabled); } @Override @NonNull - public List<LocationRequest> getTestProviderCurrentRequests(String providerName, - String opPackageName) { - if (!canCallerAccessMockLocation(opPackageName)) { + public List<LocationRequest> getTestProviderCurrentRequests(String provider, + String packageName) { + if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) + != AppOpsManager.MODE_ALLOWED) { return Collections.emptyList(); } - synchronized (mLock) { - LocationProviderManager testProvider = getLocationProviderLocked(providerName); - if (testProvider == null || !testProvider.isMock()) { - throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown"); - } - - MockLocationProvider provider = (MockLocationProvider) testProvider; - if (provider.mCurrentRequest == null) { - return Collections.emptyList(); - } - List<LocationRequest> requests = new ArrayList<>(); - for (LocationRequest request : provider.mCurrentRequest.locationRequests) { - requests.add(new LocationRequest(request)); - } - return requests; + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + throw new IllegalArgumentException("provider doesn't exist: " + provider); } + + return manager.getMockProviderRequests(); } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) { + return; + } IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); @@ -3192,6 +3062,8 @@ public class LocationManagerService extends ILocationManager.Stub { } ipw.decreaseIndent(); + mRequestStatistics.history.dump(ipw); + ipw.println("Last Known Locations:"); ipw.increaseIndent(); for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) { @@ -3224,25 +3096,27 @@ public class LocationManagerService extends ILocationManager.Stub { mLocationFudger.dump(fd, ipw, args); ipw.decreaseIndent(); } + } - ipw.println("Location Settings:"); - ipw.increaseIndent(); - mSettingsStore.dump(fd, ipw, args); - ipw.decreaseIndent(); + ipw.println("Location Settings:"); + ipw.increaseIndent(); + mSettingsStore.dump(fd, ipw, args); + ipw.decreaseIndent(); - ipw.println("Location Providers:"); - ipw.increaseIndent(); - for (LocationProviderManager provider : mProviders) { - provider.dumpLocked(fd, ipw, args); - } - ipw.decreaseIndent(); + ipw.println("Location Providers:"); + ipw.increaseIndent(); + for (LocationProviderManager manager : mProviderManagers) { + manager.dump(fd, ipw, args); } + ipw.decreaseIndent(); - if (mGnssManagerService != null) { - ipw.println("GNSS:"); - ipw.increaseIndent(); - mGnssManagerService.dump(fd, ipw, args); - ipw.decreaseIndent(); + synchronized (mLock) { + if (mGnssManagerService != null) { + ipw.println("GNSS:"); + ipw.increaseIndent(); + mGnssManagerService.dump(fd, ipw, args); + ipw.decreaseIndent(); + } } } } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 1f736502ae42..05d83606b2d0 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -58,10 +58,12 @@ import android.net.NetworkStack; import android.net.NetworkStats; import android.net.NetworkUtils; import android.net.RouteInfo; -import android.net.TetherConfigParcel; import android.net.TetherStatsParcel; import android.net.UidRange; import android.net.UidRangeParcel; +import android.net.shared.NetdUtils; +import android.net.shared.RouteUtils; +import android.net.shared.RouteUtils.ModifyOperation; import android.net.util.NetdService; import android.os.BatteryStats; import android.os.Binder; @@ -898,48 +900,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public void addRoute(int netId, RouteInfo route) { - modifyRoute(MODIFY_OPERATION_ADD, netId, route); + NetworkStack.checkNetworkStackPermission(mContext); + RouteUtils.modifyRoute(mNetdService, ModifyOperation.ADD, netId, route); } @Override public void removeRoute(int netId, RouteInfo route) { - modifyRoute(MODIFY_OPERATION_REMOVE, netId, route); - } - - private void modifyRoute(boolean add, int netId, RouteInfo route) { NetworkStack.checkNetworkStackPermission(mContext); - - final String ifName = route.getInterface(); - final String dst = route.getDestination().toString(); - final String nextHop; - - switch (route.getType()) { - case RouteInfo.RTN_UNICAST: - if (route.hasGateway()) { - nextHop = route.getGateway().getHostAddress(); - } else { - nextHop = INetd.NEXTHOP_NONE; - } - break; - case RouteInfo.RTN_UNREACHABLE: - nextHop = INetd.NEXTHOP_UNREACHABLE; - break; - case RouteInfo.RTN_THROW: - nextHop = INetd.NEXTHOP_THROW; - break; - default: - nextHop = INetd.NEXTHOP_NONE; - break; - } - try { - if (add) { - mNetdService.networkAddRoute(netId, ifName, dst, nextHop); - } else { - mNetdService.networkRemoveRoute(netId, ifName, dst, nextHop); - } - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } + RouteUtils.modifyRoute(mNetdService, ModifyOperation.REMOVE, netId, route); } private ArrayList<String> readRouteList(String filename) { @@ -1023,12 +991,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public void startTetheringWithConfiguration(boolean usingLegacyDnsProxy, String[] dhcpRange) { NetworkStack.checkNetworkStackPermission(mContext); - // an odd number of addrs will fail try { - final TetherConfigParcel config = new TetherConfigParcel(); - config.usingLegacyDnsProxy = usingLegacyDnsProxy; - config.dhcpRanges = dhcpRange; - mNetdService.tetherStartWithConfiguration(config); + NetdUtils.tetherStart(mNetdService, usingLegacyDnsProxy, dhcpRange); } catch (RemoteException | ServiceSpecificException e) { throw new IllegalStateException(e); } @@ -1060,26 +1024,21 @@ public class NetworkManagementService extends INetworkManagementService.Stub { public void tetherInterface(String iface) { NetworkStack.checkNetworkStackPermission(mContext); try { - mNetdService.tetherInterfaceAdd(iface); + final LinkAddress addr = getInterfaceConfig(iface).getLinkAddress(); + final IpPrefix dest = new IpPrefix(addr.getAddress(), addr.getPrefixLength()); + NetdUtils.tetherInterface(mNetdService, iface, dest); } catch (RemoteException | ServiceSpecificException e) { throw new IllegalStateException(e); } - List<RouteInfo> routes = new ArrayList<>(); - // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it - // suitable to use as a route destination. - routes.add(new RouteInfo(getInterfaceConfig(iface).getLinkAddress(), null, iface)); - addInterfaceToLocalNetwork(iface, routes); } @Override public void untetherInterface(String iface) { NetworkStack.checkNetworkStackPermission(mContext); try { - mNetdService.tetherInterfaceRemove(iface); + NetdUtils.untetherInterface(mNetdService, iface); } catch (RemoteException | ServiceSpecificException e) { throw new IllegalStateException(e); - } finally { - removeInterfaceFromLocalNetwork(iface); } } @@ -2122,16 +2081,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) { modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, iface); - - for (RouteInfo route : routes) { - if (!route.isDefaultRoute()) { - modifyRoute(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, route); - } - } - - // IPv6 link local should be activated always. - modifyRoute(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, - new RouteInfo(new IpPrefix("fe80::/64"), null, iface)); + // modifyInterfaceInNetwork already check calling permission. + RouteUtils.addRoutesToLocalNetwork(mNetdService, iface, routes); } @Override @@ -2141,17 +2092,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public int removeRoutesFromLocalNetwork(List<RouteInfo> routes) { - int failures = 0; - - for (RouteInfo route : routes) { - try { - modifyRoute(MODIFY_OPERATION_REMOVE, INetd.LOCAL_NET_ID, route); - } catch (IllegalStateException e) { - failures++; - } - } - - return failures; + NetworkStack.checkNetworkStackPermission(mContext); + return RouteUtils.removeRoutesFromLocalNetwork(mNetdService, routes); } @Override diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java index cfe56052118f..d20936c2d217 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java @@ -36,11 +36,11 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; +import android.os.TimestampedValue; import android.provider.Settings; import android.util.Log; import android.util.NtpTrustedTime; import android.util.TimeUtils; -import android.util.TimestampedValue; import com.android.internal.util.DumpUtils; diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index deff440aa0a6..7b4fd37f01c9 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -199,13 +199,14 @@ public class PackageWatchdog { mSystemClock = clock; mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; loadFromFile(); + sPackageWatchdog = this; } /** Creates or gets singleton instance of PackageWatchdog. */ public static PackageWatchdog getInstance(Context context) { synchronized (PackageWatchdog.class) { if (sPackageWatchdog == null) { - sPackageWatchdog = new PackageWatchdog(context); + new PackageWatchdog(context); } return sPackageWatchdog; } diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index fb1a962c0ef3..3dafc64391fd 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -18,8 +18,12 @@ package com.android.server; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.VersionedPackage; import android.os.Build; import android.os.Environment; import android.os.FileUtils; @@ -36,8 +40,12 @@ import android.util.Slog; import android.util.SparseArray; import android.util.StatsLog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.server.PackageWatchdog.FailureReasons; +import com.android.server.PackageWatchdog.PackageHealthObserver; +import com.android.server.PackageWatchdog.PackageHealthObserverImpact; import com.android.server.am.SettingsToPropertiesMapper; import com.android.server.utils.FlagNamespaceUtils; @@ -79,19 +87,30 @@ public class RescueParty { @VisibleForTesting static final long BOOT_TRIGGER_WINDOW_MILLIS = 600 * DateUtils.SECOND_IN_MILLIS; @VisibleForTesting - static final long PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS; - @VisibleForTesting static final String TAG = "RescueParty"; + private static final String NAME = "rescue-party-observer"; + + private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start"; private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device"; + private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT + | ApplicationInfo.FLAG_SYSTEM; + + /** Threshold for boot loops */ private static final Threshold sBoot = new BootThreshold(); /** Threshold for app crash loops */ private static SparseArray<Threshold> sApps = new SparseArray<>(); + /** Register the Rescue Party observer as a Package Watchdog health observer */ + public static void registerHealthObserver(Context context) { + PackageWatchdog.getInstance(context).registerHealthObserver( + RescuePartyObserver.getInstance(context)); + } + private static boolean isDisabled() { // Check if we're explicitly enabled for testing if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) { @@ -135,24 +154,6 @@ public class RescueParty { } /** - * Take note of a persistent app or apex module crash. If we notice too many of these - * events happening in rapid succession, we'll send out a rescue party. - */ - public static void noteAppCrash(Context context, int uid) { - if (isDisabled()) return; - Threshold t = sApps.get(uid); - if (t == null) { - t = new AppThreshold(uid); - sApps.put(uid, t); - } - if (t.incrementAndTest()) { - t.reset(); - incrementRescueLevel(t.uid); - executeRescueLevel(context); - } - } - - /** * Check if we're currently attempting to reboot for a factory reset. */ public static boolean isAttemptingFactoryReset() { @@ -171,11 +172,6 @@ public class RescueParty { @VisibleForTesting static void resetAllThresholds() { sBoot.reset(); - - for (int i = 0; i < sApps.size(); i++) { - Threshold appThreshold = sApps.get(sApps.keyAt(i)); - appThreshold.reset(); - } } @VisibleForTesting @@ -243,6 +239,28 @@ public class RescueParty { FlagNamespaceUtils.NAMESPACE_NO_PACKAGE); } + private static int mapRescueLevelToUserImpact(int rescueLevel) { + switch(rescueLevel) { + case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: + case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: + return PackageHealthObserverImpact.USER_IMPACT_LOW; + case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + case LEVEL_FACTORY_RESET: + return PackageHealthObserverImpact.USER_IMPACT_HIGH; + default: + return PackageHealthObserverImpact.USER_IMPACT_NONE; + } + } + + private static int getPackageUid(Context context, String packageName) { + try { + return context.getPackageManager().getPackageUid(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + // Since UIDs are always >= 0, this value means the UID could not be determined. + return -1; + } + } + private static void resetAllSettings(Context context, int mode) throws Exception { // Try our best to reset all settings possible, and once finished // rethrow any exception that we encountered @@ -271,6 +289,89 @@ public class RescueParty { } /** + * Handle mitigation action for package failures. This observer will be register to Package + * Watchdog and will receive calls about package failures. This observer is persistent so it + * may choose to mitigate failures for packages it has not explicitly asked to observe. + */ + public static class RescuePartyObserver implements PackageHealthObserver { + + private Context mContext; + + @GuardedBy("RescuePartyObserver.class") + static RescuePartyObserver sRescuePartyObserver; + + private RescuePartyObserver(Context context) { + mContext = context; + } + + /** Creates or gets singleton instance of RescueParty. */ + public static RescuePartyObserver getInstance(Context context) { + synchronized (RescuePartyObserver.class) { + if (sRescuePartyObserver == null) { + sRescuePartyObserver = new RescuePartyObserver(context); + } + return sRescuePartyObserver; + } + } + + @Override + public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, + @FailureReasons int failureReason) { + if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH + || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { + int rescueLevel = MathUtils.constrain( + SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1, + LEVEL_NONE, LEVEL_FACTORY_RESET); + return mapRescueLevelToUserImpact(rescueLevel); + } else { + return PackageHealthObserverImpact.USER_IMPACT_NONE; + } + } + + @Override + public boolean execute(@Nullable VersionedPackage failedPackage, + @FailureReasons int failureReason) { + if (isDisabled()) { + return false; + } + if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH + || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { + int triggerUid = getPackageUid(mContext, failedPackage.getPackageName()); + incrementRescueLevel(triggerUid); + executeRescueLevel(mContext); + return true; + } else { + return false; + } + } + + @Override + public boolean isPersistent() { + return true; + } + + @Override + public boolean mayObservePackage(String packageName) { + PackageManager pm = mContext.getPackageManager(); + try { + // A package is a Mainline module if this is non-null + if (pm.getModuleInfo(packageName, 0) != null) { + return true; + } + ApplicationInfo info = pm.getApplicationInfo(packageName, 0); + return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + @Override + public String getName() { + return NAME; + } + } + + /** * Threshold that can be triggered if a number of events occur within a * window of time. */ @@ -349,27 +450,6 @@ public class RescueParty { } } - /** - * Specialization of {@link Threshold} for monitoring app crashes. It stores - * counters in memory. - */ - private static class AppThreshold extends Threshold { - private int count; - private long start; - - public AppThreshold(int uid) { - // We're interested in TRIGGER_COUNT events in any - // PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS second period; apps crash pretty quickly - // so we can keep a tight leash on them. - super(uid, TRIGGER_COUNT, PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS); - } - - @Override public int getCount() { return count; } - @Override public void setCount(int count) { this.count = count; } - @Override public long getStart() { return start; } - @Override public void setStart(long start) { this.start = start; } - } - private static int[] getAllUserIds() { int[] userIds = { UserHandle.USER_SYSTEM }; try { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 3d455ee1cf19..db542145a750 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.Manifest.permission.ACCESS_MTP; import static android.Manifest.permission.INSTALL_PACKAGES; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; @@ -111,6 +112,7 @@ import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; import android.provider.DeviceConfig; +import android.provider.Downloads; import android.provider.MediaStore; import android.provider.Settings; import android.sysprop.VoldProperties; @@ -367,6 +369,8 @@ class StorageManagerService extends IStorageManager.Stub private volatile int mMediaStoreAuthorityAppId = -1; + private volatile int mDownloadsAuthorityAppId = -1; + private volatile int mCurrentUserId = UserHandle.USER_SYSTEM; private final Installer mInstaller; @@ -1788,6 +1792,15 @@ class StorageManagerService extends IStorageManager.Stub mMediaStoreAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid); } + provider = mPmInternal.resolveContentProvider( + Downloads.Impl.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.getUserId(UserHandle.USER_SYSTEM)); + + if (provider != null) { + mDownloadsAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid); + } + try { mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback); mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback); @@ -3881,6 +3894,19 @@ class StorageManagerService extends IStorageManager.Stub return Zygote.MOUNT_EXTERNAL_PASS_THROUGH; } + if (mIsFuseEnabled && mDownloadsAuthorityAppId == UserHandle.getAppId(uid)) { + // DownloadManager can write in app-private directories on behalf of apps; + // give it write access to Android/ + return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; + } + + final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) == + PERMISSION_GRANTED; + if (mIsFuseEnabled && hasMtp) { + // The process hosting the MTP server should be able to write in Android/ + return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; + } + // Determine if caller is holding runtime permission final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0, uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE); diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 2091c2a0c694..9ffe89c61a44 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -1059,8 +1059,8 @@ final class UiModeManagerService extends SystemService { if (Sandman.shouldStartDockApp(getContext(), homeIntent)) { try { int result = ActivityTaskManager.getService().startActivityWithConfig( - null, null, homeIntent, null, null, null, 0, 0, - mConfiguration, null, UserHandle.USER_CURRENT); + null, getContext().getBasePackageName(), homeIntent, null, null, null, + 0, 0, mConfiguration, null, UserHandle.USER_CURRENT); if (ActivityManager.isStartResultSuccessful(result)) { dockAppStarted = true; } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 454941ccdb03..a60b09fda2e8 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -39,8 +39,10 @@ import android.system.StructRlimit; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.util.StatsLog; +import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.ZygoteConnectionConstants; import com.android.server.am.ActivityManagerService; import com.android.server.wm.SurfaceAnimationThread; @@ -606,13 +608,18 @@ public class Watchdog extends Thread { pids.add(Process.myPid()); if (mPhonePid > 0) pids.add(mPhonePid); + long anrTime = SystemClock.uptimeMillis(); + ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false); final File stack = ActivityManagerService.dumpStackTraces( - pids, null, null, getInterestingNativePids()); + pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids()); // Give some extra time to make sure the stack traces get written. // The system's been hanging for a minute, another second or two won't hurt much. SystemClock.sleep(5000); + processCpuTracker.update(); + String cpuInfo = processCpuTracker.printCurrentState(anrTime); + // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log doSysRq('w'); doSysRq('l'); @@ -627,7 +634,7 @@ public class Watchdog extends Thread { if (mActivity != null) { mActivity.addErrorToDropBox( "watchdog", null, "system_server", null, null, null, - subject, null, stack, null); + subject, cpuInfo, stack, null); } StatsLog.write(StatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject); } diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index 4b48ef917744..143474bd5c94 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -413,6 +413,11 @@ public class AdbDebuggingManager { case MESSAGE_ADB_CLEAR: { Slog.d(TAG, "Received a request to clear the adb authorizations"); mConnectedKeys.clear(); + // If the key store has not yet been instantiated then do so now; this avoids + // the unnecessary creation of the key store when adb is not enabled. + if (mAdbKeyStore == null) { + mAdbKeyStore = new AdbKeyStore(); + } mAdbKeyStore.deleteKeyStore(); cancelJobToUpdateAdbKeyStore(); break; diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index a4c44fabfba6..d7ad1c252401 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2934,33 +2934,37 @@ final class ActivityManagerShellCommand extends ShellCommand { } ArraySet<Long> enabled = new ArraySet<>(); ArraySet<Long> disabled = new ArraySet<>(); - switch (toggleValue) { - case "enable": - enabled.add(changeId); - pw.println("Enabled change " + changeId + " for " + packageName + "."); - CompatibilityChangeConfig overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - return 0; - case "disable": - disabled.add(changeId); - pw.println("Disabled change " + changeId + " for " + packageName + "."); - overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - return 0; - case "reset": - if (platformCompat.clearOverride(changeId, packageName)) { - pw.println("Reset change " + changeId + " for " + packageName - + " to default value."); - } else { - pw.println("No override exists for changeId " + changeId + "."); - } - return 0; - default: - pw.println("Invalid toggle value: '" + toggleValue + "'."); + try { + switch (toggleValue) { + case "enable": + enabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Enabled change " + changeId + " for " + packageName + "."); + return 0; + case "disable": + disabled.add(changeId); + overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Disabled change " + changeId + " for " + packageName + "."); + return 0; + case "reset": + if (platformCompat.clearOverride(changeId, packageName)) { + pw.println("Reset change " + changeId + " for " + packageName + + " to default value."); + } else { + pw.println("No override exists for changeId " + changeId + "."); + } + return 0; + default: + pw.println("Invalid toggle value: '" + toggleValue + "'."); + } + } catch (SecurityException e) { + pw.println(e.getMessage()); } return -1; } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 5e48dcf91676..8071f52037e6 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -33,8 +33,6 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.ModuleInfo; -import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.net.Uri; import android.os.Binder; @@ -56,7 +54,6 @@ import com.android.internal.app.ProcessMap; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.server.PackageWatchdog; -import com.android.server.RescueParty; import com.android.server.wm.WindowProcessController; import java.io.FileDescriptor; @@ -423,28 +420,6 @@ class AppErrors { } if (r != null) { - boolean isApexModule = false; - try { - for (String androidPackage : r.getPackageList()) { - ModuleInfo moduleInfo = mContext.getPackageManager().getModuleInfo( - androidPackage, /*flags=*/ 0); - if (moduleInfo != null) { - isApexModule = true; - break; - } - } - } catch (IllegalStateException | PackageManager.NameNotFoundException e) { - // Call to PackageManager#getModuleInfo() can result in NameNotFoundException or - // IllegalStateException. In case they are thrown, there isn't much we can do - // other than proceed with app crash handling. - } - - if (r.isPersistent() || isApexModule) { - // If a persistent app or apex module is stuck in a crash loop, the device isn't - // very usable, so we want to consider sending out a rescue party. - RescueParty.noteAppCrash(mContext, r.uid); - } - mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(), PackageWatchdog.FAILURE_REASON_APP_CRASH); } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index a1e1f29016c2..b9f2e76c6ecb 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -103,6 +103,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.MemInfoReader; import com.android.server.LocalServices; import com.android.server.ServiceThread; +import com.android.server.SystemConfig; import com.android.server.Watchdog; import com.android.server.compat.PlatformCompat; import com.android.server.pm.dex.DexManager; @@ -357,6 +358,8 @@ public final class ProcessList { private boolean mAppDataIsolationEnabled = false; + private ArrayList<String> mAppDataIsolationWhitelistedApps; + /** * Temporary to avoid allocations. Protected by main lock. */ @@ -645,6 +648,9 @@ public final class ProcessList { // want some apps enabled while some apps disabled mAppDataIsolationEnabled = SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); + mAppDataIsolationWhitelistedApps = new ArrayList<>( + SystemConfig.getInstance().getAppDataIsolationWhitelistedApps()); + if (sKillHandler == null) { sKillThread = new ServiceThread(TAG + ":kill", @@ -1555,20 +1561,27 @@ public final class ProcessList { } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } - + int numGids = 3; + if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER + || mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) { + numGids++; + } /* * Add shared application and profile GIDs so applications can share some * resources like shared libraries and access user-wide resources */ if (ArrayUtils.isEmpty(permGids)) { - gids = new int[3]; + gids = new int[numGids]; } else { - gids = new int[permGids.length + 3]; - System.arraycopy(permGids, 0, gids, 3, permGids.length); + gids = new int[permGids.length + numGids]; + System.arraycopy(permGids, 0, gids, numGids, permGids.length); } gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid)); gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid)); gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid)); + if (numGids > 3) { + gids[3] = Process.SDCARD_RW_GID; + } // Replace any invalid GIDs if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2]; @@ -1905,6 +1918,16 @@ public final class ProcessList { result.put(packageName, Pair.create(volumeUuid, inode)); } } + if (mAppDataIsolationWhitelistedApps != null) { + for (String packageName : mAppDataIsolationWhitelistedApps) { + String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid(); + long inode = pmInt.getCeDataInode(packageName, userId); + if (inode != 0) { + result.put(packageName, Pair.create(volumeUuid, inode)); + } + } + } + return result; } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index cf83dd630a29..f15d999e1006 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -17,8 +17,10 @@ package com.android.server.compat; import android.compat.Compatibility.ChangeConfig; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Environment; +import android.os.RemoteException; import android.text.TextUtils; import android.util.LongArray; import android.util.LongSparseArray; @@ -26,8 +28,11 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.AndroidBuildClassifier; import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.CompatibilityChangeInfo; +import com.android.internal.compat.IOverrideValidator; +import com.android.internal.compat.OverrideAllowedState; import com.android.server.compat.config.Change; import com.android.server.compat.config.XmlParser; @@ -54,22 +59,14 @@ final class CompatConfig { private static final String TAG = "CompatConfig"; - private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib( - Environment.buildPath( - Environment.getRootDirectory(), "etc", "compatconfig")); - @GuardedBy("mChanges") private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); - @VisibleForTesting - CompatConfig() { - } + private IOverrideValidator mOverrideValidator; - /** - * @return The static instance of this class to be used within the system server. - */ - static CompatConfig get() { - return sInstance; + @VisibleForTesting + CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) { + mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this); } /** @@ -159,8 +156,12 @@ final class CompatConfig { * @param enabled If the change should be enabled or disabled. * @return {@code true} if the change existed before adding the override. */ - boolean addOverride(long changeId, String packageName, boolean enabled) { + boolean addOverride(long changeId, String packageName, boolean enabled) + throws RemoteException, SecurityException { boolean alreadyKnown = true; + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(changeId, packageName); + allowedState.enforce(changeId, packageName); synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c == null) { @@ -186,6 +187,20 @@ final class CompatConfig { } /** + * Returns the minimum sdk version for which this change should be enabled (or 0 if it is not + * target sdk gated). + */ + int minTargetSdkForChangeId(long changeId) { + synchronized (mChanges) { + CompatChange c = mChanges.get(changeId); + if (c == null) { + return 0; + } + return c.getEnableAfterTargetSdk(); + } + } + + /** * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This * restores the default behaviour for the given change and app, once any app processes have been * restarted. @@ -194,34 +209,44 @@ final class CompatConfig { * @param packageName The app package name that was overridden. * @return {@code true} if an override existed; */ - boolean removeOverride(long changeId, String packageName) { + boolean removeOverride(long changeId, String packageName) + throws RemoteException, SecurityException { boolean overrideExists = false; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); - if (c != null) { - overrideExists = true; - c.removePackageOverride(packageName); + try { + if (c != null) { + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(changeId, packageName); + allowedState.enforce(changeId, packageName); + overrideExists = true; + c.removePackageOverride(packageName); + } + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new RuntimeException("Unable to call override validator!", e); } } return overrideExists; } /** - * Overrides the enabled state for a given change and app. This method is intended to be used - * *only* for debugging purposes. + * Overrides the enabled state for a given change and app. * * <p>Note, package overrides are not persistent and will be lost on system or runtime restart. * * @param overrides list of overrides to default changes config. * @param packageName app for which the overrides will be applied. */ - void addOverrides(CompatibilityChangeConfig overrides, String packageName) { + void addOverrides(CompatibilityChangeConfig overrides, String packageName) + throws RemoteException, SecurityException { synchronized (mChanges) { for (Long changeId : overrides.enabledChanges()) { addOverride(changeId, packageName, true); } for (Long changeId : overrides.disabledChanges()) { addOverride(changeId, packageName, false); + } } } @@ -235,10 +260,22 @@ final class CompatConfig { * * @param packageName The package for which the overrides should be purged. */ - void removePackageOverrides(String packageName) { + void removePackageOverrides(String packageName) throws RemoteException, SecurityException { synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { - mChanges.valueAt(i).removePackageOverride(packageName); + try { + CompatChange change = mChanges.valueAt(i); + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(change.getId(), + packageName); + allowedState.enforce(change.getId(), packageName); + if (change != null) { + mChanges.valueAt(i).removePackageOverride(packageName); + } + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new RuntimeException("Unable to call override validator!", e); + } } } } @@ -326,17 +363,23 @@ final class CompatConfig { } } - CompatConfig initConfigFromLib(File libraryDir) { + static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) { + CompatConfig config = new CompatConfig(androidBuildClassifier, context); + config.initConfigFromLib(Environment.buildPath( + Environment.getRootDirectory(), "etc", "compatconfig")); + return config; + } + + void initConfigFromLib(File libraryDir) { if (!libraryDir.exists() || !libraryDir.isDirectory()) { Slog.e(TAG, "No directory " + libraryDir + ", skipping"); - return this; + return; } for (File f : libraryDir.listFiles()) { Slog.d(TAG, "Found a config file: " + f.getPath()); //TODO(b/138222363): Handle duplicate ids across config files. readConfig(f); } - return this; } private void readConfig(File configFile) { @@ -350,4 +393,7 @@ final class CompatConfig { } } + IOverrideValidator getOverrideValidator() { + return mOverrideValidator; + } } diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java new file mode 100644 index 000000000000..dfc00806992b --- /dev/null +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -0,0 +1,94 @@ +/* + * 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.compat; + +import static com.android.internal.compat.OverrideAllowedState.ALLOWED; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; +import static com.android.internal.compat.OverrideAllowedState.PACKAGE_DOES_NOT_EXIST; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.AndroidBuildClassifier; +import com.android.internal.compat.IOverrideValidator; +import com.android.internal.compat.OverrideAllowedState; + +/** + * Implementation of the policy for allowing compat change overrides. + */ +public class OverrideValidatorImpl extends IOverrideValidator.Stub { + + private AndroidBuildClassifier mAndroidBuildClassifier; + private Context mContext; + private CompatConfig mCompatConfig; + + @VisibleForTesting + OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier, + Context context, CompatConfig config) { + mAndroidBuildClassifier = androidBuildClassifier; + mContext = context; + mCompatConfig = config; + } + + @Override + public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) { + boolean debuggableBuild = false; + boolean finalBuild = false; + + debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); + finalBuild = mAndroidBuildClassifier.isFinalBuild(); + + // Allow any override for userdebug or eng builds. + if (debuggableBuild) { + return new OverrideAllowedState(ALLOWED, -1, -1); + } + PackageManager packageManager = mContext.getPackageManager(); + if (packageManager == null) { + throw new IllegalStateException("No PackageManager!"); + } + ApplicationInfo applicationInfo; + try { + applicationInfo = packageManager.getApplicationInfo(packageName, 0); + } catch (NameNotFoundException e) { + return new OverrideAllowedState(PACKAGE_DOES_NOT_EXIST, -1, -1); + } + int appTargetSdk = applicationInfo.targetSdkVersion; + // Only allow overriding debuggable apps. + if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { + return new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1); + } + int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); + // Do not allow overriding non-target sdk gated changes on user builds + if (minTargetSdk == -1) { + return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk); + } + // Allow overriding any change for debuggable apps on non-final builds. + if (!finalBuild) { + return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); + } + // Only allow to opt-in for a targetSdk gated change. + if (applicationInfo.targetSdkVersion < minTargetSdk) { + return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); + } + return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk); + } +} diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 6ec4b9fbdb93..bb8b12e86e16 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -27,9 +27,12 @@ import android.os.UserHandle; import android.util.Slog; import android.util.StatsLog; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.AndroidBuildClassifier; import com.android.internal.compat.ChangeReporter; import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.CompatibilityChangeInfo; +import com.android.internal.compat.IOverrideValidator; import com.android.internal.compat.IPlatformCompat; import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; @@ -46,11 +49,21 @@ public class PlatformCompat extends IPlatformCompat.Stub { private final Context mContext; private final ChangeReporter mChangeReporter; + private final CompatConfig mCompatConfig; public PlatformCompat(Context context) { mContext = context; mChangeReporter = new ChangeReporter( StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER); + mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext); + } + + @VisibleForTesting + PlatformCompat(Context context, CompatConfig compatConfig) { + mContext = context; + mChangeReporter = new ChangeReporter( + StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER); + mCompatConfig = compatConfig; } @Override @@ -75,7 +88,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { - if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) { + if (mCompatConfig.isChangeEnabled(changeId, appInfo)) { reportChange(changeId, appInfo.uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED); return true; @@ -122,57 +135,59 @@ public class PlatformCompat extends IPlatformCompat.Stub { * otherwise. */ public boolean registerListener(long changeId, CompatChange.ChangeListener listener) { - return CompatConfig.get().registerListener(changeId, listener); + return mCompatConfig.registerListener(changeId, listener); } @Override - public void setOverrides(CompatibilityChangeConfig overrides, String packageName) { - CompatConfig.get().addOverrides(overrides, packageName); + public void setOverrides(CompatibilityChangeConfig overrides, String packageName) + throws RemoteException, SecurityException { + mCompatConfig.addOverrides(overrides, packageName); killPackage(packageName); } @Override - public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) { - CompatConfig.get().addOverrides(overrides, packageName); + public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) + throws RemoteException, SecurityException { + mCompatConfig.addOverrides(overrides, packageName); } @Override - public void clearOverrides(String packageName) { - CompatConfig config = CompatConfig.get(); - config.removePackageOverrides(packageName); + public void clearOverrides(String packageName) throws RemoteException, SecurityException { + mCompatConfig.removePackageOverrides(packageName); killPackage(packageName); } @Override - public void clearOverridesForTest(String packageName) { - CompatConfig config = CompatConfig.get(); - config.removePackageOverrides(packageName); + public void clearOverridesForTest(String packageName) + throws RemoteException, SecurityException { + mCompatConfig.removePackageOverrides(packageName); } @Override - public boolean clearOverride(long changeId, String packageName) { - boolean existed = CompatConfig.get().removeOverride(changeId, packageName); + public boolean clearOverride(long changeId, String packageName) + throws RemoteException, SecurityException { + boolean existed = mCompatConfig.removeOverride(changeId, packageName); killPackage(packageName); return existed; } @Override public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { - return CompatConfig.get().getAppConfig(appInfo); + return mCompatConfig.getAppConfig(appInfo); } @Override public CompatibilityChangeInfo[] listAllChanges() { - return CompatConfig.get().dumpChanges(); + return mCompatConfig.dumpChanges(); } /** * Check whether the change is known to the compat config. - * @param changeId + * * @return {@code true} if the change is known. */ public boolean isKnownChangeId(long changeId) { - return CompatConfig.get().isKnownChangeId(changeId); + return mCompatConfig.isKnownChangeId(changeId); } @@ -182,11 +197,11 @@ public class PlatformCompat extends IPlatformCompat.Stub { * * @param appInfo The app in question * @return A sorted long array of change IDs. We use a primitive array to minimize memory - * footprint: Every app process will store this array statically so we aim to reduce - * overhead as much as possible. + * footprint: Every app process will store this array statically so we aim to reduce + * overhead as much as possible. */ public long[] getDisabledChanges(ApplicationInfo appInfo) { - return CompatConfig.get().getDisabledChanges(appInfo); + return mCompatConfig.getDisabledChanges(appInfo); } /** @@ -196,18 +211,24 @@ public class PlatformCompat extends IPlatformCompat.Stub { * @return The change ID, or {@code -1} if no change with that name exists. */ public long lookupChangeId(String name) { - return CompatConfig.get().lookupChangeId(name); + return mCompatConfig.lookupChangeId(name); } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; - CompatConfig.get().dumpConfig(pw); + mCompatConfig.dumpConfig(pw); + } + + @Override + public IOverrideValidator getOverrideValidator() { + return mCompatConfig.getOverrideValidator(); } /** * Clears information stored about events reported on behalf of an app. * To be called once upon app start or end. A second call would be a no-op. + * * @param appInfo the app to reset */ public void resetReporting(ApplicationInfo appInfo) { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index e39d6d5389e3..d7ae5b5c91bd 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1305,13 +1305,21 @@ public final class DisplayManagerService extends SystemService { } private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId) { - final IBinder token = getDisplayToken(displayId); - if (token == null) { - return null; + synchronized (mSyncRoot) { + final IBinder token = getDisplayToken(displayId); + if (token == null) { + return null; + } + final LogicalDisplay logicalDisplay = mLogicalDisplays.get(displayId); + if (logicalDisplay == null) { + return null; + } + + final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked(); + return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(), + displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(), + false /* useIdentityTransform */, 0 /* rotation */); } - return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe( - token, new Rect(), 0 /* width */, 0 /* height */, - false /* useIdentityTransform */, 0 /* rotation */); } @VisibleForTesting diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index ad728c18dd59..8498dcb32eb1 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -237,15 +237,15 @@ public class DisplayModeDirector { lowestConsideredPriority++; } - int defaultModeId = defaultMode.getModeId(); + int baseModeId = defaultMode.getModeId(); if (availableModes.length > 0) { - defaultModeId = availableModes[0]; + baseModeId = availableModes[0]; } // 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 defaultModeId. + // stored as baseModeId. return new DesiredDisplayModeSpecs( - defaultModeId, new RefreshRateRange(minRefreshRate, maxRefreshRate)); + baseModeId, new RefreshRateRange(minRefreshRate, maxRefreshRate)); } } @@ -526,7 +526,7 @@ public class DisplayModeDirector { } /** - * Information about the desired display mode to be set by the system. Includes the default + * Information about the desired display mode to be set by the system. Includes the base * mode ID and refresh rate range. * * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the @@ -535,10 +535,10 @@ public class DisplayModeDirector { */ public static final class DesiredDisplayModeSpecs { /** - * Default mode ID. This is what system defaults to for all other settings, or + * Base mode ID. This is what system defaults to for all other settings, or * if the refresh rate range is not available. */ - public int defaultModeId; + public int baseModeId; /** * The refresh rate range. */ @@ -548,9 +548,8 @@ public class DisplayModeDirector { refreshRateRange = new RefreshRateRange(); } - public DesiredDisplayModeSpecs( - int defaultModeId, @NonNull RefreshRateRange refreshRateRange) { - this.defaultModeId = defaultModeId; + public DesiredDisplayModeSpecs(int baseModeId, @NonNull RefreshRateRange refreshRateRange) { + this.baseModeId = baseModeId; this.refreshRateRange = refreshRateRange; } @@ -559,7 +558,7 @@ public class DisplayModeDirector { */ @Override public String toString() { - return String.format("defaultModeId=%d min=%.0f max=%.0f", defaultModeId, + return String.format("baseModeId=%d min=%.0f max=%.0f", baseModeId, refreshRateRange.min, refreshRateRange.max); } /** @@ -577,7 +576,7 @@ public class DisplayModeDirector { DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other; - if (defaultModeId != desiredDisplayModeSpecs.defaultModeId) { + if (baseModeId != desiredDisplayModeSpecs.baseModeId) { return false; } if (!refreshRateRange.equals(desiredDisplayModeSpecs.refreshRateRange)) { @@ -588,14 +587,14 @@ public class DisplayModeDirector { @Override public int hashCode() { - return Objects.hash(defaultModeId, refreshRateRange); + return Objects.hash(baseModeId, refreshRateRange); } /** * Copy values from the other object. */ public void copyFrom(DesiredDisplayModeSpecs other) { - defaultModeId = other.defaultModeId; + baseModeId = other.baseModeId; refreshRateRange.min = other.refreshRateRange.min; refreshRateRange.max = other.refreshRateRange.max; } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index cf94d4695497..fb8a4193286b 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -272,14 +272,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { // 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 = + if (mDisplayModeSpecs.baseModeId != 0) { + int activeBaseMode = 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 + if (activeBaseMode != 0) { + if (mDisplayModeSpecs.baseModeId != activeBaseMode || mDisplayModeSpecs.refreshRateRange.min != physicalDisplayConfigSpecs.minRefreshRate || mDisplayModeSpecs.refreshRateRange.max @@ -312,14 +312,14 @@ 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) { + // Determine whether the display mode specs' base mode is still there. + if (mSupportedModes.indexOfKey(mDisplayModeSpecs.baseModeId) < 0) { + if (mDisplayModeSpecs.baseModeId != 0) { Slog.w(TAG, - "DisplayModeSpecs default mode no longer available, using currently" - + " active mode as default."); + "DisplayModeSpecs base mode no longer available, using currently" + + " active mode."); } - mDisplayModeSpecs.defaultModeId = activeRecord.mMode.getModeId(); + mDisplayModeSpecs.baseModeId = activeRecord.mMode.getModeId(); mDisplayModeSpecsInvalid = true; } @@ -648,13 +648,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public void setDesiredDisplayModeSpecsLocked( DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) { - if (displayModeSpecs.defaultModeId == 0) { + if (displayModeSpecs.baseModeId == 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) { + int basePhysIndex = findDisplayInfoIndexLocked(displayModeSpecs.baseModeId); + if (basePhysIndex < 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 @@ -662,8 +662,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { // 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); + "Ignoring request for invalid base mode id " + displayModeSpecs.baseModeId); updateDeviceInfoLocked(); return; } @@ -673,7 +672,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { getHandler().sendMessage(PooledLambda.obtainMessage( LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this, getDisplayTokenLocked(), - new SurfaceControl.DesiredDisplayConfigSpecs(defaultPhysIndex, + new SurfaceControl.DesiredDisplayConfigSpecs(basePhysIndex, mDisplayModeSpecs.refreshRateRange.min, mDisplayModeSpecs.refreshRateRange.max))); } diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index b6255d15795e..c8e5f6c8f53b 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -317,7 +317,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { @Override public void setDesiredDisplayModeSpecsLocked( DisplayModeDirector.DesiredDisplayModeSpecs displayModeSpecs) { - final int id = displayModeSpecs.defaultModeId; + final int id = displayModeSpecs.baseModeId; int index = -1; if (id == 0) { // Use the default. diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index ae6d63adc7e7..90358ca1f133 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -83,11 +83,11 @@ import android.view.ViewConfiguration; import android.widget.Toast; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; -import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.DisplayThread; import com.android.server.LocalServices; @@ -186,6 +186,9 @@ public class InputManagerService extends IInputManager.Stub // The associations of input devices to displays by port. Maps from input device port (String) // to display id (int). Currently only accessed by InputReader. private final Map<String, Integer> mStaticAssociations; + private final Object mAssociationsLock = new Object(); + @GuardedBy("mAssociationLock") + private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>(); private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); @@ -240,6 +243,7 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon); private static native void nativeSetPointerCapture(long ptr, boolean detached); private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId); + private static native void nativeNotifyPortAssociationsChanged(long ptr); // Input event injection constants defined in InputDispatcher.h. private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0; @@ -1723,6 +1727,49 @@ public class InputManagerService extends IInputManager.Stub nativeSetCustomPointerIcon(mPtr, icon); } + /** + * Add a runtime association between the input port and the display port. This overrides any + * static associations. + * @param inputPort The port of the input device. + * @param displayPort The physical port of the associated display. + */ + @Override // Binder call + public void addPortAssociation(@NonNull String inputPort, int displayPort) { + if (!checkCallingPermission( + android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, + "addPortAssociation()")) { + throw new SecurityException( + "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); + } + + Objects.requireNonNull(inputPort); + synchronized (mAssociationsLock) { + mRuntimeAssociations.put(inputPort, displayPort); + } + nativeNotifyPortAssociationsChanged(mPtr); + } + + /** + * Remove the runtime association between the input port and the display port. Any existing + * static association for the cleared input port will be restored. + * @param inputPort The port of the input device to be cleared. + */ + @Override // Binder call + public void removePortAssociation(@NonNull String inputPort) { + if (!checkCallingPermission( + android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, + "clearPortAssociations()")) { + throw new SecurityException( + "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); + } + + Objects.requireNonNull(inputPort); + synchronized (mAssociationsLock) { + mRuntimeAssociations.remove(inputPort); + } + nativeNotifyPortAssociationsChanged(mPtr); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -1743,6 +1790,16 @@ public class InputManagerService extends IInputManager.Stub pw.println(" display: " + v); }); } + + synchronized (mAssociationsLock) { + if (!mRuntimeAssociations.isEmpty()) { + pw.println("Runtime Associations:"); + mRuntimeAssociations.forEach((k, v) -> { + pw.print(" port: " + k); + pw.println(" display: " + v); + }); + } + } } private boolean checkCallingPermission(String permission, String func) { @@ -1766,6 +1823,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void monitor() { synchronized (mInputFilterLock) { } + synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */} nativeMonitor(mPtr); } @@ -1930,7 +1988,7 @@ public class InputManagerService extends IInputManager.Stub * @return Flattened list */ private static List<String> flatten(@NonNull Map<String, Integer> map) { - List<String> list = new ArrayList<>(map.size() * 2); + final List<String> list = new ArrayList<>(map.size() * 2); map.forEach((k, v)-> { list.add(k); list.add(v.toString()); @@ -1943,11 +2001,11 @@ public class InputManagerService extends IInputManager.Stub * directory. */ private static Map<String, Integer> loadStaticInputPortAssociations() { - File baseDir = Environment.getVendorDirectory(); - File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); + final File baseDir = Environment.getVendorDirectory(); + final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); try { - InputStream stream = new FileInputStream(confFile); + final InputStream stream = new FileInputStream(confFile); return ConfigurationProcessor.processInputPortAssociations(stream); } catch (FileNotFoundException e) { // Most of the time, file will not exist, which is expected. @@ -1960,7 +2018,14 @@ public class InputManagerService extends IInputManager.Stub // Native callback private String[] getInputPortAssociations() { - List<String> associationList = flatten(mStaticAssociations); + final Map<String, Integer> associations = new HashMap<>(mStaticAssociations); + + // merge the runtime associations. + synchronized (mAssociationsLock) { + associations.putAll(mRuntimeAssociations); + } + + final List<String> associationList = flatten(associations); return associationList.toArray(new String[0]); } diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java index f90fab4666d6..17a4b9c6c170 100644 --- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java +++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java @@ -25,6 +25,7 @@ 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.RuleIndexRange; import com.android.server.integrity.parser.RuleIndexingController; import com.android.server.integrity.parser.RuleMetadataParser; import com.android.server.integrity.parser.RuleParseException; @@ -38,6 +39,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -152,14 +154,19 @@ public class IntegrityFileManager { throws IOException, RuleParseException { synchronized (RULES_LOCK) { // Try to identify indexes from the index file. - List<List<Integer>> ruleReadingIndexes = - mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata); + List<RuleIndexRange> ruleReadingIndexes; + try { + ruleReadingIndexes = + mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata); + } catch (Exception e) { + Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e); + ruleReadingIndexes = Collections.emptyList(); + } - // Read the rules based on the index information. - // TODO(b/145493956): Provide the identified indexes to the rule reader. + // Read the rules based on the index information when available. try (FileInputStream inputStream = new FileInputStream(new File(mRulesDir, RULES_FILE))) { - List<Rule> rules = mRuleParser.parse(inputStream); + List<Rule> rules = mRuleParser.parse(inputStream, ruleReadingIndexes); return rules; } } diff --git a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java new file mode 100644 index 000000000000..e555e3e5746e --- /dev/null +++ b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 java.io.IOException; +import java.io.InputStream; + +/** + * An input stream that tracks the total number read bytes since construction and allows moving + * fast forward to a certain byte any time during the execution. + * + * This class is used for efficient reading of rules based on the rule indexing. + */ +public class BitTrackedInputStream extends BitInputStream { + + private static int sReadBitsCount; + + /** Constructor with byte array. */ + public BitTrackedInputStream(byte[] inputStream) { + super(inputStream); + sReadBitsCount = 0; + } + + /** Constructor with input stream. */ + public BitTrackedInputStream(InputStream inputStream) { + super(inputStream); + sReadBitsCount = 0; + } + + /** Obtains an integer value of the next {@code numOfBits}. */ + @Override + public int getNext(int numOfBits) throws IOException { + sReadBitsCount += numOfBits; + return super.getNext(numOfBits); + } + + /** Returns the current cursor position showing the number of bits that are read. */ + public int getReadBitsCount() { + return sReadBitsCount; + } + + /** + * Sets the cursor to the specified byte location. + * + * Note that the integer parameter specifies the location in bytes -- not bits. + */ + public void setCursorToByteLocation(int byteLocation) throws IOException { + int bitCountToRead = byteLocation * 8 - sReadBitsCount; + if (bitCountToRead < 0) { + throw new IllegalStateException("The byte position is already read."); + } + super.getNext(bitCountToRead); + sReadBitsCount = byteLocation * 8; + } +} diff --git a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java index 62815a9806ee..f575599e1c49 100644 --- a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java +++ b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.integrity.serializer; +package com.android.server.integrity.model; import java.io.IOException; import java.io.OutputStream; diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index cbb6e4e8e06f..e744326c49db 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -37,71 +37,103 @@ import android.content.integrity.CompoundFormula; import android.content.integrity.Formula; import android.content.integrity.Rule; -import com.android.server.integrity.model.BitInputStream; +import com.android.server.integrity.model.BitTrackedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** A helper class to parse rules into the {@link Rule} model from Binary representation. */ public class RuleBinaryParser implements RuleParser { - private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); - @Override public List<Rule> parse(byte[] ruleBytes) throws RuleParseException { try { - BitInputStream bitInputStream = new BitInputStream(ruleBytes); - return parseRules(bitInputStream); + BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(ruleBytes); + return parseRules(bitTrackedInputStream, /* indexRanges= */ Collections.emptyList()); } catch (Exception e) { throw new RuleParseException(e.getMessage(), e); } } @Override - public List<Rule> parse(InputStream inputStream) throws RuleParseException { + public List<Rule> parse(InputStream inputStream, List<RuleIndexRange> indexRanges) + throws RuleParseException { try { - BitInputStream bitInputStream = new BitInputStream(inputStream); - return parseRules(bitInputStream); + BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(inputStream); + return parseRules(bitTrackedInputStream, indexRanges); } catch (Exception e) { throw new RuleParseException(e.getMessage(), e); } } - private List<Rule> parseRules(BitInputStream bitInputStream) throws IOException { - List<Rule> parsedRules = new ArrayList<>(); + private List<Rule> parseRules( + BitTrackedInputStream bitTrackedInputStream, + List<RuleIndexRange> indexRanges) + throws IOException { // Read the rule binary file format version. - bitInputStream.getNext(FORMAT_VERSION_BITS); + bitTrackedInputStream.getNext(FORMAT_VERSION_BITS); + + return indexRanges.isEmpty() + ? parseAllRules(bitTrackedInputStream) + : parseIndexedRules(bitTrackedInputStream, indexRanges); + } + + private List<Rule> parseAllRules(BitTrackedInputStream bitTrackedInputStream) + throws IOException { + List<Rule> parsedRules = new ArrayList<>(); + + while (bitTrackedInputStream.hasNext()) { + if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) { + parsedRules.add(parseRule(bitTrackedInputStream)); + } + } + + return parsedRules; + } + + private List<Rule> parseIndexedRules( + BitTrackedInputStream bitTrackedInputStream, List<RuleIndexRange> indexRanges) + throws IOException { + List<Rule> parsedRules = new ArrayList<>(); + + for (RuleIndexRange range : indexRanges) { + // Skip the rules that are not in the range. + bitTrackedInputStream.setCursorToByteLocation(range.getStartIndex()); - while (bitInputStream.hasNext()) { - if (bitInputStream.getNext(SIGNAL_BIT) == 1) { - parsedRules.add(parseRule(bitInputStream)); + // Read the rules until we reach the end index. + while (bitTrackedInputStream.hasNext() + && bitTrackedInputStream.getReadBitsCount() < range.getEndIndex()) { + if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) { + parsedRules.add(parseRule(bitTrackedInputStream)); + } } } return parsedRules; } - private Rule parseRule(BitInputStream bitInputStream) throws IOException { - Formula formula = parseFormula(bitInputStream); - int effect = bitInputStream.getNext(EFFECT_BITS); + private Rule parseRule(BitTrackedInputStream bitTrackedInputStream) throws IOException { + Formula formula = parseFormula(bitTrackedInputStream); + int effect = bitTrackedInputStream.getNext(EFFECT_BITS); - if (bitInputStream.getNext(SIGNAL_BIT) != 1) { + if (bitTrackedInputStream.getNext(SIGNAL_BIT) != 1) { throw new IllegalArgumentException("A rule must end with a '1' bit."); } return new Rule(formula, effect); } - private Formula parseFormula(BitInputStream bitInputStream) throws IOException { - int separator = bitInputStream.getNext(SEPARATOR_BITS); + private Formula parseFormula(BitTrackedInputStream bitTrackedInputStream) throws IOException { + int separator = bitTrackedInputStream.getNext(SEPARATOR_BITS); switch (separator) { case ATOMIC_FORMULA_START: - return parseAtomicFormula(bitInputStream); + return parseAtomicFormula(bitTrackedInputStream); case COMPOUND_FORMULA_START: - return parseCompoundFormula(bitInputStream); + return parseCompoundFormula(bitTrackedInputStream); case COMPOUND_FORMULA_END: return null; default: @@ -110,37 +142,40 @@ public class RuleBinaryParser implements RuleParser { } } - private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException { - int connector = bitInputStream.getNext(CONNECTOR_BITS); + private CompoundFormula parseCompoundFormula(BitTrackedInputStream bitTrackedInputStream) + throws IOException { + int connector = bitTrackedInputStream.getNext(CONNECTOR_BITS); List<Formula> formulas = new ArrayList<>(); - Formula parsedFormula = parseFormula(bitInputStream); + Formula parsedFormula = parseFormula(bitTrackedInputStream); while (parsedFormula != null) { formulas.add(parsedFormula); - parsedFormula = parseFormula(bitInputStream); + parsedFormula = parseFormula(bitTrackedInputStream); } return new CompoundFormula(connector, formulas); } - private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException { - int key = bitInputStream.getNext(KEY_BITS); - int operator = bitInputStream.getNext(OPERATOR_BITS); + private AtomicFormula parseAtomicFormula(BitTrackedInputStream bitTrackedInputStream) + throws IOException { + int key = bitTrackedInputStream.getNext(KEY_BITS); + int operator = bitTrackedInputStream.getNext(OPERATOR_BITS); switch (key) { case AtomicFormula.PACKAGE_NAME: case AtomicFormula.APP_CERTIFICATE: case AtomicFormula.INSTALLER_NAME: case AtomicFormula.INSTALLER_CERTIFICATE: - boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1; - int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS); - String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue); + boolean isHashedValue = bitTrackedInputStream.getNext(IS_HASHED_BITS) == 1; + int valueSize = bitTrackedInputStream.getNext(VALUE_SIZE_BITS); + String stringValue = getStringValue(bitTrackedInputStream, valueSize, + isHashedValue); return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue); case AtomicFormula.VERSION_CODE: - int intValue = getIntValue(bitInputStream); + int intValue = getIntValue(bitTrackedInputStream); return new AtomicFormula.IntAtomicFormula(key, operator, intValue); case AtomicFormula.PRE_INSTALLED: - boolean booleanValue = getBooleanValue(bitInputStream); + boolean booleanValue = getBooleanValue(bitTrackedInputStream); return new AtomicFormula.BooleanAtomicFormula(key, booleanValue); default: throw new IllegalArgumentException(String.format("Unknown key: %d", key)); diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java new file mode 100644 index 000000000000..8c8450e5fdc4 --- /dev/null +++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + +/** + * A wrapper class to represent an indexing range that is identified by the {@link + * RuleIndexingController}. + */ +public class RuleIndexRange { + private static int sStartIndex; + private static int sEndIndex; + + /** Constructor with start and end indexes. */ + public RuleIndexRange(int startIndex, int endIndex) { + this.sStartIndex = startIndex; + this.sEndIndex = endIndex; + } + + /** Returns the startIndex. */ + public int getStartIndex() { + return sStartIndex; + } + + /** Returns the end index. */ + public int getEndIndex() { + return sEndIndex; + } + + @Override + public boolean equals(@Nullable Object object) { + return sStartIndex == ((RuleIndexRange) object).getStartIndex() + && sEndIndex == ((RuleIndexRange) object).getEndIndex(); + } +} diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java index b642fa64baaf..c9713220d6e8 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java +++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java @@ -17,6 +17,7 @@ package com.android.server.integrity.parser; import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY; +import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY; import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue; import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue; @@ -24,25 +25,27 @@ import android.content.integrity.AppInstallMetadata; import com.android.server.integrity.model.BitInputStream; -import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; -import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; /** Helper class to identify the necessary indexes that needs to be read. */ public class RuleIndexingController { - private static TreeMap<String, Integer> sPackageNameBasedIndexes; - private static TreeMap<String, Integer> sAppCertificateBasedIndexes; - private static TreeMap<String, Integer> sUnindexedRuleIndexes; + private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes; + private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes; + private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes; /** * Provide the indexing file to read and the object will be constructed by reading and * identifying the indexes. */ - public RuleIndexingController(FileInputStream fileInputStream) throws IOException { - BitInputStream bitInputStream = new BitInputStream(fileInputStream); + public RuleIndexingController(InputStream inputStream) throws IOException { + BitInputStream bitInputStream = new BitInputStream(inputStream); sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream); sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream); sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream); @@ -52,24 +55,57 @@ public class RuleIndexingController { * Returns a list of integers with the starting and ending bytes of the rules that needs to be * read and evaluated. */ - public List<List<Integer>> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) { - // TODO(b/145493956): Identify and return the indexes that needs to be read. - return new ArrayList<>(); + public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) { + ArrayList<RuleIndexRange> indexRanges = new ArrayList(); + + // Add the range for package name indexes rules. + indexRanges.add( + searchIndexingKeysRangeContainingKey( + sPackageNameBasedIndexes, appInstallMetadata.getPackageName())); + + // Add the range for app certificate indexes rules. + indexRanges.add( + searchIndexingKeysRangeContainingKey( + sAppCertificateBasedIndexes, appInstallMetadata.getAppCertificate())); + + // Add the range for unindexed rules. + indexRanges.add( + new RuleIndexRange( + sUnindexedRuleIndexes.get(START_INDEXING_KEY), + sUnindexedRuleIndexes.get(END_INDEXING_KEY))); + + return indexRanges; } - private TreeMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream) + private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream) throws IOException { - TreeMap<String, Integer> keyToIndexMap = new TreeMap<>(); + LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>(); while (bitInputStream.hasNext()) { String key = getStringValue(bitInputStream); int value = getIntValue(bitInputStream); keyToIndexMap.put(key, value); - if (key == END_INDEXING_KEY) { + if (key.matches(END_INDEXING_KEY)) { break; } } return keyToIndexMap; } + + private RuleIndexRange searchIndexingKeysRangeContainingKey( + LinkedHashMap<String, Integer> indexMap, String searchedKey) { + TreeSet<String> keyTreeSet = + indexMap.keySet().stream() + .filter(key -> !key.matches(START_INDEXING_KEY) && !key.matches( + END_INDEXING_KEY)) + .collect(Collectors.toCollection(TreeSet::new)); + + String minIndex = keyTreeSet.floor(searchedKey); + String maxIndex = keyTreeSet.ceiling(searchedKey); + + return new RuleIndexRange( + indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex), + indexMap.get(maxIndex == null ? END_INDEXING_KEY : maxIndex)); + } } diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java index 81783d5c7324..a8e9f6134759 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleParser.java @@ -28,5 +28,6 @@ public interface RuleParser { List<Rule> parse(byte[] ruleBytes) throws RuleParseException; /** Parse rules from an input stream. */ - List<Rule> parse(InputStream inputStream) throws RuleParseException; + List<Rule> parse(InputStream inputStream, List<RuleIndexRange> ruleIndexRanges) + throws RuleParseException; } diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java index d405583442bd..497be8424286 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java @@ -62,7 +62,8 @@ public final class RuleXmlParser implements RuleParser { } @Override - public List<Rule> parse(InputStream inputStream) throws RuleParseException { + public List<Rule> parse(InputStream inputStream, List<RuleIndexRange> indexRanges) + throws RuleParseException { try { XmlPullParser xmlPullParser = Xml.newPullParser(); xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name()); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index b8791c3c3489..f964d4cf2724 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -42,6 +42,7 @@ import android.content.integrity.Rule; import com.android.internal.util.Preconditions; import com.android.server.integrity.IntegrityUtils; import com.android.server.integrity.model.BitOutputStream; +import com.android.server.integrity.model.ByteTrackedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -109,7 +110,8 @@ public class RuleBinarySerializer implements RuleSerializer { } private void serializeRuleFileMetadata(Optional<Integer> formatVersion, - ByteTrackedOutputStream outputStream) throws IOException { + ByteTrackedOutputStream outputStream) + throws IOException { int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION); BitOutputStream bitOutputStream = new BitOutputStream(); diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java index ccfc98e2291b..ed6a759409d4 100644 --- a/services/core/java/com/android/server/location/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java @@ -16,11 +16,11 @@ package com.android.server.location; +import android.annotation.Nullable; import android.content.Context; import android.location.Location; import android.os.Binder; import android.os.Bundle; -import android.os.WorkSource; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; @@ -29,127 +29,336 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.UnaryOperator; /** - * Location Manager's interface for location providers. Always starts as disabled. + * Base class for all location providers. * * @hide */ public abstract class AbstractLocationProvider { /** - * Interface for communicating from a location provider back to the location service. + * Interface for listening to location providers. */ - public interface LocationProviderManager { + public interface Listener { /** - * May be called to inform the location service of a change in this location provider's - * enabled/disabled state. + * Called when a provider's state changes. May be invoked from any thread. Will be + * invoked with a cleared binder identity. */ - void onSetEnabled(boolean enabled); + void onStateChanged(State oldState, State newState); /** - * May be called to inform the location service of a change in this location provider's - * properties. + * Called when a provider has a new location available. May be invoked from any thread. Will + * be invoked with a cleared binder identity. */ - void onSetProperties(ProviderProperties properties); + void onReportLocation(Location location); /** - * May be called to inform the location service that this provider has a new location - * available. + * Called when a provider has a new location available. May be invoked from any thread. Will + * be invoked with a cleared binder identity. */ - void onReportLocation(Location location); + void onReportLocation(List<Location> locations); + } + + /** + * Holds a representation of the public state of a provider. + */ + public static final class State { /** - * May be called to inform the location service that this provider has a new location - * available. + * Default state value for a location provider that is disabled with no properties and an + * empty provider package list. */ - void onReportLocation(List<Location> locations); + public static final State EMPTY_STATE = new State(false, null, + Collections.emptySet()); + + /** + * The provider's enabled state. + */ + public final boolean enabled; + + /** + * The provider's properties. + */ + @Nullable public final ProviderProperties properties; + + /** + * The provider's package name list - provider packages may be afforded special privileges. + */ + public final Set<String> providerPackageNames; + + private State(boolean enabled, ProviderProperties properties, + Set<String> providerPackageNames) { + this.enabled = enabled; + this.properties = properties; + this.providerPackageNames = Objects.requireNonNull(providerPackageNames); + } + + private State withEnabled(boolean enabled) { + if (enabled == this.enabled) { + return this; + } else { + return new State(enabled, properties, providerPackageNames); + } + } + + private State withProperties(ProviderProperties properties) { + if (properties.equals(this.properties)) { + return this; + } else { + return new State(enabled, properties, providerPackageNames); + } + } + + private State withProviderPackageNames(Set<String> providerPackageNames) { + if (providerPackageNames.equals(this.providerPackageNames)) { + return this; + } else { + return new State(enabled, properties, providerPackageNames); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof State)) { + return false; + } + State state = (State) o; + return enabled == state.enabled && properties == state.properties + && providerPackageNames.equals(state.providerPackageNames); + } + + @Override + public int hashCode() { + return Objects.hash(enabled, properties, providerPackageNames); + } + } + + // combines listener and state information so that they can be updated atomically with respect + // to each other and an ordering established. + private static class InternalState { + @Nullable public final Listener listener; + public final State state; + + private InternalState(@Nullable Listener listener, State state) { + this.listener = listener; + this.state = state; + } + + private InternalState withListener(Listener listener) { + if (listener == this.listener) { + return this; + } else { + return new InternalState(listener, state); + } + } + + private InternalState withState(State state) { + if (state.equals(this.state)) { + return this; + } else { + return new InternalState(listener, state); + } + } + + private InternalState withState(UnaryOperator<State> operator) { + return withState(operator.apply(state)); + } } protected final Context mContext; - private final LocationProviderManager mLocationProviderManager; + protected final Executor mExecutor; + + // we use a lock-free implementation to update state to ensure atomicity between updating the + // provider state and setting the listener, so that the state updates a listener sees are + // consistent with when the listener was set (a listener should not see any updates that occur + // before it was set, and should not miss any updates that occur after it was set). + private final AtomicReference<InternalState> mInternalState; - protected AbstractLocationProvider( - Context context, LocationProviderManager locationProviderManager) { + protected AbstractLocationProvider(Context context, Executor executor) { + this(context, executor, Collections.singleton(context.getPackageName())); + } + + protected AbstractLocationProvider(Context context, Executor executor, + Set<String> packageNames) { mContext = context; - mLocationProviderManager = locationProviderManager; + mExecutor = executor; + mInternalState = new AtomicReference<>( + new InternalState(null, State.EMPTY_STATE.withProviderPackageNames(packageNames))); } /** - * Call this method to report a change in provider enabled/disabled status. May be called from - * any thread. + * Sets the listener and returns the state at the moment the listener was set. The listener can + * expect to receive all state updates from after this point. */ - protected void setEnabled(boolean enabled) { - long identity = Binder.clearCallingIdentity(); - try { - mLocationProviderManager.onSetEnabled(enabled); - } finally { - Binder.restoreCallingIdentity(identity); + State setListener(@Nullable Listener listener) { + return mInternalState.updateAndGet( + internalState -> internalState.withListener(listener)).state; + } + + /** + * Retrieves the state of the provider. + */ + State getState() { + return mInternalState.get().state; + } + + /** + * Sets the state of the provider to the new state. + */ + void setState(State newState) { + InternalState oldInternalState = mInternalState.getAndUpdate( + internalState -> internalState.withState(newState)); + if (newState.equals(oldInternalState.state)) { + return; + } + + // we know that we only updated the state, so the listener for the old state is the same as + // the listener for the new state. + if (oldInternalState.listener != null) { + long identity = Binder.clearCallingIdentity(); + try { + oldInternalState.listener.onStateChanged(oldInternalState.state, newState); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + + private void setState(UnaryOperator<State> operator) { + InternalState oldInternalState = mInternalState.getAndUpdate( + internalState -> internalState.withState(operator)); + + // recreate the new state from our knowledge of the old state - unfortunately may result in + // an extra allocation, but oh well... + State newState = operator.apply(oldInternalState.state); + + if (newState.equals(oldInternalState.state)) { + return; + } + + // we know that we only updated the state, so the listener for the old state is the same as + // the listener for the new state. + if (oldInternalState.listener != null) { + long identity = Binder.clearCallingIdentity(); + try { + oldInternalState.listener.onStateChanged(oldInternalState.state, newState); + } finally { + Binder.restoreCallingIdentity(identity); + } } } /** - * Call this method to report a change in provider properties. May be called from - * any thread. + * The current enabled state of this provider. + */ + protected boolean isEnabled() { + return mInternalState.get().state.enabled; + } + + /** + * The current provider properties of this provider. + */ + @Nullable + protected ProviderProperties getProperties() { + return mInternalState.get().state.properties; + } + + /** + * The current package set of this provider. + */ + protected Set<String> getProviderPackages() { + return mInternalState.get().state.providerPackageNames; + } + + /** + * Call this method to report a change in provider enabled/disabled status. + */ + protected void setEnabled(boolean enabled) { + setState(state -> state.withEnabled(enabled)); + } + + /** + * Call this method to report a change in provider properties. */ protected void setProperties(ProviderProperties properties) { - long identity = Binder.clearCallingIdentity(); - try { - mLocationProviderManager.onSetProperties(properties); - } finally { - Binder.restoreCallingIdentity(identity); - } + setState(state -> state.withProperties(properties)); + } + + /** + * Call this method to report a change in provider packages. + */ + protected void setPackageNames(Set<String> packageNames) { + setState(state -> state.withProviderPackageNames(packageNames)); } /** - * Call this method to report a new location. May be called from any thread. + * Call this method to report a new location. */ protected void reportLocation(Location location) { - long identity = Binder.clearCallingIdentity(); - try { - mLocationProviderManager.onReportLocation(location); - } finally { - Binder.restoreCallingIdentity(identity); + Listener listener = mInternalState.get().listener; + if (listener != null) { + long identity = Binder.clearCallingIdentity(); + try { + listener.onReportLocation(location); + } finally { + Binder.restoreCallingIdentity(identity); + } } } /** - * Call this method to report a new location. May be called from any thread. + * Call this method to report a new location. */ protected void reportLocation(List<Location> locations) { - long identity = Binder.clearCallingIdentity(); - try { - mLocationProviderManager.onReportLocation(locations); - } finally { - Binder.restoreCallingIdentity(identity); + Listener listener = mInternalState.get().listener; + if (listener != null) { + long identity = Binder.clearCallingIdentity(); + try { + listener.onReportLocation(locations); + } finally { + Binder.restoreCallingIdentity(identity); + } } } /** - * Invoked by the location service to return a list of packages currently associated with this - * provider. May be called from any thread. + * Sets a new request and worksource for the provider. */ - public List<String> getProviderPackages() { - return Collections.singletonList(mContext.getPackageName()); + public final void setRequest(ProviderRequest request) { + // all calls into the provider must be moved onto the provider thread to prevent deadlock + mExecutor.execute(() -> onSetRequest(request)); } /** - * Invoked by the location service to deliver a new request for fulfillment to the provider. - * Replaces any previous requests completely. Will always be invoked from the location service - * thread with a cleared binder identity. + * Always invoked on the provider executor. */ - public abstract void onSetRequest(ProviderRequest request, WorkSource source); + protected abstract void onSetRequest(ProviderRequest request); + + /** + * Sends an extra command to the provider for it to interpret as it likes. + */ + public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) { + // all calls into the provider must be moved onto the provider thread to prevent deadlock + mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras)); + } /** - * Invoked by the location service to deliver a custom command to this provider. Will always be - * invoked from the location service thread with a cleared binder identity. + * Always invoked on the provider executor. */ - public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {} + protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {} /** - * Invoked by the location service to dump debug or log information. May be invoked from any - * thread. + * Dumps debug or log information. May be invoked from any thread. */ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args); } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index d8561b697caa..15cf190952d1 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -43,6 +43,7 @@ import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; @@ -113,8 +114,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private static final ProviderProperties PROPERTIES = new ProviderProperties( - true, true, false, false, true, true, true, - Criteria.POWER_HIGH, Criteria.ACCURACY_FINE); + /* requiresNetwork = */false, + /* requiresSatellite = */true, + /* requiresCell = */false, + /* hasMonetaryCost = */false, + /* supportAltitude = */true, + /* supportsSpeed = */true, + /* supportsBearing = */true, + Criteria.POWER_HIGH, + Criteria.ACCURACY_FINE); // these need to match GnssPositionMode enum in IGnss.hal private static final int GPS_POSITION_MODE_STANDALONE = 0; @@ -616,13 +624,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } - public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager, - Looper looper) { - super(context, locationProviderManager); + public GnssLocationProvider(Context context, Handler handler) { + super(context, new HandlerExecutor(handler)); ensureInitialized(); - mLooper = looper; + mLooper = handler.getLooper(); // Create a wake lock mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -639,7 +646,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0); mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context, - GnssLocationProvider.this::onNetworkAvailable, looper); + GnssLocationProvider.this::onNetworkAvailable, mLooper); // App ops service to keep track of who is accessing the GPS mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -649,7 +656,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements BatteryStats.SERVICE_NAME)); // Construct internal handler - mHandler = new ProviderHandler(looper); + mHandler = new ProviderHandler(mLooper); // Load GPS configuration and register listeners in the background: // some operations, such as opening files and registering broadcast receivers, can take a @@ -693,10 +700,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements }; mGnssMetrics = new GnssMetrics(mBatteryStats); - mNtpTimeHelper = new NtpTimeHelper(mContext, looper, this); + mNtpTimeHelper = new NtpTimeHelper(mContext, mLooper, this); GnssSatelliteBlacklistHelper gnssSatelliteBlacklistHelper = new GnssSatelliteBlacklistHelper(mContext, - looper, this); + mLooper, this); mHandler.post(gnssSatelliteBlacklistHelper::updateSatelliteBlacklist); mGnssBatchingProvider = new GnssBatchingProvider(); mGnssGeofenceProvider = new GnssGeofenceProvider(); @@ -1047,8 +1054,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } @Override - public void onSetRequest(ProviderRequest request, WorkSource source) { - sendMessage(SET_REQUEST, 0, new GpsRequest(request, source)); + public void onSetRequest(ProviderRequest request) { + sendMessage(SET_REQUEST, 0, new GpsRequest(request, request.workSource)); } private void handleSetRequest(ProviderRequest request, WorkSource source) { @@ -1185,7 +1192,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } @Override - public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) { + public void onExtraCommand(int uid, int pid, String command, Bundle extras) { long identity = Binder.clearCallingIdentity(); try { @@ -2064,10 +2071,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } /** - * This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager, - * Looper)}. - * It is in charge of loading properties and registering for events that will be posted to - * this handler. + * This method is bound to the constructor. It is in charge of loading properties and + * registering for events that will be posted to this handler. */ private void handleInitialize() { // class_init_native() already initializes the GNSS service handle during class loading. diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index 694f14904668..8a149afa6238 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -23,12 +23,13 @@ import android.content.Context; import android.content.pm.PackageManager; import android.location.Location; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; -import android.os.WorkSource; +import android.util.ArraySet; import android.util.Log; -import com.android.internal.annotations.GuardedBy; import com.android.internal.location.ILocationProvider; import com.android.internal.location.ILocationProviderManager; import com.android.internal.location.ProviderProperties; @@ -39,10 +40,8 @@ import com.android.server.ServiceWatcher; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; /** * Proxy for ILocationProvider implementations. @@ -52,59 +51,64 @@ public class LocationProviderProxy extends AbstractLocationProvider { private static final String TAG = "LocationProviderProxy"; private static final boolean D = LocationManagerService.D; - // used to ensure that updates to mProviderPackages are atomic - private final Object mProviderPackagesLock = new Object(); - - // used to ensure that updates to mRequest and mWorkSource are atomic - private final Object mRequestLock = new Object(); + private static final int MAX_ADDITIONAL_PACKAGES = 2; private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() { // executed on binder thread @Override public void onSetAdditionalProviderPackages(List<String> packageNames) { - LocationProviderProxy.this.onSetAdditionalProviderPackages(packageNames); + int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()) + 1; + ArraySet<String> allPackages = new ArraySet<>(maxCount); + allPackages.add(mServiceWatcher.getCurrentPackageName()); + for (String packageName : packageNames) { + if (packageNames.size() >= maxCount) { + return; + } + + try { + mContext.getPackageManager().getPackageInfo(packageName, MATCH_SYSTEM_ONLY); + allPackages.add(packageName); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, mServiceWatcher + " specified unknown additional provider package: " + + packageName); + } + } + + setPackageNames(allPackages); } // executed on binder thread @Override public void onSetEnabled(boolean enabled) { - LocationProviderProxy.this.setEnabled(enabled); + setEnabled(enabled); } // executed on binder thread @Override public void onSetProperties(ProviderProperties properties) { - LocationProviderProxy.this.setProperties(properties); + setProperties(properties); } // executed on binder thread @Override public void onReportLocation(Location location) { - LocationProviderProxy.this.reportLocation(location); + reportLocation(location); } }; private final ServiceWatcher mServiceWatcher; - @GuardedBy("mProviderPackagesLock") - private final CopyOnWriteArrayList<String> mProviderPackages = new CopyOnWriteArrayList<>(); - - @GuardedBy("mRequestLock") - @Nullable - private ProviderRequest mRequest; - @GuardedBy("mRequestLock") - private WorkSource mWorkSource; + @Nullable private ProviderRequest mRequest; /** * Creates a new LocationProviderProxy and immediately begins binding to the best applicable * service. */ @Nullable - public static LocationProviderProxy createAndBind( - Context context, LocationProviderManager locationProviderManager, String action, + public static LocationProviderProxy createAndBind(Context context, String action, int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId) { - LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager, + LocationProviderProxy proxy = new LocationProviderProxy(context, FgThread.getHandler(), action, overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId); if (proxy.bind()) { @@ -114,14 +118,13 @@ public class LocationProviderProxy extends AbstractLocationProvider { } } - private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager, - String action, int overlaySwitchResId, int defaultServicePackageNameResId, + private LocationProviderProxy(Context context, Handler handler, String action, + int overlaySwitchResId, int defaultServicePackageNameResId, int initialPackageNamesResId) { - super(context, locationProviderManager); + super(context, new HandlerExecutor(handler), Collections.emptySet()); mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId, - defaultServicePackageNameResId, initialPackageNamesResId, - FgThread.getHandler()) { + defaultServicePackageNameResId, initialPackageNamesResId, handler) { @Override protected void onBind() { @@ -130,14 +133,11 @@ public class LocationProviderProxy extends AbstractLocationProvider { @Override protected void onUnbind() { - resetProviderPackages(Collections.emptyList()); - setEnabled(false); - setProperties(null); + setState(State.EMPTY_STATE); } }; mRequest = null; - mWorkSource = new WorkSource(); } private boolean bind() { @@ -148,77 +148,34 @@ public class LocationProviderProxy extends AbstractLocationProvider { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher); - resetProviderPackages(Collections.emptyList()); + setPackageNames(Collections.singleton(mServiceWatcher.getCurrentPackageName())); service.setLocationProviderManager(mManager); - synchronized (mRequestLock) { - if (mRequest != null) { - service.setRequest(mRequest, mWorkSource); - } - } - } - - @Override - public List<String> getProviderPackages() { - synchronized (mProviderPackagesLock) { - return mProviderPackages; + if (mRequest != null) { + service.setRequest(mRequest, mRequest.workSource); } } @Override - public void onSetRequest(ProviderRequest request, WorkSource source) { - synchronized (mRequestLock) { - mRequest = request; - mWorkSource = source; - } + public void onSetRequest(ProviderRequest request) { mServiceWatcher.runOnBinder(binder -> { + mRequest = request; ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - service.setRequest(request, source); + service.setRequest(request, request.workSource); }); } @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("service=" + mServiceWatcher); - synchronized (mProviderPackagesLock) { - if (mProviderPackages.size() > 1) { - pw.println("additional packages=" + mProviderPackages); - } - } - } - - @Override - public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) { + public void onExtraCommand(int uid, int pid, String command, Bundle extras) { mServiceWatcher.runOnBinder(binder -> { ILocationProvider service = ILocationProvider.Stub.asInterface(binder); service.sendExtraCommand(command, extras); }); } - private void onSetAdditionalProviderPackages(List<String> packageNames) { - resetProviderPackages(packageNames); - } - - private void resetProviderPackages(List<String> additionalPackageNames) { - ArrayList<String> permittedPackages = new ArrayList<>(additionalPackageNames.size()); - for (String packageName : additionalPackageNames) { - try { - mContext.getPackageManager().getPackageInfo(packageName, MATCH_SYSTEM_ONLY); - permittedPackages.add(packageName); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, mServiceWatcher + " specified unknown additional provider package: " - + packageName); - } - } - - synchronized (mProviderPackagesLock) { - mProviderPackages.clear(); - String myPackage = mServiceWatcher.getCurrentPackageName(); - if (myPackage != null) { - mProviderPackages.add(myPackage); - mProviderPackages.addAll(permittedPackages); - } - } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("service=" + mServiceWatcher); } } diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java index b7ccb26da64b..45c833498ac7 100644 --- a/services/core/java/com/android/server/location/LocationRequestStatistics.java +++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java @@ -1,8 +1,29 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.location; import android.os.SystemClock; import android.util.Log; +import android.util.TimeUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; +import java.util.ArrayList; import java.util.HashMap; /** @@ -17,6 +38,8 @@ public class LocationRequestStatistics { public final HashMap<PackageProviderKey, PackageStatistics> statistics = new HashMap<PackageProviderKey, PackageStatistics>(); + public final RequestSummaryLimitedHistory history = new RequestSummaryLimitedHistory(); + /** * Signals that a package has started requesting locations. * @@ -34,6 +57,7 @@ public class LocationRequestStatistics { } stats.startRequesting(intervalMs); stats.updateForeground(isForeground); + history.addRequest(packageName, providerName, intervalMs); } /** @@ -48,6 +72,7 @@ public class LocationRequestStatistics { if (stats != null) { stats.stopRequesting(); } + history.removeRequest(packageName, providerName); } /** @@ -77,7 +102,7 @@ public class LocationRequestStatistics { */ public final String providerName; - public PackageProviderKey(String packageName, String providerName) { + PackageProviderKey(String packageName, String providerName) { this.packageName = packageName; this.providerName = providerName; } @@ -100,6 +125,104 @@ public class LocationRequestStatistics { } /** + * A data structure to hold past requests + */ + public static class RequestSummaryLimitedHistory { + @VisibleForTesting + static final int MAX_SIZE = 100; + + final ArrayList<RequestSummary> mList = new ArrayList<>(MAX_SIZE); + + /** + * Append an added location request to the history + */ + @VisibleForTesting + void addRequest(String packageName, String providerName, long intervalMs) { + addRequestSummary(new RequestSummary(packageName, providerName, intervalMs)); + } + + /** + * Append a removed location request to the history + */ + @VisibleForTesting + void removeRequest(String packageName, String providerName) { + addRequestSummary(new RequestSummary( + packageName, providerName, RequestSummary.REQUEST_ENDED_INTERVAL)); + } + + private void addRequestSummary(RequestSummary summary) { + while (mList.size() >= MAX_SIZE) { + mList.remove(0); + } + mList.add(summary); + } + + /** + * Dump history to a printwriter (for dumpsys location) + */ + public void dump(IndentingPrintWriter ipw) { + long systemElapsedOffsetMillis = System.currentTimeMillis() + - SystemClock.elapsedRealtime(); + + ipw.println("Last Several Location Requests:"); + ipw.increaseIndent(); + + for (RequestSummary requestSummary : mList) { + requestSummary.dump(ipw, systemElapsedOffsetMillis); + } + + ipw.decreaseIndent(); + } + } + + /** + * A data structure to hold a single request + */ + static class RequestSummary { + /** + * Name of package requesting location. + */ + private final String mPackageName; + /** + * Name of provider being requested (e.g. "gps"). + */ + private final String mProviderName; + /** + * Interval Requested, or REQUEST_ENDED_INTERVAL indicating request has ended + */ + private final long mIntervalMillis; + /** + * Elapsed time of request + */ + private final long mElapsedRealtimeMillis; + + /** + * Placeholder for requested ending (other values indicate request started/changed) + */ + static final long REQUEST_ENDED_INTERVAL = -1; + + RequestSummary(String packageName, String providerName, long intervalMillis) { + this.mPackageName = packageName; + this.mProviderName = providerName; + this.mIntervalMillis = intervalMillis; + this.mElapsedRealtimeMillis = SystemClock.elapsedRealtime(); + } + + void dump(IndentingPrintWriter ipw, long systemElapsedOffsetMillis) { + StringBuilder s = new StringBuilder(); + long systemTimeMillis = systemElapsedOffsetMillis + mElapsedRealtimeMillis; + s.append("At ").append(TimeUtils.formatForLogging(systemTimeMillis)).append(": ") + .append(mIntervalMillis == REQUEST_ENDED_INTERVAL ? "- " : "+ ") + .append(String.format("%7s", mProviderName)).append(" request from ") + .append(mPackageName); + if (mIntervalMillis != REQUEST_ENDED_INTERVAL) { + s.append(" at interval ").append(mIntervalMillis / 1000).append(" seconds"); + } + ipw.println(s); + } + } + + /** * Usage statistics for a package/provider pair. */ public static class PackageStatistics { diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/LocationSettingsStore.java index f625452975c0..0e8720ebb08f 100644 --- a/services/core/java/com/android/server/location/LocationSettingsStore.java +++ b/services/core/java/com/android/server/location/LocationSettingsStore.java @@ -16,6 +16,8 @@ package com.android.server.location; +import static android.location.LocationManager.FUSED_PROVIDER; +import static android.location.LocationManager.PASSIVE_PROVIDER; import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS; import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST; import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS; @@ -28,6 +30,7 @@ import android.app.ActivityManager; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; +import android.os.Binder; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; @@ -248,6 +251,9 @@ public class LocationSettingsStore { DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS); } + /** + * Retrieve maximum age of the last location. + */ public long getMaxLastLocationAgeMs() { return Settings.Global.getLong( mContext.getContentResolver(), @@ -256,6 +262,29 @@ public class LocationSettingsStore { } /** + * Set a value for the deprecated LOCATION_PROVIDERS_ALLOWED setting. This is used purely for + * backwards compatibility for old clients, and may be removed in the future. + */ + public void setLocationProviderAllowed(String provider, boolean enabled, int userId) { + // fused and passive provider never get public updates for legacy reasons + if (FUSED_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) { + return; + } + + long identity = Binder.clearCallingIdentity(); + try { + // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility + Settings.Secure.putStringForUser( + mContext.getContentResolver(), + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + (enabled ? "+" : "-") + provider, + userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** * Dump info for debugging. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index 472876bfd86a..60c9fc12c201 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -19,7 +19,6 @@ package com.android.server.location; import android.annotation.Nullable; import android.content.Context; import android.location.Location; -import android.os.WorkSource; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; @@ -34,41 +33,33 @@ import java.io.PrintWriter; */ public class MockProvider extends AbstractLocationProvider { - private boolean mEnabled; @Nullable private Location mLocation; - public MockProvider(Context context, - LocationProviderManager locationProviderManager, ProviderProperties properties) { - super(context, locationProviderManager); - - mEnabled = true; - mLocation = null; - + public MockProvider(Context context, ProviderProperties properties) { + // using a direct executor is only acceptable because this class is so simple it is trivial + // to verify that it does not acquire any locks or re-enter LMS from callbacks + super(context, Runnable::run); setProperties(properties); } /** Sets the enabled state of this mock provider. */ - public void setEnabled(boolean enabled) { - mEnabled = enabled; - super.setEnabled(enabled); + public void setProviderEnabled(boolean enabled) { + setEnabled(enabled); } /** Sets the location to report for this mock provider. */ - public void setLocation(Location l) { - mLocation = new Location(l); - if (!mLocation.isFromMockProvider()) { - mLocation.setIsFromMockProvider(true); - } - if (mEnabled) { - reportLocation(mLocation); - } + public void setProviderLocation(Location l) { + Location location = new Location(l); + location.setIsFromMockProvider(true); + mLocation = location; + reportLocation(location); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("last location=" + mLocation); + pw.println("last mock location=" + mLocation); } @Override - public void onSetRequest(ProviderRequest request, WorkSource source) {} + public void onSetRequest(ProviderRequest request) {} } diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java new file mode 100644 index 000000000000..f50dfe7edbb7 --- /dev/null +++ b/services/core/java/com/android/server/location/MockableLocationProvider.java @@ -0,0 +1,289 @@ +/* + * 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.location; + +import android.annotation.Nullable; +import android.content.Context; +import android.location.Location; +import android.os.Bundle; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.location.ProviderRequest; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; + +/** + * Represents a location provider that may switch between a mock implementation and a real + * implementation. Requires owners to provide a lock object that will be used internally and held + * for the duration of all listener callbacks. Owners are reponsible for ensuring this cannot lead + * to deadlock. + * + * In order to ensure deadlock does not occur, the owner must validate that the ONLY lock which can + * be held BOTH when calling into this class AND when receiving a callback from this class is the + * lock given to this class via the constructor. Holding any other lock is ok as long as there is no + * possibility that it can be obtained within both codepaths. + * + * Holding the given lock guarantees atomicity of any operations on this class for the duration. + * + * @hide + */ +public class MockableLocationProvider extends AbstractLocationProvider { + + private final Object mOwnerLock; + + @GuardedBy("mOwnerLock") + @Nullable private AbstractLocationProvider mProvider; + @GuardedBy("mOwnerLock") + @Nullable private AbstractLocationProvider mRealProvider; + @GuardedBy("mOwnerLock") + @Nullable private MockProvider mMockProvider; + + @GuardedBy("mOwnerLock") + private ProviderRequest mRequest; + + /** + * The given lock object will be held any time the listener is invoked, and may also be acquired + * and released during the course of invoking any public methods. Holding the given lock ensures + * that provider state cannot change except as result of an explicit call by the owner of the + * lock into this class. The client is reponsible for ensuring this cannot cause deadlock. + * + * The client should expect that it may being to receive callbacks as soon as this constructor + * is invoked. + */ + public MockableLocationProvider(Context context, Object ownerLock, Listener listener) { + // using a direct executor is acceptable because all inbound calls are delegated to the + // actual provider implementations which will use their own executors + super(context, Runnable::run, Collections.emptySet()); + mOwnerLock = ownerLock; + mRequest = ProviderRequest.EMPTY_REQUEST; + + setListener(listener); + } + + /** + * Returns the current provider implementation. May be null if there is no current + * implementation. + */ + @Nullable + public AbstractLocationProvider getProvider() { + synchronized (mOwnerLock) { + return mProvider; + } + } + + /** + * Sets the real provider implementation, replacing any previous real provider implementation. + * May cause an inline invocation of {@link Listener#onStateChanged(State, State)} if this + * results in a state change. + */ + public void setRealProvider(@Nullable AbstractLocationProvider provider) { + synchronized (mOwnerLock) { + if (mRealProvider == provider) { + return; + } + + mRealProvider = provider; + if (!isMock()) { + setProviderLocked(mRealProvider); + } + } + } + + /** + * Sets the mock provider implementation, replacing any previous mock provider implementation. + * Mock implementations are always used instead of real implementations if set. May cause an + * inline invocation of {@link Listener#onStateChanged(State, State)} if this results in a + * state change. + */ + public void setMockProvider(@Nullable MockProvider provider) { + synchronized (mOwnerLock) { + if (mMockProvider == provider) { + return; + } + + mMockProvider = provider; + if (mMockProvider != null) { + setProviderLocked(mMockProvider); + } else { + setProviderLocked(mRealProvider); + } + } + } + + @GuardedBy("mOwnerLock") + private void setProviderLocked(@Nullable AbstractLocationProvider provider) { + if (mProvider == provider) { + return; + } + + AbstractLocationProvider oldProvider = mProvider; + mProvider = provider; + + if (oldProvider != null) { + // do this after switching the provider - so even if the old provider is using a direct + // executor, if it re-enters this class within setRequest(), it will be ignored + oldProvider.setListener(null); + oldProvider.setRequest(ProviderRequest.EMPTY_REQUEST); + } + + State newState; + if (mProvider != null) { + newState = mProvider.setListener(new ListenerWrapper(mProvider)); + } else { + newState = State.EMPTY_STATE; + } + + ProviderRequest oldRequest = mRequest; + setState(newState); + + if (mProvider != null && oldRequest == mRequest) { + mProvider.setRequest(mRequest); + } + } + + /** + * Returns true if the current active provider implementation is the mock implementation, and + * false otherwise. + */ + public boolean isMock() { + synchronized (mOwnerLock) { + return mMockProvider != null && mProvider == mMockProvider; + } + } + + /** + * Sets the mock provider implementation's enabled state. Will throw an exception if the mock + * provider is not currently the active implementation. + */ + public void setMockProviderEnabled(boolean enabled) { + synchronized (mOwnerLock) { + Preconditions.checkState(isMock()); + mMockProvider.setProviderEnabled(enabled); + } + } + /** + * Sets the mock provider implementation's location. Will throw an exception if the mock + * provider is not currently the active implementation. + */ + public void setMockProviderLocation(Location location) { + synchronized (mOwnerLock) { + Preconditions.checkState(isMock()); + mMockProvider.setProviderLocation(location); + } + } + + @Override + public State getState() { + return super.getState(); + } + + /** + * Returns the current location request. + */ + public ProviderRequest getCurrentRequest() { + synchronized (mOwnerLock) { + return mRequest; + } + } + + protected void onSetRequest(ProviderRequest request) { + synchronized (mOwnerLock) { + if (request == mRequest) { + return; + } + + mRequest = request; + + if (mProvider != null) { + mProvider.setRequest(request); + } + } + } + + protected void onExtraCommand(int uid, int pid, String command, Bundle extras) { + synchronized (mOwnerLock) { + if (mProvider != null) { + mProvider.sendExtraCommand(uid, pid, command, extras); + } + } + } + + /** + * Dumps the current provider implementation. + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + AbstractLocationProvider provider; + synchronized (mOwnerLock) { + provider = mProvider; + pw.println("request=" + mRequest); + } + + if (provider != null) { + // dump outside the lock in case the provider wants to acquire its own locks, and since + // the default provider dump behavior does not move things onto the provider thread... + provider.dump(fd, pw, args); + } + } + + // ensures that callbacks from the incorrect provider are never visible to clients - this + // requires holding the owner's lock for the duration of the callback + private class ListenerWrapper implements Listener { + + private final AbstractLocationProvider mListenerProvider; + + private ListenerWrapper(AbstractLocationProvider listenerProvider) { + mListenerProvider = listenerProvider; + } + + @Override + public final void onStateChanged(State oldState, State newState) { + synchronized (mOwnerLock) { + if (mListenerProvider != mProvider) { + return; + } + + setState(newState); + } + } + + @Override + public final void onReportLocation(Location location) { + synchronized (mOwnerLock) { + if (mListenerProvider != mProvider) { + return; + } + + reportLocation(location); + } + } + + @Override + public final void onReportLocation(List<Location> locations) { + synchronized (mOwnerLock) { + if (mListenerProvider != mProvider) { + return; + } + + reportLocation(locations); + } + } + } +} diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java index 639b1eb1ed5e..b33877069d70 100644 --- a/services/core/java/com/android/server/location/PassiveProvider.java +++ b/services/core/java/com/android/server/location/PassiveProvider.java @@ -19,7 +19,6 @@ package com.android.server.location; import android.content.Context; import android.location.Criteria; import android.location.Location; -import android.os.WorkSource; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; @@ -37,13 +36,22 @@ import java.io.PrintWriter; public class PassiveProvider extends AbstractLocationProvider { private static final ProviderProperties PROPERTIES = new ProviderProperties( - false, false, false, false, false, false, false, - Criteria.POWER_LOW, Criteria.ACCURACY_COARSE); + /* requiresNetwork = */false, + /* requiresSatellite = */false, + /* requiresCell = */false, + /* hasMonetaryCost = */false, + /* supportsAltitude = */false, + /* supportsSpeed = */false, + /* supportsBearing = */false, + Criteria.POWER_LOW, + Criteria.ACCURACY_COARSE); - private boolean mReportLocation; + private volatile boolean mReportLocation; - public PassiveProvider(Context context, LocationProviderManager locationProviderManager) { - super(context, locationProviderManager); + public PassiveProvider(Context context) { + // using a direct executor is only acceptable because this class is so simple it is trivial + // to verify that it does not acquire any locks or re-enter LMS from callbacks + super(context, Runnable::run); mReportLocation = false; @@ -52,7 +60,7 @@ public class PassiveProvider extends AbstractLocationProvider { } @Override - public void onSetRequest(ProviderRequest request, WorkSource source) { + public void onSetRequest(ProviderRequest request) { mReportLocation = request.reportLocation; } @@ -63,7 +71,5 @@ public class PassiveProvider extends AbstractLocationProvider { } @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("report location=" + mReportLocation); - } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {} } diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 115155ce24b5..9c9a4121830f 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Intent; -import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.RouteSessionInfo; @@ -47,16 +46,16 @@ abstract class MediaRoute2Provider { } public abstract void requestCreateSession(String packageName, String routeId, - String controlCategory, long requestId); - public abstract void releaseSession(int sessionId); + String routeType, long requestId); + public abstract void releaseSession(String sessionId); - public abstract void selectRoute(int sessionId, MediaRoute2Info route); - public abstract void deselectRoute(int sessionId, MediaRoute2Info route); - public abstract void transferToRoute(int sessionId, MediaRoute2Info route); + public abstract void selectRoute(String sessionId, String routeId); + public abstract void deselectRoute(String sessionId, String routeId); + public abstract void transferToRoute(String sessionId, String routeId); - public abstract void sendControlRequest(MediaRoute2Info route, Intent request); - public abstract void requestSetVolume(MediaRoute2Info route, int volume); - public abstract void requestUpdateVolume(MediaRoute2Info route, int delta); + public abstract void sendControlRequest(String routeId, Intent request); + public abstract void requestSetVolume(String routeId, int volume); + public abstract void requestUpdateVolume(String routeId, int delta); @NonNull public String getUniqueId() { diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index f8d8f9fd5fbd..635983575226 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.media.IMediaRoute2Provider; import android.media.IMediaRoute2ProviderClient; -import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; import android.media.RouteSessionInfo; @@ -77,17 +76,17 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void requestCreateSession(String packageName, String routeId, String controlCategory, + public void requestCreateSession(String packageName, String routeId, String routeType, long requestId) { if (mConnectionReady) { - mActiveConnection.requestCreateSession(packageName, routeId, controlCategory, + mActiveConnection.requestCreateSession(packageName, routeId, routeType, requestId); updateBinding(); } } @Override - public void releaseSession(int sessionId) { + public void releaseSession(String sessionId) { if (mConnectionReady) { mActiveConnection.releaseSession(sessionId); updateBinding(); @@ -95,46 +94,46 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void selectRoute(int sessionId, MediaRoute2Info route) { + public void selectRoute(String sessionId, String routeId) { if (mConnectionReady) { - mActiveConnection.selectRoute(sessionId, route.getId()); + mActiveConnection.selectRoute(sessionId, routeId); } } @Override - public void deselectRoute(int sessionId, MediaRoute2Info route) { + public void deselectRoute(String sessionId, String routeId) { if (mConnectionReady) { - mActiveConnection.deselectRoute(sessionId, route.getId()); + mActiveConnection.deselectRoute(sessionId, routeId); } } @Override - public void transferToRoute(int sessionId, MediaRoute2Info route) { + public void transferToRoute(String sessionId, String routeId) { if (mConnectionReady) { - mActiveConnection.transferToRoute(sessionId, route.getId()); + mActiveConnection.transferToRoute(sessionId, routeId); } } @Override - public void sendControlRequest(MediaRoute2Info route, Intent request) { + public void sendControlRequest(String routeId, Intent request) { if (mConnectionReady) { - mActiveConnection.sendControlRequest(route.getId(), request); + mActiveConnection.sendControlRequest(routeId, request); updateBinding(); } } @Override - public void requestSetVolume(MediaRoute2Info route, int volume) { + public void requestSetVolume(String routeId, int volume) { if (mConnectionReady) { - mActiveConnection.requestSetVolume(route.getId(), volume); + mActiveConnection.requestSetVolume(routeId, volume); updateBinding(); } } @Override - public void requestUpdateVolume(MediaRoute2Info route, int delta) { + public void requestUpdateVolume(String routeId, int delta) { if (mConnectionReady) { - mActiveConnection.requestUpdateVolume(route.getId(), delta); + mActiveConnection.requestUpdateVolume(routeId, delta); updateBinding(); } } @@ -303,6 +302,11 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv + mComponentName); return; } + + sessionInfo = new RouteSessionInfo.Builder(sessionInfo) + .setProviderId(getUniqueId()) + .build(); + mCallback.onSessionInfoChanged(this, sessionInfo); } @@ -346,17 +350,17 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv mClient.dispose(); } - public void requestCreateSession(String packageName, String routeId, String controlCategory, + public void requestCreateSession(String packageName, String routeId, String routeType, long requestId) { try { mProvider.requestCreateSession(packageName, routeId, - controlCategory, requestId); + routeType, requestId); } catch (RemoteException ex) { Slog.e(TAG, "Failed to deliver request to create a session.", ex); } } - public void releaseSession(int sessionId) { + public void releaseSession(String sessionId) { try { mProvider.releaseSession(sessionId); } catch (RemoteException ex) { @@ -364,7 +368,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } } - public void selectRoute(int sessionId, String routeId) { + public void selectRoute(String sessionId, String routeId) { try { mProvider.selectRoute(sessionId, routeId); } catch (RemoteException ex) { @@ -372,7 +376,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } } - public void deselectRoute(int sessionId, String routeId) { + public void deselectRoute(String sessionId, String routeId) { try { mProvider.deselectRoute(sessionId, routeId); } catch (RemoteException ex) { @@ -380,7 +384,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } } - public void transferToRoute(int sessionId, String routeId) { + public void transferToRoute(String sessionId, String routeId) { try { mProvider.transferToRoute(sessionId, routeId); } catch (RemoteException ex) { diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 562a7200fb38..487ab5201278 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -16,6 +16,9 @@ package com.android.server.media; +import static android.media.MediaRouter2Utils.getOriginalId; +import static android.media.MediaRouter2Utils.getProviderId; + import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.NonNull; @@ -28,6 +31,7 @@ import android.media.IMediaRouter2Client; import android.media.IMediaRouter2Manager; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; +import android.media.RouteDiscoveryRequest; import android.media.RouteSessionInfo; import android.os.Binder; import android.os.Bundle; @@ -174,18 +178,18 @@ class MediaRouter2ServiceImpl { } public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route, - String controlCategory, int requestId) { + String routeType, int requestId) { Objects.requireNonNull(client, "client must not be null"); Objects.requireNonNull(route, "route must not be null"); - if (TextUtils.isEmpty(controlCategory)) { - throw new IllegalArgumentException("controlCategory must not be empty"); + if (TextUtils.isEmpty(routeType)) { + throw new IllegalArgumentException("routeType must not be empty"); } final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - requestCreateSessionLocked(client, route, controlCategory, requestId); + requestCreateSessionLocked(client, route, routeType, requestId); } } finally { Binder.restoreCallingIdentity(token); @@ -196,6 +200,9 @@ class MediaRouter2ServiceImpl { MediaRoute2Info route) { Objects.requireNonNull(client, "client must not be null"); Objects.requireNonNull(route, "route must not be null"); + if (TextUtils.isEmpty(uniqueSessionId)) { + throw new IllegalArgumentException("uniqueSessionId must not be empty"); + } final long token = Binder.clearCallingIdentity(); try { @@ -212,6 +219,9 @@ class MediaRouter2ServiceImpl { MediaRoute2Info route) { Objects.requireNonNull(client, "client must not be null"); Objects.requireNonNull(route, "route must not be null"); + if (TextUtils.isEmpty(uniqueSessionId)) { + throw new IllegalArgumentException("uniqueSessionId must not be empty"); + } final long token = Binder.clearCallingIdentity(); try { @@ -227,6 +237,9 @@ class MediaRouter2ServiceImpl { MediaRoute2Info route) { Objects.requireNonNull(client, "client must not be null"); Objects.requireNonNull(route, "route must not be null"); + if (TextUtils.isEmpty(uniqueSessionId)) { + throw new IllegalArgumentException("uniqueSessionId must not be empty"); + } final long token = Binder.clearCallingIdentity(); try { @@ -240,6 +253,9 @@ class MediaRouter2ServiceImpl { public void releaseSession(IMediaRouter2Client client, String uniqueSessionId) { Objects.requireNonNull(client, "client must not be null"); + if (TextUtils.isEmpty(uniqueSessionId)) { + throw new IllegalArgumentException("uniqueSessionId must not be empty"); + } final long token = Binder.clearCallingIdentity(); try { @@ -267,16 +283,16 @@ class MediaRouter2ServiceImpl { } } - public void setControlCategories(@NonNull IMediaRouter2Client client, - @NonNull List<String> categories) { + public void setDiscoveryRequest2(@NonNull IMediaRouter2Client client, + @NonNull RouteDiscoveryRequest request) { Objects.requireNonNull(client, "client must not be null"); - Objects.requireNonNull(categories, "categories must not be null"); + Objects.requireNonNull(request, "request must not be null"); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { Client2Record clientRecord = mAllClientRecords.get(client.asBinder()); - setControlCategoriesLocked(clientRecord, categories); + setDiscoveryRequestLocked(clientRecord, request); } } finally { Binder.restoreCallingIdentity(token); @@ -434,7 +450,7 @@ class MediaRouter2ServiceImpl { } private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client, - @NonNull MediaRoute2Info route, @NonNull String controlCategory, long requestId) { + @NonNull MediaRoute2Info route, @NonNull String routeType, long requestId) { final IBinder binder = client.asBinder(); final Client2Record clientRecord = mAllClientRecords.get(binder); @@ -447,7 +463,7 @@ class MediaRouter2ServiceImpl { clientRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::requestCreateSessionOnHandler, clientRecord.mUserRecord.mHandler, - clientRecord, route, controlCategory, requestId)); + clientRecord, route, routeType, requestId)); } } @@ -502,13 +518,14 @@ class MediaRouter2ServiceImpl { } } - private void setControlCategoriesLocked(Client2Record clientRecord, List<String> categories) { + private void setDiscoveryRequestLocked(Client2Record clientRecord, + RouteDiscoveryRequest discoveryRequest) { if (clientRecord != null) { - if (clientRecord.mControlCategories.equals(categories)) { + if (clientRecord.mDiscoveryRequest.equals(discoveryRequest)) { return; } - clientRecord.mControlCategories = categories; + clientRecord.mDiscoveryRequest = discoveryRequest; clientRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::updateClientUsage, clientRecord.mUserRecord.mHandler, clientRecord)); @@ -605,9 +622,9 @@ class MediaRouter2ServiceImpl { } long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId); if (clientRecord != null && managerRecord.mTrusted) { - //TODO: select category properly + //TODO: select route type properly requestCreateSessionLocked(clientRecord.mClient, route, - route.getSupportedCategories().get(0), uniqueRequestId); + route.getRouteTypes().get(0), uniqueRequestId); } } } @@ -725,7 +742,7 @@ class MediaRouter2ServiceImpl { public final boolean mTrusted; public final int mClientId; - public List<String> mControlCategories; + public RouteDiscoveryRequest mDiscoveryRequest; public boolean mIsManagerSelecting; public MediaRoute2Info mSelectingRoute; public MediaRoute2Info mSelectedRoute; @@ -735,7 +752,7 @@ class MediaRouter2ServiceImpl { mUserRecord = userRecord; mPackageName = packageName; mSelectRouteSequenceNumbers = new ArrayList<>(); - mControlCategories = Collections.emptyList(); + mDiscoveryRequest = RouteDiscoveryRequest.EMPTY; mClient = client; mUid = uid; mPid = pid; @@ -961,7 +978,7 @@ class MediaRouter2ServiceImpl { } private void requestCreateSessionOnHandler(Client2Record clientRecord, - MediaRoute2Info route, String controlCategory, long requestId) { + MediaRoute2Info route, String routeType, long requestId) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider == null) { @@ -971,20 +988,20 @@ class MediaRouter2ServiceImpl { return; } - if (!route.getSupportedCategories().contains(controlCategory)) { + if (!route.getRouteTypes().contains(routeType)) { Slog.w(TAG, "Ignoring session creation request since the given route=" + route - + " doesn't support the given category=" + controlCategory); + + " doesn't support the given type=" + routeType); notifySessionCreationFailed(clientRecord, toClientRequestId(requestId)); return; } // TODO: Apply timeout for each request (How many seconds should we wait?) SessionCreationRequest request = new SessionCreationRequest( - clientRecord, route, controlCategory, requestId); + clientRecord, route, routeType, requestId); mSessionCreationRequests.add(request); - provider.requestCreateSession(clientRecord.mPackageName, route.getId(), - controlCategory, requestId); + provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(), + routeType, requestId); } private void selectRouteOnHandler(@NonNull Client2Record clientRecord, @@ -1000,7 +1017,7 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route); + provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId()); } private void deselectRouteOnHandler(@NonNull Client2Record clientRecord, @@ -1016,7 +1033,7 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route); + provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId()); } private void transferToRouteOnHandler(@NonNull Client2Record clientRecord, @@ -1032,7 +1049,8 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route); + provider.transferToRoute(getOriginalId(uniqueSessionId), + route.getOriginalId()); } private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord, @@ -1063,9 +1081,9 @@ class MediaRouter2ServiceImpl { return false; } - final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId); + final String sessionId = getOriginalId(uniqueSessionId); if (sessionId == null) { - Slog.w(TAG, "Failed to get int session id from unique session id. " + Slog.w(TAG, "Failed to get original session id from unique session id. " + "uniqueSessionId=" + uniqueSessionId); return false; } @@ -1088,14 +1106,14 @@ class MediaRouter2ServiceImpl { return; } - final String providerId = RouteSessionInfo.getProviderId(uniqueSessionId); + final String providerId = getProviderId(uniqueSessionId); if (providerId == null) { Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. " + "uniqueSessionId=" + uniqueSessionId); return; } - final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId); + final String sessionId = getOriginalId(uniqueSessionId); if (sessionId == null) { Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. " + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId); @@ -1141,15 +1159,15 @@ class MediaRouter2ServiceImpl { } String originalRouteId = matchingRequest.mRoute.getId(); - String originalCategory = matchingRequest.mControlCategory; + String originalRouteType = matchingRequest.mRouteType; Client2Record client2Record = matchingRequest.mClientRecord; if (!sessionInfo.getSelectedRoutes().contains(originalRouteId) - || !TextUtils.equals(originalCategory, - sessionInfo.getControlCategory())) { + || !TextUtils.equals(originalRouteType, + sessionInfo.getRouteType())) { Slog.w(TAG, "Created session doesn't match the original request." + " originalRouteId=" + originalRouteId - + ", originalCategory=" + originalCategory + ", requestId=" + requestId + + ", originalRouteType=" + originalRouteType + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo); notifySessionCreationFailed(matchingRequest.mClientRecord, toClientRequestId(requestId)); @@ -1159,41 +1177,34 @@ class MediaRouter2ServiceImpl { // Succeeded notifySessionCreated(matchingRequest.mClientRecord, sessionInfo, toClientRequestId(requestId)); - mSessionToClientMap.put(sessionInfo.getUniqueSessionId(), client2Record); + mSessionToClientMap.put(sessionInfo.getId(), client2Record); // TODO: Tell managers for the session creation } private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RouteSessionInfo sessionInfo) { - RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo) - .setProviderId(provider.getUniqueId()) - .build(); Client2Record client2Record = mSessionToClientMap.get( - sessionInfoWithProviderId.getUniqueSessionId()); + sessionInfo.getId()); if (client2Record == null) { - Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId); + Slog.w(TAG, "No matching client found for session=" + sessionInfo); // TODO: Tell managers for the session update return; } - notifySessionInfoChanged(client2Record, sessionInfoWithProviderId); + notifySessionInfoChanged(client2Record, sessionInfo); // TODO: Tell managers for the session update } private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RouteSessionInfo sessionInfo) { - RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo) - .setProviderId(provider.getUniqueId()) - .build(); - Client2Record client2Record = mSessionToClientMap.get( - sessionInfoWithProviderId.getUniqueSessionId()); + Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId()); if (client2Record == null) { - Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId); + Slog.w(TAG, "No matching client found for session=" + sessionInfo); // TODO: Tell managers for the session release return; } - notifySessionReleased(client2Record, sessionInfoWithProviderId); + notifySessionReleased(client2Record, sessionInfo); // TODO: Tell managers for the session release } @@ -1239,21 +1250,21 @@ class MediaRouter2ServiceImpl { private void sendControlRequest(MediaRoute2Info route, Intent request) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider != null) { - provider.sendControlRequest(route, request); + provider.sendControlRequest(route.getOriginalId(), request); } } private void requestSetVolume(MediaRoute2Info route, int volume) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider != null) { - provider.requestSetVolume(route, volume); + provider.requestSetVolume(route.getOriginalId(), volume); } } private void requestUpdateVolume(MediaRoute2Info route, int delta) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider != null) { - provider.requestUpdateVolume(route, delta); + provider.requestUpdateVolume(route.getOriginalId(), delta); } } @@ -1401,8 +1412,8 @@ class MediaRouter2ServiceImpl { try { manager.notifyRouteSelected(clientRecord.mPackageName, clientRecord.mSelectedRoute); - manager.notifyControlCategoriesChanged(clientRecord.mPackageName, - clientRecord.mControlCategories); + manager.notifyRouteTypesChanged(clientRecord.mPackageName, + clientRecord.mDiscoveryRequest.getRouteTypes()); } catch (RemoteException ex) { Slog.w(TAG, "Failed to update client usage. Manager probably died.", ex); } @@ -1421,15 +1432,15 @@ class MediaRouter2ServiceImpl { final class SessionCreationRequest { public final Client2Record mClientRecord; public final MediaRoute2Info mRoute; - public final String mControlCategory; + public final String mRouteType; public final long mRequestId; SessionCreationRequest(@NonNull Client2Record clientRecord, @NonNull MediaRoute2Info route, - @NonNull String controlCategory, long requestId) { + @NonNull String routeType, long requestId) { mClientRecord = clientRecord; mRoute = route; - mControlCategory = controlCategory; + mRouteType = routeType; mRequestId = requestId; } } diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index d77f43b4435d..c76555cf15cf 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -39,6 +39,7 @@ import android.media.MediaRouter; import android.media.MediaRouterClientState; import android.media.RemoteDisplayState; import android.media.RemoteDisplayState.RemoteDisplayInfo; +import android.media.RouteDiscoveryRequest; import android.media.RouteSessionInfo; import android.os.Binder; import android.os.Handler; @@ -459,8 +460,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route, - String controlCategory, int requestId) { - mService2.requestCreateSession(client, route, controlCategory, requestId); + String routeType, int requestId) { + mService2.requestCreateSession(client, route, routeType, requestId); } // Binder call @@ -519,8 +520,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub } // Binder call @Override - public void setControlCategories(IMediaRouter2Client client, List<String> categories) { - mService2.setControlCategories(client, categories); + public void setDiscoveryRequest2(IMediaRouter2Client client, RouteDiscoveryRequest request) { + mService2.setDiscoveryRequest2(client, request); } // Binder call diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java new file mode 100644 index 000000000000..f3241ee44569 --- /dev/null +++ b/services/core/java/com/android/server/media/MediaSession2Record.java @@ -0,0 +1,179 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.media.MediaController2; +import android.media.Session2CommandGroup; +import android.media.Session2Token; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; +import android.os.ResultReceiver; +import android.os.UserHandle; +import android.util.Log; +import android.view.KeyEvent; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; + +/** + * Keeps the record of {@link Session2Token} helps to send command to the corresponding session. + */ +// TODO(jaewan): Do not call service method directly -- introduce listener instead. +public class MediaSession2Record implements MediaSessionRecordImpl { + private static final String TAG = "MediaSession2Record"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final Session2Token mSessionToken; + @GuardedBy("mLock") + private final HandlerExecutor mHandlerExecutor; + @GuardedBy("mLock") + private final MediaController2 mController; + @GuardedBy("mLock") + private final MediaSessionService mService; + @GuardedBy("mLock") + private boolean mIsConnected; + + public MediaSession2Record(Session2Token sessionToken, MediaSessionService service, + Looper handlerLooper) { + mSessionToken = sessionToken; + mService = service; + mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper)); + mController = new MediaController2.Builder(service.getContext(), sessionToken) + .setControllerCallback(mHandlerExecutor, new Controller2Callback()) + .build(); + } + + @Override + public String getPackageName() { + return mSessionToken.getPackageName(); + } + + public Session2Token getSession2Token() { + return mSessionToken; + } + + @Override + public int getUid() { + return mSessionToken.getUid(); + } + + @Override + public int getUserId() { + return UserHandle.getUserId(mSessionToken.getUid()); + } + + @Override + public boolean isSystemPriority() { + // System priority session is currently only allowed for telephony, and it's OK to stick to + // the media1 API at this moment. + return false; + } + + @Override + public void adjustVolume(String packageName, String opPackageName, int pid, int uid, + boolean asSystemService, int direction, int flags, boolean useSuggested) { + // TODO(jaewan): Add API to adjust volume. + } + + @Override + public boolean isActive() { + synchronized (mLock) { + return mIsConnected; + } + } + + @Override + public boolean checkPlaybackActiveState(boolean expected) { + synchronized (mLock) { + return mIsConnected && mController.isPlaybackActive() == expected; + } + } + + @Override + public boolean isPlaybackTypeLocal() { + // TODO(jaewan): Implement -- need API to know whether the playback is remote or local. + return true; + } + + @Override + public void close() { + synchronized (mLock) { + // Call close regardless of the mIsAvailable. This may be called when it's not yet + // connected. + mController.close(); + } + } + + @Override + public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, + KeyEvent ke, int sequenceId, ResultReceiver cb) { + // TODO(jaewan): Implement. + return false; + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "token=" + mSessionToken); + pw.println(prefix + "controller=" + mController); + + final String indent = prefix + " "; + pw.println(indent + "playbackActive=" + mController.isPlaybackActive()); + } + + @Override + public String toString() { + // TODO(jaewan): Also add getId(). + return getPackageName() + " (userId=" + getUserId() + ")"; + } + + private class Controller2Callback extends MediaController2.ControllerCallback { + @Override + public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) { + if (DEBUG) { + Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands); + } + synchronized (mLock) { + mIsConnected = true; + } + mService.onSessionActiveStateChanged(MediaSession2Record.this); + } + + @Override + public void onDisconnected(MediaController2 controller) { + if (DEBUG) { + Log.d(TAG, "disconnected from " + mSessionToken); + } + synchronized (mLock) { + mIsConnected = false; + } + mService.onSessionDied(MediaSession2Record.this); + } + + @Override + public void onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive) { + if (DEBUG) { + Log.d(TAG, "playback active changed, " + mSessionToken + ", active=" + + playbackActive); + } + mService.onSessionPlaybackStateChanged(MediaSession2Record.this, playbackActive); + } + } +} diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index aa24ed26023a..df115d0f2773 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -56,13 +56,15 @@ import com.android.server.LocalServices; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** * This is the system implementation of a Session. Apps will interact with the * MediaSession wrapper class instead. */ -public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable { +// TODO(jaewan): Do not call service method directly -- introduce listener instead. +public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl { private static final String TAG = "MediaSessionRecord"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -72,6 +74,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable */ private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000; + /** + * These are states that usually indicate the user took an action and should + * bump priority regardless of the old state. + */ + private static final List<Integer> ALWAYS_PRIORITY_STATES = Arrays.asList( + PlaybackState.STATE_FAST_FORWARDING, + PlaybackState.STATE_REWINDING, + PlaybackState.STATE_SKIPPING_TO_PREVIOUS, + PlaybackState.STATE_SKIPPING_TO_NEXT); + /** + * These are states that usually indicate the user took an action if they + * were entered from a non-priority state. + */ + private static final List<Integer> TRANSITION_PRIORITY_STATES = Arrays.asList( + PlaybackState.STATE_BUFFERING, + PlaybackState.STATE_CONNECTING, + PlaybackState.STATE_PLAYING); + private final MessageHandler mHandler; private final int mOwnerPid; @@ -170,6 +190,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return Info that identifies this session. */ + @Override public String getPackageName() { return mPackageName; } @@ -188,6 +209,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return The UID for this session. */ + @Override public int getUid() { return mOwnerUid; } @@ -197,6 +219,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return The user id for this session. */ + @Override public int getUserId() { return mUserId; } @@ -207,6 +230,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return True if this is a system priority session, false otherwise */ + @Override public boolean isSystemPriority() { return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0; } @@ -220,7 +244,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * @param opPackageName The op package that made the original volume request. * @param pid The pid that made the original volume request. * @param uid The uid that made the original volume request. - * @param caller caller binder. can be {@code null} if it's from the volume key. * @param asSystemService {@code true} if the event sent to the session as if it was come from * the system service instead of the app process. This helps sessions to distinguish * between the key injection by the app and key events from the hardware devices. @@ -318,9 +341,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable /** * Check if this session has been set to active by the app. + * <p> + * It's not used to prioritize sessions for dispatching media keys since API 26, but still used + * to filter session list in MediaSessionManager#getActiveSessions(). * * @return True if the session is active, false otherwise. */ + @Override public boolean isActive() { return mIsActive && !mDestroyed; } @@ -333,6 +360,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * @param expected True if playback is expected to be active. false otherwise. * @return True if the session's playback matches with the expectation. false otherwise. */ + @Override public boolean checkPlaybackActiveState(boolean expected) { if (mPlaybackState == null) { return false; @@ -345,13 +373,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return {@code true} if the playback is local. */ - public boolean isPlaybackLocal() { + @Override + public boolean isPlaybackTypeLocal() { return mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; } @Override public void binderDied() { - mService.sessionDied(this); + mService.onSessionDied(this); } /** @@ -383,7 +412,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed. * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is * needed. - * @return {@code true} if the attempt to send media button was successfuly. + * @return {@code true} if the attempt to send media button was successfully. * {@code false} otherwise. */ public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, @@ -392,6 +421,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable cb); } + @Override public void dump(PrintWriter pw, String prefix) { pw.println(prefix + mTag + " " + this); @@ -712,7 +742,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable public void destroySession() throws RemoteException { final long token = Binder.clearCallingIdentity(); try { - mService.destroySession(MediaSessionRecord.this); + mService.onSessionDied(MediaSessionRecord.this); } finally { Binder.restoreCallingIdentity(token); } @@ -734,7 +764,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable mIsActive = active; final long token = Binder.clearCallingIdentity(); try { - mService.updateSession(MediaSessionRecord.this); + mService.onSessionActiveStateChanged(MediaSessionRecord.this); } finally { Binder.restoreCallingIdentity(token); } @@ -801,12 +831,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable ? PlaybackState.STATE_NONE : mPlaybackState.getState(); int newState = state == null ? PlaybackState.STATE_NONE : state.getState(); + boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState) + || (!TRANSITION_PRIORITY_STATES.contains(oldState) + && TRANSITION_PRIORITY_STATES.contains(newState)); synchronized (mLock) { mPlaybackState = state; } final long token = Binder.clearCallingIdentity(); try { - mService.onSessionPlaystateChanged(MediaSessionRecord.this, oldState, newState); + mService.onSessionPlaybackStateChanged( + MediaSessionRecord.this, shouldUpdatePriority); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java new file mode 100644 index 000000000000..2cde89a7a6f6 --- /dev/null +++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java @@ -0,0 +1,143 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.media.AudioManager; +import android.os.ResultReceiver; +import android.view.KeyEvent; + +import java.io.PrintWriter; + +/** + * Common interfaces between {@link MediaSessionRecord} and {@link MediaSession2Record}. + */ +public interface MediaSessionRecordImpl extends AutoCloseable { + + /** + * Get the info for this session. + * + * @return Info that identifies this session. + */ + String getPackageName(); + + /** + * Get the UID this session was created for. + * + * @return The UID for this session. + */ + int getUid(); + + /** + * Get the user id this session was created for. + * + * @return The user id for this session. + */ + int getUserId(); + + /** + * Check if this session has system priorty and should receive media buttons + * before any other sessions. + * + * @return True if this is a system priority session, false otherwise + */ + boolean isSystemPriority(); + + /** + * Send a volume adjustment to the session owner. Direction must be one of + * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, + * {@link AudioManager#ADJUST_SAME}. + * + * @param packageName The package that made the original volume request. + * @param opPackageName The op package that made the original volume request. + * @param pid The pid that made the original volume request. + * @param uid The uid that made the original volume request. + * @param asSystemService {@code true} if the event sent to the session as if it was come from + * the system service instead of the app process. This helps sessions to distinguish + * between the key injection by the app and key events from the hardware devices. + * Should be used only when the volume key events aren't handled by foreground + * activity. {@code false} otherwise to tell session about the real caller. + * @param direction The direction to adjust volume in. + * @param flags Any of the flags from {@link AudioManager}. + * @param useSuggested True to use adjustSuggestedStreamVolume instead of + */ + void adjustVolume(String packageName, String opPackageName, int pid, int uid, + boolean asSystemService, int direction, int flags, boolean useSuggested); + + /** + * Check if this session has been set to active by the app. (i.e. ready to receive command and + * getters are available). + * + * @return True if the session is active, false otherwise. + */ + // TODO(jaewan): Find better naming, or remove this from the MediaSessionRecordImpl. + boolean isActive(); + + /** + * Check if the session's playback active state matches with the expectation. This always return + * {@code false} if the playback state is unknown (e.g. {@code null}), where we cannot know the + * actual playback state associated with the session. + * + * @param expected True if playback is expected to be active. false otherwise. + * @return True if the session's playback matches with the expectation. false otherwise. + */ + boolean checkPlaybackActiveState(boolean expected); + + /** + * Check whether the playback type is local or remote. + * <p> + * <ul> + * <li>Local: volume changes the stream volume because playback happens on this device.</li> + * <li>Remote: volume is sent to the apps callback because playback happens on the remote + * device and we cannot know how to control the volume of it.</li> + * </ul> + * + * @return {@code true} if the playback is local. {@code false} if the playback is remote. + */ + boolean isPlaybackTypeLocal(); + + /** + * Sends media button. + * + * @param packageName caller package name + * @param pid caller pid + * @param uid caller uid + * @param asSystemService {@code true} if the event sent to the session as if it was come from + * the system service instead of the app process. + * @param ke key events + * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed. + * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is + * needed. + * @return {@code true} if the attempt to send media button was successfully. + * {@code false} otherwise. + */ + boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, + KeyEvent ke, int sequenceId, ResultReceiver cb); + + /** + * Dumps internal state + * + * @param pw print writer + * @param prefix prefix + */ + void dump(PrintWriter pw, String prefix); + + /** + * Override {@link AutoCloseable#close} to tell not to throw exception. + */ + @Override + void close(); +} diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 0f059dba5274..f71fb582e3ed 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -40,10 +40,7 @@ import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioPlaybackConfiguration; import android.media.AudioSystem; -import android.media.IAudioService; import android.media.IRemoteVolumeController; -import android.media.MediaController2; -import android.media.Session2CommandGroup; import android.media.Session2Token; import android.media.session.IActiveSessionsListener; import android.media.session.IOnMediaKeyEventDispatchedListener; @@ -61,7 +58,6 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Message; import android.os.PowerManager; @@ -123,11 +119,6 @@ public class MediaSessionService extends SystemService implements Monitor { @GuardedBy("mLock") private final ArrayList<SessionsListenerRecord> mSessionsListeners = new ArrayList<SessionsListenerRecord>(); - // Map user id as index to list of Session2Tokens - // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in - // one place. - @GuardedBy("mLock") - private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>(); @GuardedBy("mLock") private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords = new ArrayList<>(); @@ -189,16 +180,11 @@ public class MediaSessionService extends SystemService implements Monitor { updateUser(); } - private IAudioService getAudioService() { - IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); - return IAudioService.Stub.asInterface(b); - } - private boolean isGlobalPriorityActiveLocked() { return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); } - void updateSession(MediaSessionRecord record) { + void onSessionActiveStateChanged(MediaSessionRecordImpl record) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); if (user == null) { @@ -215,12 +201,14 @@ public class MediaSessionService extends SystemService implements Monitor { Log.w(TAG, "Unknown session updated. Ignoring."); return; } - user.mPriorityStack.onSessionStateChange(record); + user.mPriorityStack.onSessionActiveStateChanged(record); } - mHandler.postSessionsChanged(record.getUserId()); + + mHandler.postSessionsChanged(record); } } + // Currently only media1 can become global priority session. void setGlobalPrioritySession(MediaSessionRecord record) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); @@ -266,11 +254,13 @@ public class MediaSessionService extends SystemService implements Monitor { List<Session2Token> getSession2TokensLocked(int userId) { List<Session2Token> list = new ArrayList<>(); if (userId == USER_ALL) { - for (int i = 0; i < mSession2TokensPerUser.size(); i++) { - list.addAll(mSession2TokensPerUser.valueAt(i)); + int size = mUserRecords.size(); + for (int i = 0; i < size; i++) { + list.addAll(mUserRecords.valueAt(i).mPriorityStack.getSession2Tokens(userId)); } } else { - list.addAll(mSession2TokensPerUser.get(userId)); + FullUserRecord user = getFullUserRecordLocked(userId); + list.addAll(user.mPriorityStack.getSession2Tokens(userId)); } return list; } @@ -297,14 +287,15 @@ public class MediaSessionService extends SystemService implements Monitor { } } - void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { + void onSessionPlaybackStateChanged(MediaSessionRecordImpl record, + boolean shouldUpdatePriority) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); if (user == null || !user.mPriorityStack.contains(record)) { Log.d(TAG, "Unknown session changed playback state. Ignoring."); return; } - user.mPriorityStack.onPlaystateChanged(record, oldState, newState); + user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority); } } @@ -347,7 +338,6 @@ public class MediaSessionService extends SystemService implements Monitor { user.destroySessionsForUserLocked(userId); } } - mSession2TokensPerUser.remove(userId); updateUser(); } } @@ -366,13 +356,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } - void sessionDied(MediaSessionRecord session) { - synchronized (mLock) { - destroySessionLocked(session); - } - } - - void destroySession(MediaSessionRecord session) { + void onSessionDied(MediaSessionRecordImpl session) { synchronized (mLock) { destroySessionLocked(session); } @@ -393,9 +377,6 @@ public class MediaSessionService extends SystemService implements Monitor { mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id)); } } - if (mSession2TokensPerUser.get(userInfo.id) == null) { - mSession2TokensPerUser.put(userInfo.id, new ArrayList<>()); - } } } // Ensure that the current full user exists. @@ -405,9 +386,6 @@ public class MediaSessionService extends SystemService implements Monitor { Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); - if (mSession2TokensPerUser.get(currentFullUserId) == null) { - mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>()); - } } mFullUserIds.put(currentFullUserId, currentFullUserId); } @@ -444,7 +422,7 @@ public class MediaSessionService extends SystemService implements Monitor { * 5. We need to unlink to death from the cb binder * 6. We need to tell the session to do any final cleanup (onDestroy) */ - private void destroySessionLocked(MediaSessionRecord session) { + private void destroySessionLocked(MediaSessionRecordImpl session) { if (DEBUG) { Log.d(TAG, "Destroying " + session); } @@ -461,7 +439,7 @@ public class MediaSessionService extends SystemService implements Monitor { } session.close(); - mHandler.postSessionsChanged(session.getUserId()); + mHandler.postSessionsChanged(session); } private void enforcePackageName(String packageName, int uid) { @@ -541,15 +519,6 @@ public class MediaSessionService extends SystemService implements Monitor { return false; } - private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, - String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) - throws RemoteException { - synchronized (mLock) { - return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, - tag, sessionInfo); - } - } - /* * When a session is created the following things need to happen. * 1. Its callback binder needs a link to death @@ -557,29 +526,31 @@ public class MediaSessionService extends SystemService implements Monitor { * 3. It needs to be added to the priority stack. * 4. It needs to be added to the relevant user record. */ - private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, + private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null) { - Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); - throw new RuntimeException("Session request from invalid user."); - } + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); + throw new RuntimeException("Session request from invalid user."); + } - final MediaSessionRecord session; - try { - session = new MediaSessionRecord(callerPid, callerUid, userId, - callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper()); - } catch (RemoteException e) { - throw new RuntimeException("Media Session owner died prematurely.", e); - } + final MediaSessionRecord session; + try { + session = new MediaSessionRecord(callerPid, callerUid, userId, + callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper()); + } catch (RemoteException e) { + throw new RuntimeException("Media Session owner died prematurely.", e); + } - user.mPriorityStack.addSession(session); - mHandler.postSessionsChanged(userId); + user.mPriorityStack.addSession(session); + mHandler.postSessionsChanged(session); - if (DEBUG) { - Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); + if (DEBUG) { + Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); + } + return session; } - return session; } private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { @@ -600,16 +571,16 @@ public class MediaSessionService extends SystemService implements Monitor { return -1; } - private void pushSessionsChanged(int userId) { + private void pushSession1Changed(int userId) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { - Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId); + Log.w(TAG, "pushSession1ChangedOnHandler failed. No user with id=" + userId); return; } List<MediaSessionRecord> records = getActiveSessionsLocked(userId); int size = records.size(); - ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); + ArrayList<MediaSession.Token> tokens = new ArrayList<>(); for (int i = 0; i < size; i++) { tokens.add(records.get(i).getSessionToken()); } @@ -629,6 +600,27 @@ public class MediaSessionService extends SystemService implements Monitor { } } + void pushSession2Changed(int userId) { + synchronized (mLock) { + List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL); + List<Session2Token> session2Tokens = getSession2TokensLocked(userId); + + for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { + Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); + try { + if (listenerRecord.userId == USER_ALL) { + listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); + } else if (listenerRecord.userId == userId) { + listenerRecord.listener.onSession2TokensChanged(session2Tokens); + } + } catch (RemoteException e) { + Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); + mSession2TokensListenerRecords.remove(i); + } + } + } + } + private void pushRemoteVolumeUpdateLocked(int userId) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { @@ -638,8 +630,13 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int size = mRemoteVolumeControllers.beginBroadcast(); - MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); - MediaSession.Token token = record == null ? null : record.getSessionToken(); + MediaSessionRecordImpl record = user.mPriorityStack.getDefaultRemoteSession(userId); + if (record instanceof MediaSession2Record) { + // TODO(jaewan): Implement + return; + } + MediaSession.Token token = record == null + ? null : ((MediaSessionRecord) record).getSessionToken(); for (int i = size - 1; i >= 0; i--) { try { @@ -653,34 +650,15 @@ public class MediaSessionService extends SystemService implements Monitor { } } - void pushSession2TokensChangedLocked(int userId) { - List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL); - List<Session2Token> session2Tokens = getSession2TokensLocked(userId); - - for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { - Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); - try { - if (listenerRecord.userId == USER_ALL) { - listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); - } else if (listenerRecord.userId == userId) { - listenerRecord.listener.onSession2TokensChanged(session2Tokens); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); - mSession2TokensListenerRecords.remove(i); - } - } - } - /** * Called when the media button receiver for the {@code record} is changed. * * @param record the media session whose media button receiver is updated. */ - public void onMediaButtonReceiverChanged(MediaSessionRecord record) { + public void onMediaButtonReceiverChanged(MediaSessionRecordImpl record) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - MediaSessionRecord mediaButtonSession = + MediaSessionRecordImpl mediaButtonSession = user.mPriorityStack.getMediaButtonSession(); if (record == mediaButtonSession) { user.rememberMediaButtonReceiverLocked(mediaButtonSession); @@ -868,39 +846,34 @@ public class MediaSessionService extends SystemService implements Monitor { pw.println(indent + "Restored MediaButtonReceiverComponentType: " + mRestoredMediaButtonReceiverComponentType); mPriorityStack.dump(pw, indent); - pw.println(indent + "Session2Tokens:"); - for (int i = 0; i < mSession2TokensPerUser.size(); i++) { - List<Session2Token> list = mSession2TokensPerUser.valueAt(i); - if (list == null || list.size() == 0) { - continue; - } - for (Session2Token token : list) { - pw.println(indent + " " + token); - } - } } @Override - public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, - MediaSessionRecord newMediaButtonSession) { + public void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession, + MediaSessionRecordImpl newMediaButtonSession) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Media button session is changed to " + newMediaButtonSession); } synchronized (mLock) { if (oldMediaButtonSession != null) { - mHandler.postSessionsChanged(oldMediaButtonSession.getUserId()); + mHandler.postSessionsChanged(oldMediaButtonSession); } if (newMediaButtonSession != null) { rememberMediaButtonReceiverLocked(newMediaButtonSession); - mHandler.postSessionsChanged(newMediaButtonSession.getUserId()); + mHandler.postSessionsChanged(newMediaButtonSession); } pushAddressedPlayerChangedLocked(); } } // Remember media button receiver and keep it in the persistent storage. - public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { - PendingIntent receiver = record.getMediaButtonReceiver(); + public void rememberMediaButtonReceiverLocked(MediaSessionRecordImpl record) { + if (record instanceof MediaSession2Record) { + // TODO(jaewan): Implement + return; + } + MediaSessionRecord sessionRecord = (MediaSessionRecord) record; + PendingIntent receiver = sessionRecord.getMediaButtonReceiver(); mLastMediaButtonReceiver = receiver; mRestoredMediaButtonReceiver = null; mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID; @@ -925,10 +898,15 @@ public class MediaSessionService extends SystemService implements Monitor { private void pushAddressedPlayerChangedLocked( IOnMediaKeyEventSessionChangedListener callback) { try { - MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); + MediaSessionRecordImpl mediaButtonSession = getMediaButtonSessionLocked(); if (mediaButtonSession != null) { - callback.onMediaKeyEventSessionChanged(mediaButtonSession.getPackageName(), - mediaButtonSession.getSessionToken()); + if (mediaButtonSession instanceof MediaSessionRecord) { + MediaSessionRecord session1 = (MediaSessionRecord) mediaButtonSession; + callback.onMediaKeyEventSessionChanged(session1.getPackageName(), + session1.getSessionToken()); + } else { + // TODO(jaewan): Implement + } } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { callback.onMediaKeyEventSessionChanged( mCurrentFullUserRecord.mLastMediaButtonReceiver @@ -951,7 +929,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } - private MediaSessionRecord getMediaButtonSessionLocked() { + private MediaSessionRecordImpl getMediaButtonSessionLocked() { return isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); } @@ -1132,14 +1110,13 @@ public class MediaSessionService extends SystemService implements Monitor { throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid + " but actually=" + sessionToken.getUid()); } - Controller2Callback callback = new Controller2Callback(sessionToken); - // Note: It's safe not to keep controller here because it wouldn't be GC'ed until - // it's closed. - // TODO: Keep controller as well for better readability - // because the GC behavior isn't straightforward. - MediaController2 controller = new MediaController2.Builder(mContext, sessionToken) - .setControllerCallback(new HandlerExecutor(mHandler), callback) - .build(); + MediaSession2Record record = new MediaSession2Record( + sessionToken, MediaSessionService.this, mHandler.getLooper()); + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + user.mPriorityStack.addSession(record); + } + // Do not immediately notify changes -- do so when framework can dispatch command } finally { Binder.restoreCallingIdentity(token); } @@ -1180,7 +1157,8 @@ public class MediaSessionService extends SystemService implements Monitor { null /* optional packageName */); List<Session2Token> result; synchronized (mLock) { - result = getSession2TokensLocked(resolvedUserId); + FullUserRecord user = getFullUserRecordLocked(userId); + result = user.mPriorityStack.getSession2Tokens(resolvedUserId); } return new ParceledListSlice(result); } finally { @@ -2018,7 +1996,7 @@ public class MediaSessionService extends SystemService implements Monitor { private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, int suggestedStream, int direction, int flags) { - MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession + MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); boolean preferSuggestedStream = false; @@ -2109,7 +2087,13 @@ public class MediaSessionService extends SystemService implements Monitor { private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { - MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); + if (mCurrentFullUserRecord.getMediaButtonSessionLocked() + instanceof MediaSession2Record) { + // TODO(jaewan): Implement + return; + } + MediaSessionRecord session = + (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked(); if (session != null) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to " + session); @@ -2389,15 +2373,19 @@ public class MediaSessionService extends SystemService implements Monitor { } final class MessageHandler extends Handler { - private static final int MSG_SESSIONS_CHANGED = 1; - private static final int MSG_VOLUME_INITIAL_DOWN = 2; + private static final int MSG_SESSIONS_1_CHANGED = 1; + private static final int MSG_SESSIONS_2_CHANGED = 2; + private static final int MSG_VOLUME_INITIAL_DOWN = 3; private final SparseArray<Integer> mIntegerCache = new SparseArray<>(); @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_SESSIONS_CHANGED: - pushSessionsChanged((int) msg.obj); + case MSG_SESSIONS_1_CHANGED: + pushSession1Changed((int) msg.obj); + break; + case MSG_SESSIONS_2_CHANGED: + pushSession2Changed((int) msg.obj); break; case MSG_VOLUME_INITIAL_DOWN: synchronized (mLock) { @@ -2412,41 +2400,19 @@ public class MediaSessionService extends SystemService implements Monitor { } } - public void postSessionsChanged(int userId) { + public void postSessionsChanged(MediaSessionRecordImpl record) { // Use object instead of the arguments when posting message to remove pending requests. - Integer userIdInteger = mIntegerCache.get(userId); + Integer userIdInteger = mIntegerCache.get(record.getUserId()); if (userIdInteger == null) { - userIdInteger = Integer.valueOf(userId); - mIntegerCache.put(userId, userIdInteger); + userIdInteger = Integer.valueOf(record.getUserId()); + mIntegerCache.put(record.getUserId(), userIdInteger); } - removeMessages(MSG_SESSIONS_CHANGED, userIdInteger); - obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget(); - } - } - private class Controller2Callback extends MediaController2.ControllerCallback { - private final Session2Token mToken; - - Controller2Callback(Session2Token token) { - mToken = token; - } - - @Override - public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mToken.getUid()); - mSession2TokensPerUser.get(userId).add(mToken); - pushSession2TokensChangedLocked(userId); - } - } - - @Override - public void onDisconnected(MediaController2 controller) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mToken.getUid()); - mSession2TokensPerUser.get(userId).remove(mToken); - pushSession2TokensChangedLocked(userId); - } + int msg = (record instanceof MediaSessionRecord) + ? MSG_SESSIONS_1_CHANGED : MSG_SESSIONS_2_CHANGED; + removeMessages(msg, userIdInteger); + obtainMessage(msg, userIdInteger).sendToTarget(); } } + } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 732563f6e05e..7bb7cf4b74ad 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -16,8 +16,8 @@ package com.android.server.media; +import android.media.Session2Token; import android.media.session.MediaSession; -import android.media.session.PlaybackState; import android.os.Debug; import android.os.UserHandle; import android.util.IntArray; @@ -45,51 +45,30 @@ class MediaSessionStack { /** * Called when the media button session is changed. */ - void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, - MediaSessionRecord newMediaButtonSession); + void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession, + MediaSessionRecordImpl newMediaButtonSession); } /** - * These are states that usually indicate the user took an action and should - * bump priority regardless of the old state. + * Sorted list of the media sessions */ - private static final int[] ALWAYS_PRIORITY_STATES = { - PlaybackState.STATE_FAST_FORWARDING, - PlaybackState.STATE_REWINDING, - PlaybackState.STATE_SKIPPING_TO_PREVIOUS, - PlaybackState.STATE_SKIPPING_TO_NEXT }; - /** - * These are states that usually indicate the user took an action if they - * were entered from a non-priority state. - */ - private static final int[] TRANSITION_PRIORITY_STATES = { - PlaybackState.STATE_BUFFERING, - PlaybackState.STATE_CONNECTING, - PlaybackState.STATE_PLAYING }; - - /** - * Sorted list of the media sessions. - * The session of which PlaybackState is changed to ALWAYS_PRIORITY_STATES or - * TRANSITION_PRIORITY_STATES comes first. - * @see #shouldUpdatePriority - */ - private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); + private final List<MediaSessionRecordImpl> mSessions = new ArrayList<>(); private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener; /** * The media button session which receives media key events. - * It could be null if the previous media buttion session is released. + * It could be null if the previous media button session is released. */ - private MediaSessionRecord mMediaButtonSession; + private MediaSessionRecordImpl mMediaButtonSession; - private MediaSessionRecord mCachedVolumeDefault; + private MediaSessionRecordImpl mCachedVolumeDefault; /** * Cache the result of the {@link #getActiveSessions} per user. */ - private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists = + private final SparseArray<List<MediaSessionRecord>> mCachedActiveLists = new SparseArray<>(); MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) { @@ -102,7 +81,7 @@ class MediaSessionStack { * * @param record The record to add. */ - public void addSession(MediaSessionRecord record) { + public void addSession(MediaSessionRecordImpl record) { mSessions.add(record); clearCache(record.getUserId()); @@ -117,7 +96,7 @@ class MediaSessionStack { * * @param record The record to remove. */ - public void removeSession(MediaSessionRecord record) { + public void removeSession(MediaSessionRecordImpl record) { mSessions.remove(record); if (mMediaButtonSession == record) { // When the media button session is removed, nullify the media button session and do not @@ -131,7 +110,7 @@ class MediaSessionStack { /** * Return if the record exists in the priority tracker. */ - public boolean contains(MediaSessionRecord record) { + public boolean contains(MediaSessionRecordImpl record) { return mSessions.contains(record); } @@ -142,9 +121,12 @@ class MediaSessionStack { * @return the MediaSessionRecord. Can be {@code null} if the session is gone meanwhile. */ public MediaSessionRecord getMediaSessionRecord(MediaSession.Token sessionToken) { - for (MediaSessionRecord record : mSessions) { - if (Objects.equals(record.getSessionToken(), sessionToken)) { - return record; + for (MediaSessionRecordImpl record : mSessions) { + if (record instanceof MediaSessionRecord) { + MediaSessionRecord session1 = (MediaSessionRecord) record; + if (Objects.equals(session1.getSessionToken(), sessionToken)) { + return session1; + } } } return null; @@ -154,15 +136,15 @@ class MediaSessionStack { * Notify the priority tracker that a session's playback state changed. * * @param record The record that changed. - * @param oldState Its old playback state. - * @param newState Its new playback state. + * @param shouldUpdatePriority {@code true} if the record needs to prioritized */ - public void onPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { - if (shouldUpdatePriority(oldState, newState)) { + public void onPlaybackStateChanged( + MediaSessionRecordImpl record, boolean shouldUpdatePriority) { + if (shouldUpdatePriority) { mSessions.remove(record); mSessions.add(0, record); clearCache(record.getUserId()); - } else if (!MediaSession.isActiveState(newState)) { + } else if (record.checkPlaybackActiveState(false)) { // Just clear the volume cache when a state goes inactive mCachedVolumeDefault = null; } @@ -172,7 +154,7 @@ class MediaSessionStack { // In that case, we pick the media session whose PlaybackState matches // the audio playback configuration. if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) { - MediaSessionRecord newMediaButtonSession = + MediaSessionRecordImpl newMediaButtonSession = findMediaButtonSession(mMediaButtonSession.getUid()); if (newMediaButtonSession != mMediaButtonSession) { updateMediaButtonSession(newMediaButtonSession); @@ -185,7 +167,7 @@ class MediaSessionStack { * * @param record The record that changed. */ - public void onSessionStateChange(MediaSessionRecord record) { + public void onSessionActiveStateChanged(MediaSessionRecordImpl record) { // For now just clear the cache. Eventually we'll selectively clear // depending on what changed. clearCache(record.getUserId()); @@ -203,7 +185,7 @@ class MediaSessionStack { } IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); for (int i = 0; i < audioPlaybackUids.size(); i++) { - MediaSessionRecord mediaButtonSession = + MediaSessionRecordImpl mediaButtonSession = findMediaButtonSession(audioPlaybackUids.get(i)); if (mediaButtonSession != null) { // Found the media button session. @@ -225,9 +207,9 @@ class MediaSessionStack { * @return The media button session. Returns {@code null} if the app doesn't have a media * session. */ - private MediaSessionRecord findMediaButtonSession(int uid) { - MediaSessionRecord mediaButtonSession = null; - for (MediaSessionRecord session : mSessions) { + private MediaSessionRecordImpl findMediaButtonSession(int uid) { + MediaSessionRecordImpl mediaButtonSession = null; + for (MediaSessionRecordImpl session : mSessions) { if (uid == session.getUid()) { if (session.checkPlaybackActiveState( mAudioPlayerStateMonitor.isPlaybackActive(session.getUid()))) { @@ -253,8 +235,8 @@ class MediaSessionStack { * for all users in this {@link MediaSessionStack}. * @return All the active sessions in priority order. */ - public ArrayList<MediaSessionRecord> getActiveSessions(int userId) { - ArrayList<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId); + public List<MediaSessionRecord> getActiveSessions(int userId) { + List<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId); if (cachedActiveList == null) { cachedActiveList = getPriorityList(true, userId); mCachedActiveLists.put(userId, cachedActiveList); @@ -263,26 +245,46 @@ class MediaSessionStack { } /** + * Gets the session2 tokens. + * + * @param userId The user to check. It can be {@link UserHandle#USER_ALL} to get all session2 + * tokens for all users in this {@link MediaSessionStack}. + * @return All session2 tokens. + */ + public List<Session2Token> getSession2Tokens(int userId) { + ArrayList<Session2Token> session2Records = new ArrayList<>(); + for (MediaSessionRecordImpl record : mSessions) { + if ((userId == UserHandle.USER_ALL || record.getUserId() == userId) + && record.isActive() + && record instanceof MediaSession2Record) { + MediaSession2Record session2 = (MediaSession2Record) record; + session2Records.add(session2.getSession2Token()); + } + } + return session2Records; + } + + /** * Get the media button session which receives the media button events. * * @return The media button session or null. */ - public MediaSessionRecord getMediaButtonSession() { + public MediaSessionRecordImpl getMediaButtonSession() { return mMediaButtonSession; } - private void updateMediaButtonSession(MediaSessionRecord newMediaButtonSession) { - MediaSessionRecord oldMediaButtonSession = mMediaButtonSession; + private void updateMediaButtonSession(MediaSessionRecordImpl newMediaButtonSession) { + MediaSessionRecordImpl oldMediaButtonSession = mMediaButtonSession; mMediaButtonSession = newMediaButtonSession; mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged( oldMediaButtonSession, newMediaButtonSession); } - public MediaSessionRecord getDefaultVolumeSession() { + public MediaSessionRecordImpl getDefaultVolumeSession() { if (mCachedVolumeDefault != null) { return mCachedVolumeDefault; } - ArrayList<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL); + List<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL); int size = records.size(); for (int i = 0; i < size; i++) { MediaSessionRecord record = records.get(i); @@ -294,13 +296,13 @@ class MediaSessionStack { return null; } - public MediaSessionRecord getDefaultRemoteSession(int userId) { - ArrayList<MediaSessionRecord> records = getPriorityList(true, userId); + public MediaSessionRecordImpl getDefaultRemoteSession(int userId) { + List<MediaSessionRecord> records = getPriorityList(true, userId); int size = records.size(); for (int i = 0; i < size; i++) { MediaSessionRecord record = records.get(i); - if (!record.isPlaybackLocal()) { + if (!record.isPlaybackTypeLocal()) { return record; } } @@ -308,16 +310,11 @@ class MediaSessionStack { } public void dump(PrintWriter pw, String prefix) { - ArrayList<MediaSessionRecord> sortedSessions = getPriorityList(false, - UserHandle.USER_ALL); - int count = sortedSessions.size(); pw.println(prefix + "Media button session is " + mMediaButtonSession); - pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); + pw.println(prefix + "Sessions Stack - have " + mSessions.size() + " sessions:"); String indent = prefix + " "; - for (int i = 0; i < count; i++) { - MediaSessionRecord record = sortedSessions.get(i); + for (MediaSessionRecordImpl record : mSessions) { record.dump(pw, indent); - pw.println(); } } @@ -335,17 +332,19 @@ class MediaSessionStack { * will return sessions for all users. * @return The priority sorted list of sessions. */ - public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) { - ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); + public List<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) { + List<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); int lastPlaybackActiveIndex = 0; int lastActiveIndex = 0; - int size = mSessions.size(); - for (int i = 0; i < size; i++) { - final MediaSessionRecord session = mSessions.get(i); + for (MediaSessionRecordImpl record : mSessions) { + if (!(record instanceof MediaSessionRecord)) { + continue; + } + final MediaSessionRecord session = (MediaSessionRecord) record; - if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { - // Filter out sessions for the wrong user + if ((userId != UserHandle.USER_ALL && userId != session.getUserId())) { + // Filter out sessions for the wrong user or session2. continue; } @@ -369,26 +368,6 @@ class MediaSessionStack { return result; } - private boolean shouldUpdatePriority(int oldState, int newState) { - if (containsState(newState, ALWAYS_PRIORITY_STATES)) { - return true; - } - if (!containsState(oldState, TRANSITION_PRIORITY_STATES) - && containsState(newState, TRANSITION_PRIORITY_STATES)) { - return true; - } - return false; - } - - private boolean containsState(int state, int[] states) { - for (int i = 0; i < states.length; i++) { - if (states[i] == state) { - return true; - } - } - return false; - } - private void clearCache(int userId) { mCachedVolumeDefault = null; mCachedActiveLists.remove(userId); diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 8fdfcbfb2ad9..0ea4e63231d4 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -48,8 +48,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { static final String BLUETOOTH_ROUTE_ID = "BLUETOOTH_ROUTE"; // TODO: Move these to a proper place - public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO"; - public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO"; + public static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO"; + public static final String TYPE_LIVE_VIDEO = "android.media.intent.route.TYPE_LIVE_VIDEO"; private final AudioManager mAudioManager; private final IAudioService mAudioService; @@ -91,44 +91,44 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void requestCreateSession(String packageName, String routeId, String controlCategory, + public void requestCreateSession(String packageName, String routeId, String routeType, long requestId) { // Do nothing } @Override - public void releaseSession(int sessionId) { + public void releaseSession(String sessionId) { // Do nothing } @Override - public void selectRoute(int sessionId, MediaRoute2Info route) { + public void selectRoute(String sessionId, String routeId) { //TODO: implement method } @Override - public void deselectRoute(int sessionId, MediaRoute2Info route) { + public void deselectRoute(String sessionId, String routeId) { //TODO: implement method } @Override - public void transferToRoute(int sessionId, MediaRoute2Info route) { + public void transferToRoute(String sessionId, String routeId) { //TODO: implement method } //TODO: implement method @Override - public void sendControlRequest(@NonNull MediaRoute2Info route, @NonNull Intent request) { + public void sendControlRequest(@NonNull String routeId, @NonNull Intent request) { } //TODO: implement method @Override - public void requestSetVolume(MediaRoute2Info route, int volume) { + public void requestSetVolume(String routeId, int volume) { } //TODO: implement method @Override - public void requestUpdateVolume(MediaRoute2Info route, int delta) { + public void requestUpdateVolume(String routeId, int delta) { } void initializeRoutes() { @@ -141,8 +141,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)) - .addSupportedCategory(CATEGORY_LIVE_AUDIO) - .addSupportedCategory(CATEGORY_LIVE_VIDEO) + .addRouteType(TYPE_LIVE_AUDIO) + .addRouteType(TYPE_LIVE_VIDEO) .build(); AudioRoutesInfo newAudioRoutes = null; @@ -181,8 +181,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)) - .addSupportedCategory(CATEGORY_LIVE_AUDIO) - .addSupportedCategory(CATEGORY_LIVE_VIDEO) + .addRouteType(TYPE_LIVE_AUDIO) + .addRouteType(TYPE_LIVE_VIDEO) .build(); if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) { @@ -193,7 +193,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mCurAudioRoutesInfo.bluetoothName) .setDescription(mContext.getResources().getText( R.string.bluetooth_a2dp_audio_route_name).toString()) - .addSupportedCategory(CATEGORY_LIVE_AUDIO) + .addRouteType(TYPE_LIVE_AUDIO) .build(); } else { mBluetoothA2dpRoute = null; diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index 3ca1803262e7..22b01bee6c6a 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -229,7 +229,7 @@ public class NetworkStatsFactory { entry.txPackets += reader.nextLong(); } - stats.addValues(entry); + stats.addEntry(entry); reader.finishLine(); } } catch (NullPointerException|NumberFormatException e) { @@ -279,7 +279,7 @@ public class NetworkStatsFactory { entry.txBytes = reader.nextLong(); entry.txPackets = reader.nextLong(); - stats.addValues(entry); + stats.addEntry(entry); reader.finishLine(); } } catch (NullPointerException|NumberFormatException e) { @@ -439,7 +439,7 @@ public class NetworkStatsFactory { if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface)) && (limitUid == UID_ALL || limitUid == entry.uid) && (limitTag == TAG_ALL || limitTag == entry.tag)) { - stats.addValues(entry); + stats.addEntry(entry); } reader.finishLine(); diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index ec8a8e7c4c1a..7a6f29764f09 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -27,6 +27,7 @@ import static android.net.ConnectivityManager.isNetworkTypeMobile; import static android.net.NetworkStack.checkNetworkStackPermission; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.IFACE_VT; import static android.net.NetworkStats.INTERFACES_ALL; import static android.net.NetworkStats.METERED_ALL; import static android.net.NetworkStats.ROAMING_ALL; @@ -211,7 +212,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** * Virtual network interface for video telephony. This is for VT data usage counting purpose. */ - public static final String VT_INTERFACE = "vt_data0"; + // TODO: Remove this after no one is using it. + public static final String VT_INTERFACE = NetworkStats.IFACE_VT; /** * Settings that can be changed externally. @@ -712,7 +714,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); final NetworkStats stats = new NetworkStats(end - start, 1); - stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, + stats.addEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets, entry.txBytes, entry.txPackets, entry.operations)); return stats; @@ -1179,8 +1181,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), ident.getRoaming(), true /* metered */, true /* onDefaultNetwork */); - findOrCreateNetworkIdentitySet(mActiveIfaces, VT_INTERFACE).add(vtIdent); - findOrCreateNetworkIdentitySet(mActiveUidIfaces, VT_INTERFACE).add(vtIdent); + findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent); + findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent); } if (isMobile) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a4f4d079df27..728b297b4d8e 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2481,7 +2481,7 @@ public class NotificationManagerService extends SystemService { .setUid(r.sbn.getUid()) .setChannelId(r.getChannel().getId()) .setChannelName(r.getChannel().getName().toString()) - .setPostedTimeMs(r.sbn.getPostTime()) + .setPostedTimeMs(System.currentTimeMillis()) .setTitle(getHistoryTitle(r.getNotification())) .setText(getHistoryText( r.sbn.getPackageContext(getContext()), r.getNotification())) diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS new file mode 100644 index 000000000000..5a19656b36a6 --- /dev/null +++ b/services/core/java/com/android/server/notification/OWNERS @@ -0,0 +1,4 @@ +dsandler@android.com +juliacr@google.com +beverlyt@google.com +pixel@google.com diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index b25e1e2160c3..ed7139991937 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -15,35 +15,45 @@ */ package com.android.server.pm; +import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; +import android.Manifest; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; +import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IApplicationThread; import android.app.admin.DevicePolicyEventLogger; +import android.app.admin.DevicePolicyManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ICrossProfileApps; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; +import com.android.internal.app.IAppOpsService; +import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; +import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerInternal; import java.util.ArrayList; @@ -55,6 +65,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private Context mContext; private Injector mInjector; + private AppOpsService mAppOpsService; + private final DevicePolicyManagerInternal mDpmi; + private final IPackageManager mIpm; public CrossProfileAppsServiceImpl(Context context) { this(context, new InjectorImpl(context)); @@ -64,6 +77,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { CrossProfileAppsServiceImpl(Context context, Injector injector) { mContext = context; mInjector = injector; + mIpm = AppGlobals.getPackageManager(); + mDpmi = LocalServices.getService(DevicePolicyManagerInternal.class); } @Override @@ -153,6 +168,63 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { userId); } + @Override + public boolean canRequestInteractAcrossProfiles(String callingPackage) { + Objects.requireNonNull(callingPackage); + verifyCallingPackage(callingPackage); + + final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked( + callingPackage, mInjector.getCallingUserId()); + if (targetUserProfiles.isEmpty()) { + return false; + } + + if (!hasRequestedAppOpPermission( + AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), callingPackage)) { + return false; + } + return isCrossProfilePackageWhitelisted(callingPackage); + } + + private boolean hasRequestedAppOpPermission(String permission, String packageName) { + try { + String[] packages = mIpm.getAppOpPermissionPackages(permission); + return ArrayUtils.contains(packages, packageName); + } catch (RemoteException exc) { + Slog.e(TAG, "PackageManager dead. Cannot get permission info"); + return false; + } + } + + @Override + public boolean canInteractAcrossProfiles(String callingPackage) { + Objects.requireNonNull(callingPackage); + verifyCallingPackage(callingPackage); + + final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked( + callingPackage, mInjector.getCallingUserId()); + if (targetUserProfiles.isEmpty()) { + return false; + } + + final int callingUid = mInjector.getCallingUid(); + return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid) + || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid) + || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid) + || AppOpsManager.MODE_ALLOWED == getAppOpsService().noteOperation( + OP_INTERACT_ACROSS_PROFILES, callingUid, callingPackage, /* featureId= */ null, + /*shouldCollectAsyncNotedOp= */false, /*message= */null); + } + + private boolean isCrossProfilePackageWhitelisted(String packageName) { + final long ident = mInjector.clearCallingIdentity(); + try { + return mDpmi.getAllCrossProfilePackages().contains(packageName); + } finally { + mInjector.restoreCallingIdentity(ident); + } + } + private List<UserHandle> getTargetUserProfilesUnchecked( String callingPackage, @UserIdInt int callingUserId) { final long ident = mInjector.clearCallingIdentity(); @@ -239,6 +311,19 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage); } + private static boolean isPermissionGranted(String permission, int uid) { + return PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission( + permission, uid, /* owningUid= */-1, /* exported= */ true); + } + + private AppOpsService getAppOpsService() { + if (mAppOpsService == null) { + IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); + mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b); + } + return mAppOpsService; + } + private static class InjectorImpl implements Injector { private Context mContext; diff --git a/services/core/java/com/android/server/pm/InstallSource.java b/services/core/java/com/android/server/pm/InstallSource.java index 0541797523a4..6684e3f8973e 100644 --- a/services/core/java/com/android/server/pm/InstallSource.java +++ b/services/core/java/com/android/server/pm/InstallSource.java @@ -18,6 +18,8 @@ package com.android.server.pm; import android.annotation.Nullable; +import com.android.internal.util.Preconditions; + import java.util.Objects; /** @@ -29,16 +31,27 @@ final class InstallSource { * An instance of InstallSource representing an absence of knowledge of the source of * a package. Used in preference to null. */ - static final InstallSource EMPTY = new InstallSource(null, null, null, false); + static final InstallSource EMPTY = new InstallSource(null, null, null, false, false, null); /** We also memoize this case because it is common - all un-updated system apps. */ - private static final InstallSource EMPTY_ORPHANED = new InstallSource(null, null, null, true); + private static final InstallSource EMPTY_ORPHANED = new InstallSource( + null, null, null, true, false, null); - /** The package that requested the installation, if known. */ + /** + * The package that requested the installation, if known. May not correspond to a currently + * installed package if {@link #isInitiatingPackageUninstalled} is true. + */ @Nullable final String initiatingPackageName; /** + * The signing details of the initiating package, if known. Always null if + * {@link #initiatingPackageName} is null. + */ + @Nullable + final PackageSignatures initiatingPackageSignatures; + + /** * The package on behalf of which the initiating package requested the installation, if any. * For example if a downloaded APK is installed via the Package Installer this could be the * app that performed the download. This value is provided by the initiating package and not @@ -57,76 +70,120 @@ final class InstallSource { /** Indicates if the package that was the installerPackageName has been uninstalled. */ final boolean isOrphaned; + /** + * Indicates if the package in initiatingPackageName has been uninstalled. Always false if + * {@link #initiatingPackageName} is null. + */ + final boolean isInitiatingPackageUninstalled; + + static InstallSource create(@Nullable String initiatingPackageName, + @Nullable String originatingPackageName, @Nullable String installerPackageName) { + return create(initiatingPackageName, originatingPackageName, installerPackageName, + false, false); + } + static InstallSource create(@Nullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, - boolean isOrphaned) { + boolean isOrphaned, boolean isInitiatingPackageUninstalled) { return createInternal( intern(initiatingPackageName), intern(originatingPackageName), intern(installerPackageName), - isOrphaned); + isOrphaned, isInitiatingPackageUninstalled, null); } private static InstallSource createInternal(@Nullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, - boolean isOrphaned) { + boolean isOrphaned, boolean isInitiatingPackageUninstalled, + @Nullable PackageSignatures initiatingPackageSignatures) { if (initiatingPackageName == null && originatingPackageName == null - && installerPackageName == null) { + && installerPackageName == null && initiatingPackageSignatures == null + && !isInitiatingPackageUninstalled) { return isOrphaned ? EMPTY_ORPHANED : EMPTY; } return new InstallSource(initiatingPackageName, originatingPackageName, - installerPackageName, isOrphaned); + installerPackageName, isOrphaned, isInitiatingPackageUninstalled, + initiatingPackageSignatures + ); } private InstallSource(@Nullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, - boolean isOrphaned) { + boolean isOrphaned, boolean isInitiatingPackageUninstalled, + @Nullable PackageSignatures initiatingPackageSignatures) { + if (initiatingPackageName == null) { + Preconditions.checkArgument(initiatingPackageSignatures == null); + Preconditions.checkArgument(!isInitiatingPackageUninstalled); + } this.initiatingPackageName = initiatingPackageName; this.originatingPackageName = originatingPackageName; this.installerPackageName = installerPackageName; this.isOrphaned = isOrphaned; + this.isInitiatingPackageUninstalled = isInitiatingPackageUninstalled; + this.initiatingPackageSignatures = initiatingPackageSignatures; } /** - * Return an InstallSource the same as this one except with the specified installerPackageName. + * Return an InstallSource the same as this one except with the specified + * {@link #installerPackageName}. */ - InstallSource setInstallerPackage(String installerPackageName) { + InstallSource setInstallerPackage(@Nullable String installerPackageName) { if (Objects.equals(installerPackageName, this.installerPackageName)) { return this; } return createInternal(initiatingPackageName, originatingPackageName, - intern(installerPackageName), isOrphaned); + intern(installerPackageName), isOrphaned, isInitiatingPackageUninstalled, + initiatingPackageSignatures + ); } /** - * Return an InstallSource the same as this one except with the specified value for isOrphaned. + * Return an InstallSource the same as this one except with the specified value for + * {@link #isOrphaned}. */ InstallSource setIsOrphaned(boolean isOrphaned) { if (isOrphaned == this.isOrphaned) { return this; } return createInternal(initiatingPackageName, originatingPackageName, installerPackageName, - isOrphaned); + isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures); } /** - * Return an InstallSource the same as this one except it does not refer to the specified - * installer package name (which is being uninstalled). + * Return an InstallSource the same as this one except with the specified + * {@link #initiatingPackageSignatures}. */ - InstallSource removeInstallerPackage(String packageName) { + InstallSource setInitiatingPackageSignatures(@Nullable PackageSignatures signatures) { + if (signatures == initiatingPackageSignatures) { + return this; + } + return createInternal(initiatingPackageName, originatingPackageName, installerPackageName, + isOrphaned, isInitiatingPackageUninstalled, signatures); + } + + /** + * Return an InstallSource the same as this one updated to reflect that the specified installer + * package name has been uninstalled. + */ + InstallSource removeInstallerPackage(@Nullable String packageName) { if (packageName == null) { return this; } boolean modified = false; - String initiatingPackageName = this.initiatingPackageName; + boolean isInitiatingPackageUninstalled = this.isInitiatingPackageUninstalled; String originatingPackageName = this.originatingPackageName; String installerPackageName = this.installerPackageName; boolean isOrphaned = this.isOrphaned; - if (packageName.equals(initiatingPackageName)) { - initiatingPackageName = null; - modified = true; + if (packageName.equals(this.initiatingPackageName)) { + if (!isInitiatingPackageUninstalled) { + // In this case we deliberately do not clear the package name (and signatures). + // We allow an app to retrieve details of its own install initiator even after + // it has been uninstalled. + isInitiatingPackageUninstalled = true; + modified = true; + } } if (packageName.equals(originatingPackageName)) { originatingPackageName = null; @@ -141,8 +198,9 @@ final class InstallSource { if (!modified) { return this; } + return createInternal(initiatingPackageName, originatingPackageName, installerPackageName, - isOrphaned); + isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures); } @Nullable diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index e2dfa126225f..c43f23454ca6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -635,7 +635,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } InstallSource installSource = InstallSource.create(installerPackageName, - originatingPackageName, requestedInstallerPackageName, false); + originatingPackageName, requestedInstallerPackageName); session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this, mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid, installSource, params, createdMillis, diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 426cd01d3d0b..97a2d43e6183 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -140,6 +140,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -209,6 +210,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; private static final int[] EMPTY_CHILD_SESSION_ARRAY = {}; + private static final String SYSTEM_DATA_LOADER_PACKAGE = "android"; + // TODO: enforce INSTALL_ALLOW_TEST // TODO: enforce INSTALL_ALLOW_DOWNGRADE @@ -555,6 +558,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.dataLoaderParams); } } + + if (isStreamingInstallation() + && this.params.dataLoaderParams.getComponentName().getPackageName() + == SYSTEM_DATA_LOADER_PACKAGE) { + assertShellOrSystemCalling("System data loaders"); + } } public SessionInfo generateInfo() { @@ -770,6 +779,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private void assertShellOrSystemCalling(String operation) { + switch (Binder.getCallingUid()) { + case android.os.Process.SHELL_UID: + case android.os.Process.ROOT_UID: + case android.os.Process.SYSTEM_UID: + break; + default: + throw new SecurityException(operation + " only supported from shell or system"); + } + } + private void assertCanWrite(boolean reverseMode) { if (isDataLoaderInstallation()) { throw new IllegalStateException( @@ -780,15 +800,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { assertPreparedAndNotSealedLocked("assertCanWrite"); } if (reverseMode) { - switch (Binder.getCallingUid()) { - case android.os.Process.SHELL_UID: - case android.os.Process.ROOT_UID: - case android.os.Process.SYSTEM_UID: - break; - default: - throw new SecurityException( - "Reverse mode only supported from shell or system"); - } + assertShellOrSystemCalling("Reverse mode"); } } @@ -1025,13 +1037,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } - private class FileSystemConnector extends IPackageInstallerSessionFileSystemConnector.Stub { + private final class FileSystemConnector extends + IPackageInstallerSessionFileSystemConnector.Stub { + final Set<String> mAddedFiles; + + FileSystemConnector(List<InstallationFile> addedFiles) { + mAddedFiles = addedFiles.stream().map(file -> file.getName()).collect( + Collectors.toSet()); + } + @Override public void writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd) { if (incomingFd == null) { throw new IllegalArgumentException("incomingFd can't be null"); } + if (!mAddedFiles.contains(name)) { + throw new SecurityException("File name is not in the list of added files."); + } try { doWriteInternal(name, offsetBytes, lengthBytes, incomingFd); } catch (IOException e) { @@ -1452,7 +1475,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } mInstallerUid = uid; - mInstallSource = InstallSource.create(packageName, null, packageName, false); + mInstallSource = InstallSource.create(packageName, null, packageName); } } catch (PackageManager.NameNotFoundException e) { onSessionTransferStatus(statusReceiver, packageName, @@ -2467,8 +2490,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } - FileSystemConnector connector = new FileSystemConnector(); - List<InstallationFile> addedFiles = mFiles.stream().filter( file -> sAddedFilter.accept(new File(file.name))).map( file -> new InstallationFile( @@ -2480,6 +2501,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect( Collectors.toList()); + final FileSystemConnector connector = new FileSystemConnector(addedFiles); + DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); if (dataLoaderManager == null) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, @@ -2515,11 +2538,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; - final DataLoaderParams params = this.params.dataLoaderParams; - final FileSystemControlParcel control = new FileSystemControlParcel(); control.callback = connector; + final DataLoaderParams params = this.params.dataLoaderParams; + Bundle dataLoaderParams = new Bundle(); dataLoaderParams.putParcelable("componentName", params.getComponentName()); dataLoaderParams.putParcelable("control", control); @@ -3115,7 +3138,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } InstallSource installSource = InstallSource.create(installInitiatingPackageName, - installOriginatingPackageName, installerPackageName, false); + installOriginatingPackageName, installerPackageName); return new PackageInstallerSession(callback, context, pm, sessionProvider, installerThread, stagingManager, sessionId, userId, installerUid, installSource, params, createdMillis, stageDir, stageCid, fileInfosArray, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 70550ee27b8e..b26e6c7021c1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -13452,9 +13452,7 @@ public class PackageManagerService extends IPackageManager.Stub // Okay! targetPackageSetting.setInstallerPackageName(installerPackageName); - if (installerPackageName != null) { - mSettings.mInstallerPackages.add(installerPackageName); - } + mSettings.addInstallerPackageNames(targetPackageSetting.installSource); scheduleWriteSettingsLocked(); } } @@ -15160,9 +15158,10 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); final String pkgName = pkg.getPackageName(); - final String installerPackageName = installArgs.installSource.installerPackageName; final int[] installedForUsers = res.origUsers; final int installReason = installArgs.installReason; + InstallSource installSource = installArgs.installSource; + final String installerPackageName = installSource.installerPackageName; if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getCodePath()); synchronized (mLock) { @@ -15171,7 +15170,7 @@ public class PackageManagerService extends IPackageManager.Stub // For system-bundled packages, we assume that installing an upgraded version // of the package implies that the user actually wants to run that new code, // so we enable the package. - PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.mPackages.get(pkgName); final int userId = installArgs.user.getIdentifier(); if (ps != null) { if (isSystemApp(pkg)) { @@ -15208,8 +15207,16 @@ public class PackageManagerService extends IPackageManager.Stub ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName); } - ps.setInstallSource(installArgs.installSource); - + if (installSource.initiatingPackageName != null) { + final PackageSetting ips = mSettings.mPackages.get( + installSource.initiatingPackageName); + if (ips != null) { + installSource = installSource.setInitiatingPackageSignatures( + ips.signatures); + } + } + ps.setInstallSource(installSource); + mSettings.addInstallerPackageNames(installSource); // When replacing an existing package, preserve the original install reason for all // users that had the package installed before. @@ -15239,7 +15246,6 @@ public class PackageManagerService extends IPackageManager.Stub res.name = pkgName; res.uid = pkg.getUid(); res.pkg = pkg; - mSettings.setInstallerPackageName(pkgName, installerPackageName); res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); //to update install status Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); @@ -19374,7 +19380,7 @@ public class PackageManagerService extends IPackageManager.Stub // PermissionController manages default home directly. return false; } - mPermissionManager.setDefaultHome(currentPackageName, userId, (successful) -> { + mPermissionManager.setDefaultHome(packageName, userId, (successful) -> { if (successful) { postPreferredActivityChangedBroadcast(userId); } @@ -19978,19 +19984,25 @@ public class PackageManagerService extends IPackageManager.Stub } } - // All installSource strings are interned, so == is ok here - if (installSource.initiatingPackageName == installSource.installerPackageName) { - // The installer and initiator will often be the same, and when they are - // we can skip doing the same check again. - initiatingPackageName = installerPackageName; + if (installSource.isInitiatingPackageUninstalled) { + // TODO(b/146555198) Allow the app itself to see the info + // (at least for non-instant apps) + initiatingPackageName = null; } else { - initiatingPackageName = installSource.initiatingPackageName; - final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName); - if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { - initiatingPackageName = null; + // All installSource strings are interned, so == is ok here + if (installSource.initiatingPackageName == installSource.installerPackageName) { + // The installer and initiator will often be the same, and when they are + // we can skip doing the same check again. + initiatingPackageName = installerPackageName; + } else { + initiatingPackageName = installSource.initiatingPackageName; + final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName); + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { + initiatingPackageName = null; + } } - } + originatingPackageName = installSource.originatingPackageName; if (originatingPackageName != null) { final PackageSetting ps = mSettings.mPackages.get(originatingPackageName); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 10e2780863d8..5adab378bb73 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -34,7 +34,6 @@ 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; @@ -137,10 +136,6 @@ 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 DATA_LOADER_PACKAGE = "android"; - private static final String DATA_LOADER_CLASS = - "com.android.server.pm.PackageManagerShellCommandDataLoader"; - final IPackageManager mInterface; final IPermissionManager mPermissionManager; final private WeakHashMap<String, Resources> mResourceCache = @@ -164,7 +159,7 @@ class PackageManagerShellCommand extends ShellCommand { final PrintWriter pw = getOutPrintWriter(); try { - switch(cmd) { + switch (cmd) { case "path": return runPath(); case "dump": @@ -1163,9 +1158,8 @@ class PackageManagerShellCommand extends ShellCommand { private int runStreamingInstall() throws RemoteException { final InstallParams params = makeInstallParams(); if (params.sessionParams.dataLoaderParams == null) { - final DataLoaderParams dataLoaderParams = DataLoaderParams.forStreaming( - new ComponentName(DATA_LOADER_PACKAGE, DATA_LOADER_CLASS), ""); - params.sessionParams.setDataLoaderParams(dataLoaderParams); + params.sessionParams.setDataLoaderParams( + PackageManagerShellCommandDataLoader.getDataLoaderParams(this)); } return doRunInstall(params); } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java index 1ee9ab8927bb..a814cb8942e2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java @@ -17,18 +17,22 @@ package com.android.server.pm; import android.annotation.NonNull; +import android.content.ComponentName; import android.content.pm.DataLoaderParams; import android.content.pm.InstallationFile; import android.os.ParcelFileDescriptor; +import android.os.ShellCommand; import android.service.dataloader.DataLoaderService; import android.text.TextUtils; import android.util.Slog; +import android.util.SparseArray; import libcore.io.IoUtils; -import java.io.File; import java.io.IOException; +import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; import java.util.Collection; /** @@ -37,41 +41,109 @@ import java.util.Collection; public class PackageManagerShellCommandDataLoader extends DataLoaderService { public static final String TAG = "PackageManagerShellCommandDataLoader"; + private static final String PACKAGE = "android"; + private static final String CLASS = PackageManagerShellCommandDataLoader.class.getName(); + + static final SecureRandom sRandom = new SecureRandom(); + static final SparseArray<WeakReference<ShellCommand>> sShellCommands = new SparseArray<>(); + + private static final char ARGS_DELIM = '&'; + private static final String SHELL_COMMAND_ID_PREFIX = "shellCommandId="; + private static final int INVALID_SHELL_COMMAND_ID = -1; + private static final int TOO_MANY_PENDING_SHELL_COMMANDS = 10; + + private static final String STDIN_PATH = "-"; + + static DataLoaderParams getDataLoaderParams(ShellCommand shellCommand) { + int commandId; + synchronized (sShellCommands) { + // Clean up old references. + for (int i = sShellCommands.size() - 1; i >= 0; i--) { + WeakReference<ShellCommand> oldRef = sShellCommands.valueAt(i); + if (oldRef.get() == null) { + sShellCommands.removeAt(i); + } + } + + // Sanity check. + if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) { + Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size()); + } + + // Generate new id and put ref to the array. + do { + commandId = sRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + } while (sShellCommands.contains(commandId)); + + sShellCommands.put(commandId, new WeakReference<>(shellCommand)); + } + + final String args = SHELL_COMMAND_ID_PREFIX + commandId; + return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS), args); + } + + private static int extractShellCommandId(String args) { + int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX); + if (sessionIdIdx < 0) { + Slog.e(TAG, "Missing shell command id param."); + return INVALID_SHELL_COMMAND_ID; + } + sessionIdIdx += SHELL_COMMAND_ID_PREFIX.length(); + int delimIdx = args.indexOf(ARGS_DELIM, sessionIdIdx); + try { + if (delimIdx < 0) { + return Integer.parseInt(args.substring(sessionIdIdx)); + } else { + return Integer.parseInt(args.substring(sessionIdIdx, delimIdx)); + } + } catch (NumberFormatException e) { + Slog.e(TAG, "Incorrect shell command id format.", e); + return INVALID_SHELL_COMMAND_ID; + } + } + static class DataLoader implements DataLoaderService.DataLoader { - private ParcelFileDescriptor mInFd = null; + private DataLoaderParams mParams = null; private FileSystemConnector mConnector = null; - private static final String STDIN_PATH = "-"; - @Override public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams, @NonNull FileSystemConnector connector) { + mParams = dataLoaderParams; mConnector = connector; return true; } + @Override public boolean onPrepareImage(Collection<InstallationFile> addedFiles, Collection<String> removedFiles) { + final int commandId = extractShellCommandId(mParams.getArguments()); + if (commandId == INVALID_SHELL_COMMAND_ID) { + return false; + } + + final WeakReference<ShellCommand> shellCommandRef; + synchronized (sShellCommands) { + shellCommandRef = sShellCommands.get(commandId, null); + } + final ShellCommand shellCommand = + shellCommandRef != null ? shellCommandRef.get() : null; + if (shellCommand == null) { + Slog.e(TAG, "Missing shell command."); + return false; + } 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()); + final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( + shellCommand.getInFileDescriptor()); 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 = shellCommand.openFileForSystem(filePath, "r"); + mConnector.writeData(fileInfo.getName(), 0, incomingFd.getStatSize(), incomingFd); } finally { IoUtils.closeQuietly(incomingFd); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 9642a1a21cf3..ec84b51577f9 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -280,8 +280,11 @@ public final class Settings { /** Map from package name to settings */ final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>(); - /** List of packages that installed other packages */ - final ArraySet<String> mInstallerPackages = new ArraySet<>(); + /** + * List of packages that were involved in installing other packages, i.e. are listed + * in at least one app's InstallSource. + */ + private final ArraySet<String> mInstallerPackages = new ArraySet<>(); /** Map from package name to appId and excluded userids */ private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>(); @@ -441,16 +444,6 @@ public final class Settings { return mPermissions.canPropagatePermissionToInstantApp(permName); } - void setInstallerPackageName(String pkgName, String installerPkgName) { - PackageSetting p = mPackages.get(pkgName); - if (p != null) { - p.setInstallerPackageName(installerPkgName); - if (installerPkgName != null) { - mInstallerPackages.add(installerPkgName); - } - } - } - /** Gets and optionally creates a new shared user id. */ SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags, boolean create) throws PackageManagerException { @@ -2826,6 +2819,9 @@ public final class Settings { if (installSource.initiatingPackageName != null) { serializer.attribute(null, "installInitiator", installSource.initiatingPackageName); } + if (installSource.isInitiatingPackageUninstalled) { + serializer.attribute(null, "installInitiatorUninstalled", "true"); + } if (installSource.originatingPackageName != null) { serializer.attribute(null, "installOriginator", installSource.originatingPackageName); } @@ -2843,6 +2839,11 @@ public final class Settings { pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); + if (installSource.initiatingPackageSignatures != null) { + installSource.initiatingPackageSignatures.writeXml( + serializer, "install-initiator-sigs", mPastSignatures); + } + writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates()); writeSigningKeySetLPr(serializer, pkg.keySetData); @@ -3578,6 +3579,7 @@ public final class Settings { String isOrphaned = null; String installOriginatingPackageName = null; String installInitiatingPackageName = null; + String installInitiatorUninstalled = null; String volumeUuid = null; String categoryHintString = null; String updateAvailable = null; @@ -3623,6 +3625,8 @@ public final class Settings { isOrphaned = parser.getAttributeValue(null, "isOrphaned"); installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator"); installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator"); + installInitiatorUninstalled = parser.getAttributeValue(null, + "installInitiatorUninstalled"); volumeUuid = parser.getAttributeValue(null, "volumeUuid"); categoryHintString = parser.getAttributeValue(null, "categoryHint"); if (categoryHintString != null) { @@ -3777,9 +3781,11 @@ public final class Settings { } if (packageSetting != null) { packageSetting.uidError = "true".equals(uidError); - packageSetting.installSource = InstallSource.create( + InstallSource installSource = InstallSource.create( installInitiatingPackageName, installOriginatingPackageName, - installerPackageName, "true".equals(isOrphaned)); + installerPackageName, "true".equals(isOrphaned), + "true".equals(installInitiatorUninstalled)); + packageSetting.installSource = installSource; packageSetting.volumeUuid = volumeUuid; packageSetting.categoryHint = categoryHint; packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr; @@ -3809,9 +3815,7 @@ public final class Settings { packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null); } - if (installerPackageName != null) { - mInstallerPackages.add(installerPackageName); - } + addInstallerPackageNames(installSource); int outerDepth = parser.getDepth(); int type; @@ -3857,6 +3861,11 @@ public final class Settings { mKeySetRefs.put(id, 1); } packageSetting.keySetData.addDefinedKeySet(id, alias); + } else if (tagName.equals("install-initiator-sigs")) { + final PackageSignatures signatures = new PackageSignatures(); + signatures.readXml(parser, mPastSignatures); + packageSetting.installSource = + packageSetting.installSource.setInitiatingPackageSignatures(signatures); } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { readDomainVerificationLPw(parser, packageSetting); } else { @@ -3870,6 +3879,18 @@ public final class Settings { } } + void addInstallerPackageNames(InstallSource installSource) { + if (installSource.installerPackageName != null) { + mInstallerPackages.add(installSource.installerPackageName); + } + if (installSource.initiatingPackageName != null) { + mInstallerPackages.add(installSource.initiatingPackageName); + } + if (installSource.originatingPackageName != null) { + mInstallerPackages.add(installSource.originatingPackageName); + } + } + private void readDisabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser, int userId) throws IOException, XmlPullParserException { int outerDepth = parser.getDepth(); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index e7269a6d9498..a86c8d7545b1 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -58,6 +58,7 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; +import com.android.internal.infra.AndroidFuture; import com.android.internal.util.IntPair; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; @@ -68,7 +69,7 @@ import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; /** * This is a permission policy that governs over all permission mechanism @@ -280,7 +281,7 @@ public final class PermissionPolicyService extends SystemService { if (DEBUG) Slog.i(LOG_TAG, "defaultPermsWereGrantedSinceBoot(" + userId + ")"); // Now call into the permission controller to apply policy around permissions - final CountDownLatch latch = new CountDownLatch(1); + final AndroidFuture<Boolean> future = new AndroidFuture<>(); // We need to create a local manager that does not schedule work on the main // there as we are on the main thread and want to block until the work is @@ -290,22 +291,22 @@ public final class PermissionPolicyService extends SystemService { getUserContext(getContext(), UserHandle.of(userId)), FgThread.getHandler()); permissionControllerManager.grantOrUpgradeDefaultRuntimePermissions( - FgThread.getExecutor(), - (Boolean success) -> { - if (!success) { + FgThread.getExecutor(), successful -> { + if (successful) { + future.complete(null); + } else { // We are in an undefined state now, let us crash and have // rescue party suggest a wipe to recover to a good one. - final String message = "Error granting/upgrading runtime permissions"; + final String message = "Error granting/upgrading runtime permissions" + + " for user " + userId; Slog.wtf(LOG_TAG, message); - throw new IllegalStateException(message); + future.completeExceptionally(new IllegalStateException(message)); } - latch.countDown(); - } - ); + }); try { - latch.await(); - } catch (InterruptedException e) { - /* ignore */ + future.get(); + } catch (InterruptedException | ExecutionException e) { + throw new IllegalStateException(e); } permissionControllerManager.updateUserSensitive(); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 5a124a717de2..ea83adba4d8a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -562,10 +562,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mScreenshotChordPowerKeyTriggered; private long mScreenshotChordPowerKeyTime; - private static final long MOVING_DISPLAY_TO_TOP_DURATION_MILLIS = 10; - private volatile boolean mMovingDisplayToTopKeyTriggered; - private volatile long mMovingDisplayToTopKeyTime; - // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up private int mRingerToggleChord = VOLUME_HUSH_OFF; @@ -633,7 +629,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_POWER_VERY_LONG_PRESS = 25; private static final int MSG_NOTIFY_USER_ACTIVITY = 26; private static final int MSG_RINGER_TOGGLE_CHORD = 27; - private static final int MSG_MOVE_DISPLAY_TO_TOP = 28; private class PolicyHandler extends Handler { @Override @@ -697,7 +692,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { accessibilityShortcutActivated(); break; case MSG_BUGREPORT_TV: - requestFullBugreport(); + requestFullBugreportOrLaunchHandlerApp(); break; case MSG_ACCESSIBILITY_TV: if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) { @@ -723,10 +718,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_RINGER_TOGGLE_CHORD: handleRingerChordGesture(); break; - case MSG_MOVE_DISPLAY_TO_TOP: - mWindowManagerFuncs.moveDisplayToTop(msg.arg1); - mMovingDisplayToTopKeyTriggered = false; - break; } } } @@ -2545,36 +2536,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, int policyFlags) { - final long result = interceptKeyBeforeDispatchingInner(focusedToken, event, policyFlags); - final int eventDisplayId = event.getDisplayId(); - if (result == 0 && !mPerDisplayFocusEnabled - && eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) { - // An event is targeting a non-focused display. Try to move the display to top so that - // it can become the focused display to interact with the user. - final long eventDownTime = event.getDownTime(); - if (mMovingDisplayToTopKeyTime < eventDownTime) { - // We have not handled this event yet. Move the display to top, and then tell - // dispatcher to try again later. - mMovingDisplayToTopKeyTime = eventDownTime; - mMovingDisplayToTopKeyTriggered = true; - mHandler.sendMessage( - mHandler.obtainMessage(MSG_MOVE_DISPLAY_TO_TOP, eventDisplayId, 0)); - return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS; - } else if (mMovingDisplayToTopKeyTriggered) { - // The message has not been handled yet. Tell dispatcher to try again later. - return MOVING_DISPLAY_TO_TOP_DURATION_MILLIS; - } - // The target display is still not the top focused display. Drop the event because the - // display may not contain any window which can receive keys. - Slog.w(TAG, "Dropping key targeting non-focused display #" + eventDisplayId - + " keyCode=" + KeyEvent.keyCodeToString(event.getKeyCode())); - return -1; - } - return result; - } - - private long interceptKeyBeforeDispatchingInner(IBinder focusedToken, KeyEvent event, - int policyFlags) { final boolean keyguardOn = keyguardOn(); final int keyCode = event.getKeyCode(); final int repeatCount = event.getRepeatCount(); @@ -3061,12 +3022,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { return mAccessibilityTvScheduled; } - private void requestFullBugreport() { + private void requestFullBugreportOrLaunchHandlerApp() { if ("1".equals(SystemProperties.get("ro.debuggable")) || Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) { try { - ActivityManager.getService().requestFullBugReport(); + if (!ActivityManager.getService().launchBugReportHandlerApp()) { + ActivityManager.getService().requestFullBugReport(); + } } catch (RemoteException e) { Slog.e(TAG, "Error taking bugreport", e); } @@ -3613,7 +3576,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean canceled = event.isCanceled(); final int keyCode = event.getKeyCode(); final int displayId = event.getDisplayId(); - final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; // If screen is off then we treat the case where the keyguard is open but hidden @@ -4035,6 +3997,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY"); } + if ((result & ACTION_PASS_TO_USER) != 0) { + // If the key event is targeted to a specific display, then the user is interacting with + // that display. Therefore, give focus to the display that the user is interacting with. + if (!mPerDisplayFocusEnabled + && displayId != INVALID_DISPLAY && displayId != mTopFocusedDisplayId) { + // An event is targeting a non-focused display. Move the display to top so that + // it can become the focused display to interact with the user. + // This should be done asynchronously, once the focus logic is fully moved to input + // from windowmanager. Currently, we need to ensure the setInputWindows completes, + // which would force the focus event to be queued before the current key event. + // TODO(b/70668286): post call to 'moveDisplayToTop' to mHandler instead + Log.i(TAG, "Moving non-focused display " + displayId + " to top " + + "because a key is targeting it"); + mWindowManagerFuncs.moveDisplayToTop(displayId); + } + } + return result; } @@ -5213,7 +5192,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Intent dock = createHomeDockIntent(); if (dock != null) { int result = ActivityTaskManager.getService() - .startActivityAsUser(null, null, dock, + .startActivityAsUser(null, mContext.getBasePackageName(), dock, dock.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, ActivityManager.START_FLAG_ONLY_IF_NEEDED, @@ -5224,7 +5203,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } int result = ActivityTaskManager.getService() - .startActivityAsUser(null, null, mHomeIntent, + .startActivityAsUser(null, mContext.getBasePackageName(), mHomeIntent, mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, ActivityManager.START_FLAG_ONLY_IF_NEEDED, diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index b4f4eca5f188..f661b5e0d8a8 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -22,7 +22,7 @@ import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 02656eaf5c6c..da848d87ba71 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -24,10 +24,10 @@ import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; +import android.os.TimestampedValue; import android.telephony.TelephonyManager; import android.util.LocalLog; import android.util.Slog; -import android.util.TimestampedValue; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 94c2192b620f..23b94bdb74c8 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -751,10 +751,10 @@ class ActivityMetricsLogger { if (abort) { launchObserverNotifyActivityLaunchCancelled(info); } else { - logAppTransitionFinished(info); if (info.isInterestingToLoggerAndObserver()) { launchObserverNotifyActivityLaunchFinished(info, timestampNs); } + logAppTransitionFinished(info); } info.mPendingDrawActivities.clear(); mTransitionInfoList.remove(info); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 65d0c4c2515f..26d76a8d6e28 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -243,9 +243,9 @@ import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.PipModeChangeItem; import android.app.servertransaction.ResumeActivityItem; +import android.app.servertransaction.StartActivityItem; import android.app.servertransaction.StopActivityItem; import android.app.servertransaction.TopResumedActivityChangeItem; -import android.app.servertransaction.WindowVisibilityItem; import android.app.usage.UsageEvents.Event; import android.content.ComponentName; import android.content.Intent; @@ -4497,7 +4497,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A sleeping = false; app.postPendingUiCleanMsg(true); if (reportToClient) { - makeClientVisible(); + mClientVisibilityDeferred = false; + makeActiveIfNeeded(starting); } else { mClientVisibilityDeferred = true; } @@ -4511,23 +4512,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A handleAlreadyVisible(); } - /** Send visibility change message to the client and pause if needed. */ - void makeClientVisible() { - mClientVisibilityDeferred = false; - try { - mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, - WindowVisibilityItem.obtain(true /* showWindow */)); - makeActiveIfNeeded(null /* activeActivity*/); - if (isState(STOPPING, STOPPED)) { - // Set state to STARTED in order to have consistent state with client while - // making an non-active activity visible from stopped. - setState(STARTED, "makeClientVisible"); - } - } catch (Exception e) { - Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e); - } - } - void makeInvisible() { if (!mVisibleRequested) { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this); @@ -4556,14 +4540,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A switch (getState()) { case STOPPING: case STOPPED: - if (attachedToProcess()) { - if (DEBUG_VISIBILITY) { - Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + this); - } - mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), - appToken, WindowVisibilityItem.obtain(false /* showWindow */)); - } - // Reset the flag indicating that an app can enter picture-in-picture once the // activity is hidden supportsEnterPipOnTaskSwitch = false; @@ -4595,17 +4571,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean makeActiveIfNeeded(ActivityRecord activeActivity) { if (shouldResumeActivity(activeActivity)) { if (DEBUG_VISIBILITY) { - Slog.v("TAG_VISIBILITY", "Resume visible activity, " + this); + Slog.v(TAG_VISIBILITY, "Resume visible activity, " + this); } return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */, null /* options */); } else if (shouldPauseActivity(activeActivity)) { if (DEBUG_VISIBILITY) { - Slog.v("TAG_VISIBILITY", "Pause visible activity, " + this); + Slog.v(TAG_VISIBILITY, "Pause visible activity, " + this); } // An activity must be in the {@link PAUSING} state for the system to validate // the move to {@link PAUSED}. - setState(PAUSING, "makeVisibleIfNeeded"); + setState(PAUSING, "makeActiveIfNeeded"); try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, PauseActivityItem.obtain(finishing, false /* userLeaving */, @@ -4613,6 +4589,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } catch (Exception e) { Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e); } + } else if (shouldStartActivity()) { + if (DEBUG_VISIBILITY) { + Slog.v(TAG_VISIBILITY, "Start visible activity, " + this); + } + setState(STARTED, "makeActiveIfNeeded"); + try { + mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, + StartActivityItem.obtain()); + } catch (Exception e) { + Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e); + } } return false; } @@ -4656,6 +4643,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** + * Check if activity should be moved to STARTED state. + * NOTE: This will not check if activity should be made paused or resumed first, so it must only + * be called after checking with {@link #shouldResumeActivity(ActivityRecord)} + * and {@link #shouldPauseActivity(ActivityRecord)}. + */ + private boolean shouldStartActivity() { + return mVisibleRequested && isState(STOPPED); + } + + /** * Check if activity is eligible to be made active (resumed of paused). The activity: * - should be paused, stopped or stopping * - should not be the currently active one or launching behind other tasks @@ -4890,16 +4887,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } setState(STOPPING, "stopIfPossible"); if (DEBUG_VISIBILITY) { - Slog.v(TAG_VISIBILITY, "Stopping visibleRequested=" - + mVisibleRequested + " for " + this); - } - if (!mVisibleRequested) { - setVisibility(false); + Slog.v(TAG_VISIBILITY, "Stopping:" + this); } EventLogTags.writeWmStopActivity( mUserId, System.identityHashCode(this), shortComponentName); mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, - StopActivityItem.obtain(mVisibleRequested, configChangeFlags)); + StopActivityItem.obtain(configChangeFlags)); + if (stack.shouldSleepOrShutDownActivities()) { setSleeping(true); } @@ -6107,11 +6101,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // This also avoids if the next activity never reports idle (e.g. animating view), // the previous will need to wait until idle timeout to be stopped or destroyed. mStackSupervisor.scheduleProcessStoppingAndFinishingActivities(); - } else { - // Instead of doing the full stop routine here, let's just hide any activities - // we now can, and let them stop when the normal idle happens. - mStackSupervisor.processStoppingActivities(null /* launchedActivity */, - true /* onlyUpdateVisibility */, true /* unused */, null /* unused */); } } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -7206,7 +7195,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // {@link ActivityTaskManagerService.activityStopped}). try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, - StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */)); + StopActivityItem.obtain(0 /* configChanges */)); } catch (RemoteException e) { Slog.w(TAG, "Exception thrown during restart " + this, e); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index e8564fc80ac3..d63165adb2f8 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -83,6 +83,7 @@ import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -2076,54 +2077,11 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { boolean processPausingActivities, String reason) { // Stop any activities that are scheduled to do so but have been waiting for the transition // animation to finish. - processStoppingActivities(launchedActivity, false /* onlyUpdateVisibility */, - processPausingActivities, reason); - - final int numFinishingActivities = mFinishingActivities.size(); - if (numFinishingActivities == 0) { - return; - } - - // Finish any activities that are scheduled to do so but have been waiting for the next one - // to start. - final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities); - mFinishingActivities.clear(); - for (int i = 0; i < numFinishingActivities; i++) { - final ActivityRecord r = finishingActivities.get(i); - if (r.isInHistory()) { - r.destroyImmediately(true /* removeFromApp */, "finish-" + reason); - } - } - } - - /** Stop or destroy the stopping activities if they are not interactive. */ - void processStoppingActivities(ActivityRecord launchedActivity, boolean onlyUpdateVisibility, - boolean processPausingActivities, String reason) { - final int numStoppingActivities = mStoppingActivities.size(); - if (numStoppingActivities == 0) { - return; - } ArrayList<ActivityRecord> readyToStopActivities = null; - - final boolean nowVisible = mRootWindowContainer.allResumedActivitiesVisible(); - for (int activityNdx = numStoppingActivities - 1; activityNdx >= 0; --activityNdx) { - final ActivityRecord s = mStoppingActivities.get(activityNdx); - if (nowVisible && s.finishing) { - - // If this activity is finishing, it is sitting on top of - // everyone else but we now know it is no longer needed... - // so get rid of it. Otherwise, we need to go through the - // normal flow and hide it once we determine that it is - // hidden by the activities in front of it. - if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s); - s.setVisibility(false); - } - if (onlyUpdateVisibility) { - continue; - } - - final boolean animating = s.isAnimating(TRANSITION); - if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + for (int i = mStoppingActivities.size() - 1; i >= 0; --i) { + final ActivityRecord s = mStoppingActivities.get(i); + final boolean animating = s.isAnimating(TRANSITION | PARENTS); + if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + s.nowVisible + " animating=" + animating + " finishing=" + s.finishing); final ActivityStack stack = s.getActivityStack(); @@ -2145,7 +2103,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } readyToStopActivities.add(s); - mStoppingActivities.remove(activityNdx); + mStoppingActivities.remove(i); } } @@ -2161,6 +2119,22 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } } + + final int numFinishingActivities = mFinishingActivities.size(); + if (numFinishingActivities == 0) { + return; + } + + // Finish any activities that are scheduled to do so but have been waiting for the next one + // to start. + final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities); + mFinishingActivities.clear(); + for (int i = 0; i < numFinishingActivities; i++) { + final ActivityRecord r = finishingActivities.get(i); + if (r.isInHistory()) { + r.destroyImmediately(true /* removeFromApp */, "finish-" + reason); + } + } } void removeHistoryRecords(WindowProcessController app) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 76c0e4eeecae..474c5c960eb0 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -7203,7 +7203,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER); if (mController != null) { final long token = proto.start(CONTROLLER); - proto.write(CONTROLLER, mController.toString()); + proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, + mController.toString()); proto.write(IS_A_MONKEY, mControllerIsAMonkey); proto.end(token); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 09111d07e8c1..014cb76c0064 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -436,10 +436,9 @@ public class AppTransition implements Dump { mNextAppTransition = TRANSIT_UNSET; mNextAppTransitionFlags = 0; setAppTransitionState(APP_STATE_RUNNING); - final AnimationAdapter topOpeningAnim = - (topOpeningApp != null && topOpeningApp.getAnimatingContainer() != null) - ? topOpeningApp.getAnimatingContainer().getAnimation() - : null; + final WindowContainer wc = + topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null; + final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null; int redoLayout = notifyAppTransitionStartingLocked(transit, topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0, diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7f944454ecdf..ba9d757d979f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -205,6 +205,7 @@ import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; +import android.view.IDisplayWindowInsetsController; import android.view.ISystemGestureExclusionListener; import android.view.IWindow; import android.view.InputChannel; @@ -218,6 +219,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; import android.view.View; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants.PointerEventListener; @@ -570,6 +572,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ WindowState mInputMethodTarget; + InsetsControlTarget mInputMethodControlTarget; + /** If true hold off on modifying the animation layer of mInputMethodTarget */ boolean mInputMethodTargetWaitingAnim; @@ -598,6 +602,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final float mWindowCornerRadius; private final SparseArray<ShellRoot> mShellRoots = new SparseArray<>(); + RemoteInsetsControlTarget mRemoteInsetsControlTarget = null; + private final IBinder.DeathRecipient mRemoteInsetsDeath = + () -> { + synchronized (mWmService.mGlobalLock) { + mRemoteInsetsControlTarget = null; + } + }; private RootWindowContainer mRootWindowContainer; @@ -772,7 +783,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If this is the first layout, we need to initialize the last frames and inset values, // as otherwise we'd immediately cause an unnecessary resize. if (firstLayout) { - w.updateLastFrames(); + // The client may compute its actual requested size according to the first layout, + // so we still request the window to resize if the current frame is empty. + if (!w.getFrameLw().isEmpty()) { + w.updateLastFrames(); + } w.updateLastInsetValues(); w.updateLocationInParentDisplayIfNeeded(); } @@ -1152,6 +1167,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mShellRoots.remove(windowType); } + void setRemoteInsetsController(IDisplayWindowInsetsController controller) { + if (mRemoteInsetsControlTarget != null) { + mRemoteInsetsControlTarget.mRemoteInsetsController.asBinder().unlinkToDeath( + mRemoteInsetsDeath, 0); + mRemoteInsetsControlTarget = null; + } + if (controller != null) { + try { + controller.asBinder().linkToDeath(mRemoteInsetsDeath, 0); + mRemoteInsetsControlTarget = new RemoteInsetsControlTarget(controller); + } catch (RemoteException e) { + return; + } + } + } + /** Changes the display the input window token is housed on to this one. */ void reParentWindowToken(WindowToken token) { final DisplayContent prevDc = token.getDisplayContent(); @@ -3379,6 +3410,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + boolean isImeAttachedToApp() { + return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null + && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + // An activity with override bounds should be letterboxed inside its parent bounds, + // so it doesn't fill the screen. + && mInputMethodTarget.mActivityRecord.matchParentBounds()); + } + private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) { if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) { return; @@ -3387,7 +3426,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMethodTarget = target; mInputMethodTargetWaitingAnim = targetWaitingAnim; assignWindowLayers(false /* setLayoutNeeded */); - mInsetsStateController.onImeTargetChanged(target); + mInputMethodControlTarget = computeImeControlTarget(); + mInsetsStateController.onImeTargetChanged(mInputMethodControlTarget); updateImeParent(); } @@ -3412,11 +3452,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. - if (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null - && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - // An activity with override bounds should be letterboxed inside its parent bounds, - // so it doesn't fill the screen. - && mInputMethodTarget.mActivityRecord.matchParentBounds()) { + if (isImeAttachedToApp()) { return mInputMethodTarget.mActivityRecord.getSurfaceControl(); } @@ -3424,6 +3460,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mWindowContainers.getSurfaceControl(); } + /** + * Computes which control-target the IME should be attached to. + */ + @VisibleForTesting + InsetsControlTarget computeImeControlTarget() { + if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) { + return mRemoteInsetsControlTarget; + } + + // Otherwise, we just use the ime target + return mInputMethodTarget; + } + void setLayoutNeeded() { if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3)); mLayoutNeeded = true; @@ -6684,4 +6733,50 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo Context getDisplayUiContext() { return mDisplayPolicy.getSystemUiContext(); } + + class RemoteInsetsControlTarget implements InsetsControlTarget { + private final IDisplayWindowInsetsController mRemoteInsetsController; + + RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) { + mRemoteInsetsController = controller; + } + + void notifyInsetsChanged() { + try { + mRemoteInsetsController.insetsChanged( + getInsetsStateController().getRawInsetsState()); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver inset state change", e); + } + } + + @Override + public void notifyInsetsControlChanged() { + final InsetsStateController stateController = getInsetsStateController(); + try { + mRemoteInsetsController.insetsControlChanged(stateController.getRawInsetsState(), + stateController.getControlsForDispatch(this)); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver inset state change", e); + } + } + + @Override + public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { + try { + mRemoteInsetsController.showInsets(types, fromIme); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver showInsets", e); + } + } + + @Override + public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { + try { + mRemoteInsetsController.hideInsets(types, fromIme); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver showInsets", e); + } + } + } } diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index 55f5e289377e..c09834f2b3c1 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -79,12 +79,13 @@ class EnsureActivitiesVisibleHelper { final PooledConsumer f = PooledLambda.obtainConsumer( EnsureActivitiesVisibleHelper::setActivityVisibilityState, this, - PooledLambda.__(ActivityRecord.class), resumeTopActivity); + PooledLambda.__(ActivityRecord.class), starting, resumeTopActivity); mContiner.forAllActivities(f); f.recycle(); } - private void setActivityVisibilityState(ActivityRecord r, final boolean resumeTopActivity) { + private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting, + final boolean resumeTopActivity) { final boolean isTop = r == mTop; if (mAboveTop && !isTop) { return; @@ -129,7 +130,8 @@ class EnsureActivitiesVisibleHelper { "Skipping: already visible at " + r); if (r.mClientVisibilityDeferred && mNotifyClients) { - r.makeClientVisible(); + r.makeActiveIfNeeded(r.mClientVisibilityDeferred ? null : starting); + r.mClientVisibilityDeferred = false; } r.handleAlreadyVisible(); diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 05ede21472f2..fb97ecf8bbc4 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -69,7 +69,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { mShowImeRunner = () -> { // Target should still be the same. if (isImeTargetFromDisplayContentAndImeSame()) { - mDisplayContent.mInputMethodTarget.showInsets( + mDisplayContent.mInputMethodControlTarget.showInsets( WindowInsets.Type.ime(), true /* fromIme */); } abortShowImePostLayout(); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 184e7d61c355..a13383d3991e 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -70,6 +70,9 @@ class InsetsSourceProvider { */ private boolean mServerVisible; + private boolean mSeamlessRotating; + private long mFinishSeamlessRotateFrameNumber = -1; + private final boolean mControllable; InsetsSourceProvider(InsetsSource source, InsetsStateController stateController, @@ -170,7 +173,9 @@ class InsetsSourceProvider { updateSourceFrame(); if (mControl != null) { final Rect frame = mWin.getWindowFrames().mFrame; - if (mControl.setSurfacePosition(frame.left, frame.top)) { + if (mControl.setSurfacePosition(frame.left, frame.top) && mControlTarget != null) { + // The leash has been stale, we need to create a new one for the client. + updateControlForTarget(mControlTarget, true /* force */); mStateController.notifyControlChanged(mControlTarget); } } @@ -189,6 +194,11 @@ class InsetsSourceProvider { } void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { + if (mSeamlessRotating) { + // We are un-rotating the window against the display rotation. We don't want the target + // to control the window for now. + return; + } if (mWin == null) { mControlTarget = target; return; @@ -203,14 +213,42 @@ class InsetsSourceProvider { } mAdapter = new ControlAdapter(); setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); - mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter, - !mClientVisible /* hidden */); + final Transaction t = mDisplayContent.getPendingTransaction(); + mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */); + final SurfaceControl leash = mAdapter.mCapturedLeash; + final long frameNumber = mFinishSeamlessRotateFrameNumber; + mFinishSeamlessRotateFrameNumber = -1; + if (frameNumber >= 0 && mWin.mHasSurface && leash != null) { + // We just finished the seamless rotation. We don't want to change the position or the + // window crop of the surface controls (including the leash) until the client finishes + // drawing the new frame of the new orientation. Although we cannot defer the reparent + // operation, it is fine, because reparent won't cause any visual effect. + final SurfaceControl barrier = mWin.mWinAnimator.mSurfaceController.mSurfaceControl; + t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber); + t.deferTransactionUntil(leash, barrier, frameNumber); + } mControlTarget = target; - mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash, + mControl = new InsetsSourceControl(mSource.getType(), leash, new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top)); } - boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) { + void startSeamlessRotation() { + if (!mSeamlessRotating) { + mSeamlessRotating = true; + + // This will revoke the leash and clear the control target. + mWin.cancelAnimation(); + } + } + + void finishSeamlessRotation(boolean timeout) { + if (mSeamlessRotating) { + mSeamlessRotating = false; + mFinishSeamlessRotateFrameNumber = timeout ? -1 : mWin.getFrameNumber(); + } + } + + boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) { if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) { return false; } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 152607470ed1..b2234d17984e 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -91,6 +91,10 @@ class InsetsStateController { return state; } + InsetsState getRawInsetsState() { + return mState; + } + @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) { ArrayList<Integer> controlled = mControlTargetTypeMap.get(target); if (controlled == null) { @@ -144,7 +148,7 @@ class InsetsStateController { getImeSourceProvider().onPostInsetsDispatched(); } - void onInsetsModified(WindowState windowState, InsetsState state) { + void onInsetsModified(InsetsControlTarget windowState, InsetsState state) { boolean changed = false; for (int i = state.getSourcesCount() - 1; i >= 0; i--) { final InsetsSource source = state.sourceAt(i); @@ -199,7 +203,7 @@ class InsetsStateController { if (target == previous) { return; } - final InsetsSourceProvider provider = getSourceProvider(type); + final InsetsSourceProvider provider = mProviders.get(type); if (provider == null) { return; } @@ -207,6 +211,7 @@ class InsetsStateController { return; } provider.updateControlForTarget(target, false /* force */); + target = provider.getControlTarget(); if (previous != null) { removeFromControlMaps(previous, type, false /* fake */); mPendingControlChanged.add(previous); @@ -296,6 +301,9 @@ class InsetsStateController { void notifyInsetsChanged() { mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); + if (mDisplayContent.mRemoteInsetsControlTarget != null) { + mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged(); + } } void dump(String prefix, PrintWriter pw) { diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 399c5d3ae45f..eaa0ea72452a 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -16,8 +16,12 @@ package com.android.server.wm; +import static com.android.server.wm.AnimationSpecProto.ROTATE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; +import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS; +import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA; +import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA; import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING; import static com.android.server.wm.ScreenRotationAnimationProto.STARTED; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -25,7 +29,9 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER; +import android.animation.ArgbEvaluator; import android.content.Context; +import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; @@ -40,7 +46,9 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Transformation; +import com.android.internal.R; import com.android.server.protolog.common.ProtoLog; +import com.android.server.wm.utils.RotationAnimationUtils; import java.io.PrintWriter; @@ -60,10 +68,10 @@ import java.io.PrintWriter; * animation first rotate the new content into the old orientation to then be able to * animate to the new orientation * - * <li> The exiting Blackframe: <p> - * Because the change of orientation might change the width and height of the content (i.e - * when rotating from portrait to landscape) we "crop" the new content using black frames - * around the screenshot so the new content does not go beyond the screenshot's bounds + * <li> The Background color frame: <p> + * To have the animation seem more seamless, we add a color transitioning background behind the + * exiting and entering layouts. We compute the brightness of the start and end + * layouts and transition from the two brightness values as grayscale underneath the animation * * <li> The entering Blackframe: <p> * The enter Blackframe is similar to the exit Blackframe but is only used when a custom @@ -81,8 +89,6 @@ class ScreenRotationAnimation { */ private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER; private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE; - private static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1; - private static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2; private final Context mContext; private final DisplayContent mDisplayContent; @@ -90,16 +96,18 @@ class ScreenRotationAnimation { private final Transformation mRotateExitTransformation = new Transformation(); private final Transformation mRotateEnterTransformation = new Transformation(); // Complete transformations being applied. - private final Transformation mExitTransformation = new Transformation(); private final Transformation mEnterTransformation = new Transformation(); - private final Matrix mFrameInitialMatrix = new Matrix(); private final Matrix mSnapshotInitialMatrix = new Matrix(); - private final Matrix mSnapshotFinalMatrix = new Matrix(); - private final Matrix mExitFrameFinalMatrix = new Matrix(); private final WindowManagerService mService; + /** Only used for custom animations and not screen rotation. */ private SurfaceControl mEnterBlackFrameLayer; - private SurfaceControl mRotationLayer; - private SurfaceControl mSurfaceControl; + /** This layer contains the actual screenshot that is to be faded out. */ + private SurfaceControl mScreenshotLayer; + /** + * Only used for screen rotation and not custom animations. Layered behind all other layers + * to avoid showing any "empty" spots + */ + private SurfaceControl mBackColorSurface; private BlackFrame mEnteringBlackFrame; private int mWidth, mHeight; @@ -120,8 +128,11 @@ class ScreenRotationAnimation { private boolean mFinishAnimReady; private long mFinishAnimStartTime; private boolean mForceDefaultOrientation; - private BlackFrame mExitingBlackFrame; private SurfaceRotationAnimationController mSurfaceRotationAnimationController; + /** Intensity of light/whiteness of the layout before rotation occurs. */ + private float mStartLuma; + /** Intensity of light/whiteness of the layout after rotation occurs. */ + private float mEndLuma; public ScreenRotationAnimation(Context context, DisplayContent displayContent, boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) { @@ -162,9 +173,15 @@ class ScreenRotationAnimation { final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); try { - mRotationLayer = displayContent.makeOverlay() + mBackColorSurface = displayContent.makeChildSurface(null) + .setName("BackColorSurface") + .setColorLayer() + .build(); + + mScreenshotLayer = displayContent.makeOverlay() .setName("RotationLayer") - .setContainerLayer() + .setBufferSize(mWidth, mHeight) + .setSecure(isSecure) .build(); mEnterBlackFrameLayer = displayContent.makeOverlay() @@ -172,26 +189,21 @@ class ScreenRotationAnimation { .setContainerLayer() .build(); - mSurfaceControl = mService.makeSurfaceBuilder(null) - .setName("ScreenshotSurface") - .setParent(mRotationLayer) - .setBufferSize(mWidth, mHeight) - .setSecure(isSecure) - .build(); - // In case display bounds change, screenshot buffer and surface may mismatch so set a // scaling mode. SurfaceControl.Transaction t2 = mService.mTransactionFactory.get(); - t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW); + t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW); t2.apply(true /* sync */); // Capture a screenshot into the surface we just created. final int displayId = display.getDisplayId(); final Surface surface = mService.mSurfaceFactory.get(); - surface.copyFrom(mSurfaceControl); + surface.copyFrom(mScreenshotLayer); SurfaceControl.ScreenshotGraphicBuffer gb = mService.mDisplayManagerInternal.screenshot(displayId); if (gb != null) { + mStartLuma = RotationAnimationUtils.getAvgBorderLuma(gb.getGraphicBuffer(), + gb.getColorSpace()); try { surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(), gb.getColorSpace()); @@ -202,13 +214,15 @@ class ScreenRotationAnimation { // screenshot surface we display it in also has FLAG_SECURE so that // the user can not screenshot secure layers via the screenshot surface. if (gb.containsSecureLayers()) { - t.setSecure(mSurfaceControl, true); + t.setSecure(mScreenshotLayer, true); } - t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE); - t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT); - t.setAlpha(mSurfaceControl, 0); - t.show(mRotationLayer); - t.show(mSurfaceControl); + t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE); + t.reparent(mBackColorSurface, displayContent.getSurfaceControl()); + t.setLayer(mBackColorSurface, -1); + t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma}); + t.setAlpha(mBackColorSurface, 1); + t.show(mScreenshotLayer); + t.show(mBackColorSurface); } else { Slog.w(TAG, "Unable to take screenshot of display " + displayId); } @@ -218,32 +232,11 @@ class ScreenRotationAnimation { } ProtoLog.i(WM_SHOW_SURFACE_ALLOC, - " FREEZE %s: CREATE", mSurfaceControl); + " FREEZE %s: CREATE", mScreenshotLayer); setRotation(t, originalRotation); t.apply(); } - private static void createRotationMatrix(int rotation, int width, int height, - Matrix outMatrix) { - switch (rotation) { - case Surface.ROTATION_0: - outMatrix.reset(); - break; - case Surface.ROTATION_90: - outMatrix.setRotate(90, 0, 0); - outMatrix.postTranslate(height, 0); - break; - case Surface.ROTATION_180: - outMatrix.setRotate(180, 0, 0); - outMatrix.postTranslate(width, height); - break; - case Surface.ROTATION_270: - outMatrix.setRotate(270, 0, 0); - outMatrix.postTranslate(0, width); - break; - } - } - public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(STARTED, mStarted); @@ -252,11 +245,11 @@ class ScreenRotationAnimation { } boolean hasScreenshot() { - return mSurfaceControl != null; + return mScreenshotLayer != null; } private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) { - if (mRotationLayer == null) { + if (mScreenshotLayer == null) { return; } matrix.getValues(mTmpFloats); @@ -267,24 +260,19 @@ class ScreenRotationAnimation { x -= mCurrentDisplayRect.left; y -= mCurrentDisplayRect.top; } - t.setPosition(mRotationLayer, x, y); - t.setMatrix(mRotationLayer, + t.setPosition(mScreenshotLayer, x, y); + t.setMatrix(mScreenshotLayer, mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y], mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]); - t.setAlpha(mSurfaceControl, (float) 1.0); - t.setAlpha(mRotationLayer, (float) 1.0); - t.show(mRotationLayer); + t.setAlpha(mScreenshotLayer, (float) 1.0); + t.show(mScreenshotLayer); } public void printTo(String prefix, PrintWriter pw) { - pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl); + pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer); pw.print(" mWidth="); pw.print(mWidth); pw.print(" mHeight="); pw.println(mHeight); - pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame); - if (mExitingBlackFrame != null) { - mExitingBlackFrame.printTo(prefix + " ", pw); - } pw.print(prefix); pw.print("mEnteringBlackFrame="); pw.println(mEnteringBlackFrame); @@ -303,20 +291,10 @@ class ScreenRotationAnimation { pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println(); pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation); pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println(); - pw.print(prefix); pw.print("mExitTransformation="); - mExitTransformation.printShortString(pw); pw.println(); pw.print(prefix); pw.print("mEnterTransformation="); mEnterTransformation.printShortString(pw); pw.println(); - pw.print(prefix); pw.print("mFrameInitialMatrix="); - mFrameInitialMatrix.printShortString(pw); - pw.println(); pw.print(prefix); pw.print("mSnapshotInitialMatrix="); - mSnapshotInitialMatrix.printShortString(pw); - pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mExitFrameFinalMatrix="); - mExitFrameFinalMatrix.printShortString(pw); - pw.println(); + mSnapshotInitialMatrix.printShortString(pw);pw.println(); pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation); if (mForceDefaultOrientation) { pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString()); @@ -331,7 +309,7 @@ class ScreenRotationAnimation { // to the snapshot to make it stay in the same original position // with the current screen rotation. int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0); - createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix); + RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix); setRotationTransform(t, mSnapshotInitialMatrix); } @@ -341,7 +319,7 @@ class ScreenRotationAnimation { */ private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) { - if (mSurfaceControl == null) { + if (mScreenshotLayer == null) { // Can't do animation. return false; } @@ -354,89 +332,58 @@ class ScreenRotationAnimation { // Figure out how the screen has moved from the original rotation. int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation); - mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_alpha); final boolean customAnim; if (exitAnim != 0 && enterAnim != 0) { customAnim = true; mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim); mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim); + mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext, + R.anim.screen_rotate_alpha); } else { customAnim = false; - switch (delta) { + switch (delta) { /* Counter-Clockwise Rotations */ case Surface.ROTATION_0: mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_0_exit); + R.anim.screen_rotate_0_exit); mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_0_enter); + R.anim.screen_rotate_0_enter); break; case Surface.ROTATION_90: mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_plus_90_exit); + R.anim.screen_rotate_plus_90_exit); mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_plus_90_enter); + R.anim.screen_rotate_plus_90_enter); break; case Surface.ROTATION_180: mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_180_exit); + R.anim.screen_rotate_180_exit); mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_180_enter); + R.anim.screen_rotate_180_enter); break; case Surface.ROTATION_270: mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_minus_90_exit); + R.anim.screen_rotate_minus_90_exit); mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, - com.android.internal.R.anim.screen_rotate_minus_90_enter); + R.anim.screen_rotate_minus_90_enter); break; } } - // Initialize the animations. This is a hack, redefining what "parent" - // means to allow supplying the last and next size. In this definition - // "%p" is the original (let's call it "previous") size, and "%" is the - // screen's current/new size. - mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); - mAnimRunning = false; - mFinishAnimReady = false; - mFinishAnimStartTime = -1; - mRotateExitAnimation.restrictDuration(maxAnimationDuration); mRotateExitAnimation.scaleCurrentDuration(animationScale); + mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); mRotateEnterAnimation.restrictDuration(maxAnimationDuration); mRotateEnterAnimation.scaleCurrentDuration(animationScale); - mRotateAlphaAnimation.restrictDuration(maxAnimationDuration); - mRotateAlphaAnimation.scaleCurrentDuration(animationScale); - if (!customAnim && mExitingBlackFrame == null) { - try { - // Compute the transformation matrix that must be applied - // the the black frame to make it stay in the initial position - // before the new screen rotation. This is different than the - // snapshot transformation because the snapshot is always based - // of the native orientation of the screen, not the orientation - // we were last in. - createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix); - - final Rect outer; - final Rect inner; - if (mForceDefaultOrientation) { - // Going from a smaller Display to a larger Display, add curtains to sides - // or top and bottom. Going from a larger to smaller display will result in - // no BlackSurfaces being constructed. - outer = mCurrentDisplayRect; - inner = mOriginalDisplayRect; - } else { - outer = new Rect(-mWidth, -mHeight, mWidth * 2, mHeight * 2); - inner = new Rect(0, 0, mWidth, mHeight); - } - mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner, - SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation, - mRotationLayer); - } catch (OutOfResourcesException e) { - Slog.w(TAG, "Unable to allocate black surface", e); - } + mAnimRunning = false; + mFinishAnimReady = false; + mFinishAnimStartTime = -1; + + if (customAnim) { + mRotateAlphaAnimation.restrictDuration(maxAnimationDuration); + mRotateAlphaAnimation.scaleCurrentDuration(animationScale); } if (customAnim && mEnteringBlackFrame == null) { @@ -451,7 +398,12 @@ class ScreenRotationAnimation { } } - mSurfaceRotationAnimationController.startAnimation(); + if (customAnim) { + mSurfaceRotationAnimationController.startCustomAnimation(); + } else { + mSurfaceRotationAnimationController.startScreenRotationAnimation(); + } + return true; } @@ -460,11 +412,13 @@ class ScreenRotationAnimation { */ public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) { - if (mSurfaceControl == null) { + if (mScreenshotLayer == null) { // Can't do animation. return false; } if (!mStarted) { + mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(), + mDisplayContent.getWindowingLayer()); startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight, exitAnim, enterAnim); } @@ -480,28 +434,28 @@ class ScreenRotationAnimation { mSurfaceRotationAnimationController.cancel(); mSurfaceRotationAnimationController = null; } - if (mSurfaceControl != null) { - ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mSurfaceControl); - mSurfaceControl = null; + + if (mScreenshotLayer != null) { + ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mScreenshotLayer); SurfaceControl.Transaction t = mService.mTransactionFactory.get(); - if (mRotationLayer != null) { - if (mRotationLayer.isValid()) { - t.remove(mRotationLayer); - } - mRotationLayer = null; + if (mScreenshotLayer.isValid()) { + t.remove(mScreenshotLayer); } + mScreenshotLayer = null; + if (mEnterBlackFrameLayer != null) { if (mEnterBlackFrameLayer.isValid()) { t.remove(mEnterBlackFrameLayer); } mEnterBlackFrameLayer = null; } + if (mBackColorSurface != null) { + t.remove(mBackColorSurface); + mBackColorSurface = null; + } t.apply(); } - if (mExitingBlackFrame != null) { - mExitingBlackFrame.kill(); - mExitingBlackFrame = null; - } + if (mEnteringBlackFrame != null) { mEnteringBlackFrame.kill(); mEnteringBlackFrame = null; @@ -537,18 +491,28 @@ class ScreenRotationAnimation { * Utility class that runs a {@link ScreenRotationAnimation} on the {@link * SurfaceAnimationRunner}. * <p> - * The rotation animation is divided into the following hierarchy: + * The rotation animation supports both screen rotation and custom animations + * + * For custom animations: + * <ul> + * <li> + * The screenshot layer which has an added animation of it's alpha channel + * ("screen_rotate_alpha") and that will be applied along with the custom animation. + * </li> + * <li> A device layer that is animated with the provided custom animation </li> + * </ul> + * + * For screen rotation: * <ul> - * <li> A first rotation layer, containing the blackframes. This layer is animated by the - * "screen_rotate_X_exit" that applies a scale and rotate and where X is value of the rotation. - * <ul> - * <li> A child layer containing the screenshot on which is added an animation of it's - * alpha channel ("screen_rotate_alpha") and that will rotate with his parent layer.</li> - * </ul> - * <li> A second rotation layer used when custom animations are passed in + * <li> A rotation layer that is both rotated and faded out during a single animation </li> + * <li> A device layer that is both rotated and faded in during a single animation </li> + * <li> A background color layer that transitions colors behind the first two layers </li> + * </ul> + * * {@link ScreenRotationAnimation#startAnimation( * SurfaceControl.Transaction, long, float, int, int, int, int)}. * </ul> + * * <p> * Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of * this three {@link SurfaceControl}s which then delegates the animation to the @@ -556,22 +520,35 @@ class ScreenRotationAnimation { */ class SurfaceRotationAnimationController { private SurfaceAnimator mDisplayAnimator; - private SurfaceAnimator mEnterBlackFrameAnimator; private SurfaceAnimator mScreenshotRotationAnimator; private SurfaceAnimator mRotateScreenAnimator; + private SurfaceAnimator mEnterBlackFrameAnimator; + + void startCustomAnimation() { + try { + mService.mSurfaceAnimationRunner.deferStartingAnimations(); + mRotateScreenAnimator = startScreenshotAlphaAnimation(); + mDisplayAnimator = startDisplayRotation(); + if (mEnteringBlackFrame != null) { + mEnterBlackFrameAnimator = startEnterBlackFrameAnimation(); + } + } finally { + mService.mSurfaceAnimationRunner.continueStartingAnimations(); + } + } /** * Start the rotation animation of the display and the screenshot on the * {@link SurfaceAnimationRunner}. */ - void startAnimation() { - mRotateScreenAnimator = startScreenshotAlphaAnimation(); - mDisplayAnimator = startDisplayRotation(); - if (mExitingBlackFrame != null) { + void startScreenRotationAnimation() { + try { + mService.mSurfaceAnimationRunner.deferStartingAnimations(); + mDisplayAnimator = startDisplayRotation(); mScreenshotRotationAnimator = startScreenshotRotationAnimation(); - } - if (mEnteringBlackFrame != null) { - mEnterBlackFrameAnimator = startEnterBlackFrameAnimation(); + startColorAnimation(); + } finally { + mService.mSurfaceAnimationRunner.continueStartingAnimations(); } } @@ -596,8 +573,8 @@ class ScreenRotationAnimation { private SurfaceAnimator startScreenshotAlphaAnimation() { return startAnimation(initializeBuilder() - .setSurfaceControl(mSurfaceControl) - .setAnimationLeashParent(mRotationLayer) + .setSurfaceControl(mScreenshotLayer) + .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) .setWidth(mWidth) .setHeight(mHeight) .build(), @@ -616,13 +593,67 @@ class ScreenRotationAnimation { private SurfaceAnimator startScreenshotRotationAnimation() { return startAnimation(initializeBuilder() - .setSurfaceControl(mRotationLayer) + .setSurfaceControl(mScreenshotLayer) .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) .build(), createWindowAnimationSpec(mRotateExitAnimation), this::onAnimationEnd); } + + /** + * Applies the color change from {@link #mStartLuma} to {@link #mEndLuma} as a + * grayscale color + */ + private void startColorAnimation() { + int colorTransitionMs = mContext.getResources().getInteger( + R.integer.config_screen_rotation_color_transition); + final SurfaceAnimationRunner runner = mService.mSurfaceAnimationRunner; + final float[] rgbTmpFloat = new float[3]; + final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma); + final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma); + final long duration = colorTransitionMs * (long) mService.getCurrentAnimatorScale(); + final ArgbEvaluator va = ArgbEvaluator.getInstance(); + runner.startAnimation( + new LocalAnimationAdapter.AnimationSpec() { + @Override + public long getDuration() { + return duration; + } + + @Override + public void apply(SurfaceControl.Transaction t, SurfaceControl leash, + long currentPlayTime) { + float fraction = (float)currentPlayTime / (float)getDuration(); + int color = (Integer) va.evaluate(fraction, startColor, endColor); + Color middleColor = Color.valueOf(color); + rgbTmpFloat[0] = middleColor.red(); + rgbTmpFloat[1] = middleColor.green(); + rgbTmpFloat[2] = middleColor.blue(); + if (leash.isValid()) { + t.setColor(leash, rgbTmpFloat); + } + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "startLuma=" + mStartLuma + + " endLuma=" + mEndLuma + + " durationMs=" + colorTransitionMs); + } + + @Override + public void dumpDebugInner(ProtoOutputStream proto) { + final long token = proto.start(ROTATE); + proto.write(START_LUMA, mStartLuma); + proto.write(END_LUMA, mEndLuma); + proto.write(DURATION_MS, colorTransitionMs); + proto.end(token); + } + }, + mBackColorSurface, mDisplayContent.getPendingTransaction(), null); + } + private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) { return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */, false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */); @@ -646,7 +677,6 @@ class ScreenRotationAnimation { LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter( animationSpec, mService.mSurfaceAnimationRunner); - animator.startAnimation(mDisplayContent.getPendingTransaction(), localAnimationAdapter, false); return animator; @@ -692,7 +722,6 @@ class ScreenRotationAnimation { if (mEnterBlackFrameAnimator != null) { mEnterBlackFrameAnimator.cancelAnimation(); } - if (mScreenshotRotationAnimator != null) { mScreenshotRotationAnimator.cancelAnimation(); } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java index 50cea2ed52cc..5633b6be87b9 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java @@ -78,6 +78,10 @@ class SurfaceAnimationRunner { @GuardedBy("mLock") private boolean mAnimationStartDeferred; + /** + * There should only ever be one instance of this class. Usual spot for it is with + * {@link WindowManagerService} + */ SurfaceAnimationRunner(Supplier<Transaction> transactionFactory, PowerManagerInternal powerManagerInternal) { this(null /* callbackProvider */, null /* animatorFactory */, diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 976730ec4337..5286a6e32958 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -322,12 +322,16 @@ class SurfaceAnimator { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash() .setParent(mAnimatable.getAnimationLeashParent()) + .setHidden(hidden) .setName(surface + " - animation-leash"); final SurfaceControl leash = builder.build(); t.setWindowCrop(leash, width, height); + + // TODO: rely on builder.setHidden(hidden) instead of show and setAlpha when b/138459974 is + // fixed. t.show(leash); - // TODO: change this back to use show instead of alpha when b/138459974 is fixed. t.setAlpha(leash, hidden ? 0 : 1); + t.reparent(surface, leash); return leash; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index cefef37a1363..8ac212f7ef03 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -777,7 +777,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * otherwise. */ boolean isWaitingForTransitionStart() { - return getActivity(app -> app.isWaitingForTransitionStart()) != null; + return false; } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 223e9b9e3df2..74fdba1cd13e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -205,6 +205,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDisplayFoldListener; +import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayWindowRotationController; import android.view.IDockedStackListener; @@ -2768,6 +2769,7 @@ public class WindowManagerService extends IWindowManager.Stub true /* includingParents */); } } + syncInputTransactions(); } /** @@ -3727,6 +3729,48 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setDisplayWindowInsetsController( + int displayId, IDisplayWindowInsetsController insetsController) { + if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return; + } + dc.setRemoteInsetsController(insetsController); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void modifyDisplayWindowInsets(int displayId, InsetsState state) { + if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null || dc.mRemoteInsetsControlTarget == null) { + return; + } + dc.getInsetsStateController().onInsetsModified( + dc.mRemoteInsetsControlTarget, state); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public int watchRotation(IRotationWatcher watcher, int displayId) { final DisplayContent displayContent; synchronized (mGlobalLock) { @@ -7314,7 +7358,8 @@ public class WindowManagerService extends IWindowManager.Stub // If there was a pending IME show(), reset it as IME has been // requested to be hidden. dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout(); - dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */); + dc.mInputMethodControlTarget.hideInsets(WindowInsets.Type.ime(), + true /* fromIme */); } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4e768da18041..ba40f623ea66 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -688,6 +688,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mForceSeamlesslyRotate || requested) { + if (mControllableInsetProvider != null) { + mControllableInsetProvider.startSeamlessRotation(); + } mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo()); mPendingSeamlessRotate.unrotate(transaction, this); getDisplayContent().getDisplayRotation().markForSeamlessRotation(this, @@ -702,6 +705,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mPendingSeamlessRotate = null; getDisplayContent().getDisplayRotation().markForSeamlessRotation(this, false /* seamlesslyRotated */); + if (mControllableInsetProvider != null) { + mControllableInsetProvider.finishSeamlessRotation(timeout); + } } } @@ -792,8 +798,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mSeq = seq; mPowerManagerWrapper = powerManagerWrapper; mForceSeamlesslyRotate = token.mRoundedCornerOverlay; - mRequestedInsetsState = - getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this); + mRequestedInsetsState = new InsetsState( + getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this), + true /* copySources */); if (DEBUG) { Slog.v(TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java new file mode 100644 index 000000000000..94f66768d5ef --- /dev/null +++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java @@ -0,0 +1,97 @@ +/* + * 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.wm.utils; + +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.Rect; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceControl; + + +/** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/ +public class RotationAnimationUtils { + + /** + * Converts the provided {@link GraphicBuffer} and converts it to a bitmap to then sample the + * luminance at the borders of the bitmap + * @return the average luminance of all the pixels at the borders of the bitmap + */ + public static float getAvgBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) { + Bitmap hwBitmap = Bitmap.wrapHardwareBuffer(graphicBuffer, colorSpace); + if (hwBitmap == null) { + return 0; + } + + Bitmap swaBitmap = hwBitmap.copy(Bitmap.Config.ARGB_8888, false); + float totalLuma = 0; + int height = swaBitmap.getHeight(); + int width = swaBitmap.getWidth(); + int i; + for (i = 0; i < width; i++) { + totalLuma += swaBitmap.getColor(i, 0).luminance(); + totalLuma += swaBitmap.getColor(i, height - 1).luminance(); + } + for (i = 0; i < height; i++) { + totalLuma += swaBitmap.getColor(0, i).luminance(); + totalLuma += swaBitmap.getColor(width - 1, i).luminance(); + } + return totalLuma / (2 * width + 2 * height); + } + + /** + * Gets the average border luma by taking a screenshot of the {@param surfaceControl}. + * @see #getAvgBorderLuma(GraphicBuffer, ColorSpace) + */ + public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) { + if (surfaceControl == null) { + return 0; + } + + Point size = new Point(); + display.getSize(size); + Rect crop = new Rect(0, 0, size.x, size.y); + SurfaceControl.ScreenshotGraphicBuffer buffer = + SurfaceControl.captureLayers(surfaceControl, crop, 1); + return RotationAnimationUtils.getAvgBorderLuma(buffer.getGraphicBuffer(), + buffer.getColorSpace()); + } + + public static void createRotationMatrix(int rotation, int width, int height, Matrix outMatrix) { + switch (rotation) { + case Surface.ROTATION_0: + outMatrix.reset(); + break; + case Surface.ROTATION_90: + outMatrix.setRotate(90, 0, 0); + outMatrix.postTranslate(height, 0); + break; + case Surface.ROTATION_180: + outMatrix.setRotate(180, 0, 0); + outMatrix.postTranslate(width, height); + break; + case Surface.ROTATION_270: + outMatrix.setRotate(270, 0, 0); + outMatrix.postTranslate(0, width); + break; + } + } +} diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 2e8e5e7b706a..c0891d739788 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1757,6 +1757,12 @@ static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jclass /* clazz */, jlon return im->getInputManager()->getReader()->canDispatchToDisplay(deviceId, displayId); } +static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + im->getInputManager()->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_DISPLAY_INFO); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { @@ -1842,6 +1848,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*) nativeSetCustomPointerIcon }, { "nativeCanDispatchToDisplay", "(JII)Z", (void*) nativeCanDispatchToDisplay }, + { "nativeNotifyPortAssociationsChanged", "(J)V", + (void*) nativeNotifyPortAssociationsChanged }, }; #define FIND_CLASS(var, className) \ diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp index 380ee942af98..cdbe77a3d64c 100644 --- a/services/devicepolicy/Android.bp +++ b/services/devicepolicy/Android.bp @@ -18,8 +18,3 @@ java_library_static { "compat-changeid-annotation-processor", ], } - -platform_compat_config { - name: "services-devicepolicy-platform-compat-config", - src: ":services.devicepolicy", -} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 5a1d552bf325..8641059aebd5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -64,4 +64,8 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { } public void setLocationEnabled(ComponentName who, boolean locationEnabled) {} + + public boolean isOrganizationOwnedDeviceWithManagedProfile() { + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 13246ba9e587..d5ff2802499c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8607,6 +8607,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean isOrganizationOwnedDeviceWithManagedProfile() { + if (!mHasFeature) { + return false; + } + enforceManageUsers(); + + return mInjector.binderWithCleanCallingIdentity(() -> { + for (UserInfo ui : mUserManager.getUsers()) { + if (ui.isManagedProfile() && isProfileOwnerOfOrganizationOwnedDevice(ui.id)) { + return true; + } + } + + return false; + }); + } + + @Override public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) { ensureCallerIdentityMatchesIfNotSystem(packageName, pid, uid); @@ -11705,6 +11723,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mStateCache; } + @Override + public List<String> getAllCrossProfilePackages() { + return DevicePolicyManagerService.this.getAllCrossProfilePackages(); + } + } private Intent createShowAdminSupportIntent(ComponentName admin, int userId) { @@ -14584,8 +14607,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { for (int i = 0; i < users.length; i++) { final ComponentName componentName = getProfileOwner(users[i]); if (componentName != null) { - admins.add(getActiveAdminForCallerLocked( - componentName, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)); + ActiveAdmin admin = getActiveAdminUncheckedLocked(componentName, users[i]); + if (admin != null) { + admins.add(admin); + } } } return admins; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index bfec51c92651..b6a8ca447213 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -751,6 +751,7 @@ public final class SystemServer { // Now that we have the bare essentials of the OS up and running, take // note that we just booted, which might send out a rescue party if // we're stuck in a runtime restart loop. + RescueParty.registerHealthObserver(mSystemContext); RescueParty.noteBoot(mSystemContext); // Manages LEDs and display backlight so we need it to bring up the display. diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java index 61cd88aac921..c93e5c5e4759 100644 --- a/services/net/java/android/net/ip/IpClientCallbacks.java +++ b/services/net/java/android/net/ip/IpClientCallbacks.java @@ -17,6 +17,7 @@ package android.net.ip; import android.net.DhcpResults; +import android.net.DhcpResultsParcelable; import android.net.Layer2PacketParcelable; import android.net.LinkProperties; @@ -69,6 +70,18 @@ public class IpClientCallbacks { public void onNewDhcpResults(DhcpResults dhcpResults) {} /** + * Callback called when new DHCP results are available. + * + * <p>This is purely advisory and not an indication of provisioning success or failure. This is + * only here for callers that want to expose DHCPv4 results to other APIs + * (e.g., WifiInfo#setInetAddress). + * + * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not + * the passed-in DhcpResults object is null. + */ + public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {} + + /** * Indicates that provisioning was successful. */ public void onProvisioningSuccess(LinkProperties newLp) {} diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java index 4d60e6239376..7f723b1c232b 100644 --- a/services/net/java/android/net/ip/IpClientUtil.java +++ b/services/net/java/android/net/ip/IpClientUtil.java @@ -119,6 +119,7 @@ public class IpClientUtil { @Override public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) { mCb.onNewDhcpResults(fromStableParcelable(dhcpResults)); + mCb.onNewDhcpResults(dhcpResults); } @Override diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java index 8632ca4c2898..8b2f15c2babb 100644 --- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -81,7 +81,10 @@ import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowPackageManager; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.List; /** @@ -1238,13 +1241,49 @@ public class UserBackupManagerServiceTest { assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2); } + /** + * Test that {@link UserBackupManagerService#dump()} for system user does not prefix dump with + * "User 0:". + */ + @Test + public void testDump_forSystemUser_DoesNotHaveUserPrefix() throws Exception { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + UserBackupManagerService service = + BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks( + UserHandle.USER_SYSTEM, + mContext, + mBackupThread, + mBaseStateDir, + mDataDir, + mTransportManager); + + StringWriter dump = new StringWriter(); + service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]); + + assertThat(dump.toString()).startsWith("Backup Manager is "); + } + + /** + * Test that {@link UserBackupManagerService#dump()} for non-system user prefixes dump with + * "User <userid>:". + */ + @Test + public void testDump_forNonSystemUser_HasUserPrefix() throws Exception { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks(); + + StringWriter dump = new StringWriter(); + service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]); + + assertThat(dump.toString()).startsWith("User " + USER_ID + ":" + "Backup Manager is "); + } + private File createTestFile() throws IOException { File testFile = new File(mContext.getFilesDir(), "test"); testFile.createNewFile(); return testFile; } - /** * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method. diff --git a/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java new file mode 100644 index 000000000000..4cbdbd178944 --- /dev/null +++ b/services/robotests/src/com/android/server/location/LocationRequestStatisticsTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.location; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import com.android.internal.util.IndentingPrintWriter; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Unit tests for {@link LocationRequestStatistics}. + */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class LocationRequestStatisticsTest { + + /** + * Check adding and removing requests & strings + */ + @Test + public void testRequestSummary() { + LocationRequestStatistics.RequestSummary summary = + new LocationRequestStatistics.RequestSummary( + "com.example", "gps", 1000); + StringWriter stringWriter = new StringWriter(); + summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriter), " "), 1234); + assertThat(stringWriter.toString()).startsWith("At"); + + StringWriter stringWriterRemove = new StringWriter(); + summary = new LocationRequestStatistics.RequestSummary( + "com.example", "gps", + LocationRequestStatistics.RequestSummary.REQUEST_ENDED_INTERVAL); + summary.dump(new IndentingPrintWriter(new PrintWriter(stringWriterRemove), " "), 2345); + assertThat(stringWriterRemove.toString()).contains("-"); + } + + /** + * Check summary list size capping + */ + @Test + public void testSummaryList() { + LocationRequestStatistics statistics = new LocationRequestStatistics(); + statistics.history.addRequest("com.example", "gps", 1000); + assertThat(statistics.history.mList.size()).isEqualTo(1); + // Try (not) to overflow + for (int i = 0; i < LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE; i++) { + statistics.history.addRequest("com.example", "gps", 1000); + } + assertThat(statistics.history.mList.size()).isEqualTo( + LocationRequestStatistics.RequestSummaryLimitedHistory.MAX_SIZE); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index 0d6020c3d06d..30d89d31ba64 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.isNull; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.VersionedPackage; import android.os.RecoverySystem; import android.os.SystemProperties; import android.os.UserHandle; @@ -39,6 +40,8 @@ import android.provider.DeviceConfig; import android.provider.Settings; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.server.PackageWatchdog.PackageHealthObserverImpact; +import com.android.server.RescueParty.RescuePartyObserver; import com.android.server.am.SettingsToPropertiesMapper; import com.android.server.utils.FlagNamespaceUtils; @@ -57,13 +60,15 @@ import java.util.HashMap; * Test RescueParty. */ public class RescuePartyTest { - private static final int PERSISTENT_APP_UID = 12; private static final long CURRENT_NETWORK_TIME_MILLIS = 0L; private static final String FAKE_NATIVE_NAMESPACE1 = "native1"; private static final String FAKE_NATIVE_NAMESPACE2 = "native2"; private static final String[] FAKE_RESET_NATIVE_NAMESPACES = {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2}; + private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1); + private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; + private MockitoSession mSession; @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -182,25 +187,25 @@ public class RescuePartyTest { @Test public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { - notePersistentAppCrash(RescueParty.TRIGGER_COUNT); + notePersistentAppCrash(); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS); assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS, SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - notePersistentAppCrash(RescueParty.TRIGGER_COUNT); + notePersistentAppCrash(); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES); assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES, SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - notePersistentAppCrash(RescueParty.TRIGGER_COUNT); + notePersistentAppCrash(); verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS); assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS, SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - notePersistentAppCrash(RescueParty.TRIGGER_COUNT); + notePersistentAppCrash(); verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG)); assertEquals(RescueParty.LEVEL_FACTORY_RESET, @@ -221,20 +226,6 @@ public class RescuePartyTest { } @Test - public void testPersistentAppCrashDetectionWithWrongInterval() { - notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1); - - // last persistent app crash is just outside of the boot loop detection window - doReturn(CURRENT_NETWORK_TIME_MILLIS - + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS + 1) - .when(() -> RescueParty.getElapsedRealtime()); - notePersistentAppCrash(/*numTimes=*/1); - - assertEquals(RescueParty.LEVEL_NONE, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - } - - @Test public void testBootLoopDetectionWithProperInterval() { noteBoot(RescueParty.TRIGGER_COUNT - 1); @@ -249,21 +240,6 @@ public class RescuePartyTest { } @Test - public void testPersistentAppCrashDetectionWithProperInterval() { - notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1); - - // last persistent app crash is just inside of the boot loop detection window - doReturn(CURRENT_NETWORK_TIME_MILLIS - + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS) - .when(() -> RescueParty.getElapsedRealtime()); - notePersistentAppCrash(/*numTimes=*/1); - - verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - } - - @Test public void testBootLoopDetectionWithWrongTriggerCount() { noteBoot(RescueParty.TRIGGER_COUNT - 1); assertEquals(RescueParty.LEVEL_NONE, @@ -271,13 +247,6 @@ public class RescuePartyTest { } @Test - public void testPersistentAppCrashDetectionWithWrongTriggerCount() { - notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1); - assertEquals(RescueParty.LEVEL_NONE, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - } - - @Test public void testIsAttemptingFactoryReset() { noteBoot(RescueParty.TRIGGER_COUNT * 4); @@ -319,6 +288,77 @@ public class RescuePartyTest { FAKE_NATIVE_NAMESPACE2, /*makeDefault=*/true)); } + @Test + public void testExplicitlyEnablingAndDisablingRescue() { + SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); + SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); + assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false); + + SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); + assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)); + } + + @Test + public void testHealthCheckLevels() { + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + + // Ensure that no action is taken for cases where the failure reason is unknown + SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( + RescueParty.LEVEL_FACTORY_RESET)); + assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN), + PackageHealthObserverImpact.USER_IMPACT_NONE); + + /* + For the following cases, ensure that the returned user impact corresponds with the user + impact of the next available rescue level, not the current one. + */ + SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( + RescueParty.LEVEL_NONE)); + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), + PackageHealthObserverImpact.USER_IMPACT_LOW); + + SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( + RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS)); + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), + PackageHealthObserverImpact.USER_IMPACT_LOW); + + + SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( + RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES)); + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), + PackageHealthObserverImpact.USER_IMPACT_HIGH); + + + SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( + RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS)); + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), + PackageHealthObserverImpact.USER_IMPACT_HIGH); + + + SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( + RescueParty.LEVEL_FACTORY_RESET)); + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), + PackageHealthObserverImpact.USER_IMPACT_HIGH); + } + + @Test + public void testRescueLevelIncrementsWhenExecuted() { + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( + RescueParty.LEVEL_NONE)); + observer.execute(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH); + assertEquals(SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, -1), + RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS); + } + private void verifySettingsResets(int resetMode) { verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null, resetMode, UserHandle.USER_SYSTEM)); @@ -332,9 +372,8 @@ public class RescuePartyTest { } } - private void notePersistentAppCrash(int numTimes) { - for (int i = 0; i < numTimes; i++) { - RescueParty.noteAppCrash(mMockContext, PERSISTENT_APP_UID); - } + private void notePersistentAppCrash() { + RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage( + "com.package.name", 1), PackageWatchdog.FAILURE_REASON_UNKNOWN); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index d11d98766b01..591c3a385e23 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -85,6 +85,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.WindowManagerInternal; import org.junit.Before; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java new file mode 100644 index 000000000000..75239db92121 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -0,0 +1,128 @@ +/* + * 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; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.Manifest; +import android.app.PendingIntent; +import android.app.RemoteAction; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.drawable.Icon; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import androidx.test.InstrumentationRegistry; + +import com.android.server.LocalServices; +import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener; +import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.server.wm.WindowManagerInternal; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * APCT tests for {@link AccessibilityManagerService}. + */ +public class AccessibilityManagerServiceTest extends AndroidTestCase { + private static final String TAG = "A11Y_MANAGER_SERVICE_TEST"; + private static final int ACTION_ID = 20; + private static final String LABEL = "label"; + private static final String INTENT_ACTION = "TESTACTION"; + private static final String DESCRIPTION = "description"; + private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast( + InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0); + private static final RemoteAction TEST_ACTION = new RemoteAction( + Icon.createWithContentUri("content://test"), + LABEL, + DESCRIPTION, + TEST_PENDING_INTENT); + private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION = + new AccessibilityAction(ACTION_ID, LABEL); + + @Mock private PackageManager mMockPackageManager; + @Mock private WindowManagerInternal mMockWindowManagerService; + @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy; + @Mock private SystemActionPerformer mMockSystemActionPerformer; + @Mock private AccessibilityWindowManager mMockA11yWindowManager; + @Mock private AccessibilityDisplayListener mMockA11yDisplayListener; + @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal; + + private AccessibilityManagerService mA11yms; + + @Override + protected void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + LocalServices.removeServiceForTest(WindowManagerInternal.class); + LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); + LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerService); + LocalServices.addService( + ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal); + mA11yms = new AccessibilityManagerService( + InstrumentationRegistry.getContext(), + mMockPackageManager, + mMockSecurityPolicy, + mMockSystemActionPerformer, + mMockA11yWindowManager, + mMockA11yDisplayListener); + } + + @SmallTest + public void testRegisterSystemActionWithoutPermission() throws Exception { + doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission( + Manifest.permission.MANAGE_ACCESSIBILITY, + AccessibilityManagerService.FUNCTION_REGISTER_SYSTEM_ACTION); + + try { + mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID); + fail(); + } catch (SecurityException expected) { + } + verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION); + } + + @SmallTest + public void testRegisterSystemAction() throws Exception { + mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID); + verify(mMockSystemActionPerformer).registerSystemAction(ACTION_ID, TEST_ACTION); + } + + @SmallTest + public void testUnregisterSystemActionWithoutPermission() throws Exception { + doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission( + Manifest.permission.MANAGE_ACCESSIBILITY, + AccessibilityManagerService.FUNCTION_UNREGISTER_SYSTEM_ACTION); + + try { + mA11yms.unregisterSystemAction(ACTION_ID); + fail(); + } catch (SecurityException expected) { + } + verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID); + } + + @SmallTest + public void testUnregisterSystemAction() throws Exception { + mA11yms.unregisterSystemAction(ACTION_ID); + verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index 99dd9a12eb72..2ce70b6f0889 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -44,6 +44,7 @@ import android.os.UserHandle; import android.testing.DexmakerShareClassLoaderRule; import android.view.Display; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java index 67075edb0143..9db5a080c093 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -53,6 +53,7 @@ import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java index 44a514f7623c..96ae102e53f3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java @@ -31,6 +31,8 @@ import android.accessibilityservice.FingerprintGestureController; import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback; import android.accessibilityservice.IAccessibilityServiceConnection; +import com.android.server.accessibility.test.MessageCapturingHandler; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java index de7bc443b8c5..30d00ad716e6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java @@ -31,6 +31,7 @@ import android.hardware.fingerprint.IFingerprintService; import android.view.KeyEvent; import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient; +import com.android.server.accessibility.test.MessageCapturingHandler; import org.junit.After; import org.junit.Before; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java index 23ce483be107..41235560dc91 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java @@ -44,6 +44,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.KeyEventDispatcher.KeyEventFilter; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.policy.WindowManagerPolicy; import org.hamcrest.Description; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java index 322653b4115c..78e651b7a3c1 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java @@ -33,6 +33,7 @@ import android.view.KeyEvent; import androidx.test.runner.AndroidJUnit4; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.policy.WindowManagerPolicy; import org.hamcrest.Description; @@ -212,4 +213,4 @@ public class KeyboardInterceptorTest { description.appendText("Matches key event"); } } -}
\ No newline at end of file +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java index 773b8778c7bf..82c6498bd9be 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java @@ -47,6 +47,7 @@ import android.view.MagnificationSpec; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java index 36e854ca77cd..1ac4a8ed96d0 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java @@ -54,6 +54,7 @@ import android.view.accessibility.AccessibilityEvent; import androidx.test.runner.AndroidJUnit4; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.accessibility.utils.MotionEventMatcher; import org.hamcrest.Description; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java index 8da927dcb4ab..c8baca610bdc 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java @@ -37,6 +37,7 @@ import android.hardware.display.DisplayManager; import android.os.IBinder; import android.view.accessibility.AccessibilityEvent; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.WindowManagerInternal; import org.junit.After; diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java index d4182f3d31e2..5a1ad8655ab0 100644 --- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java @@ -672,6 +672,30 @@ public final class AdbDebuggingManagerTest { connectionTime2, mKeyStore.getLastConnectionTime(TEST_KEY_2)); } + @Test + public void testClearAuthorizationsBeforeAdbEnabled() throws Exception { + // The adb key store is not instantiated until adb is enabled; however if the user attempts + // to clear the adb authorizations when adb is disabled after a boot a NullPointerException + // was thrown as deleteKeyStore is invoked against the key store. This test ensures the + // key store can be successfully cleared when adb is disabled. + mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper()); + + clearKeyStore(); + } + + @Test + public void testClearAuthorizationsDeletesKeyFiles() throws Exception { + mAdbKeyFile.createNewFile(); + mAdbKeyXmlFile.createNewFile(); + + clearKeyStore(); + + assertFalse("The adb key file should have been deleted after revocation of the grants", + mAdbKeyFile.exists()); + assertFalse("The adb xml key file should have been deleted after revocation of the grants", + mAdbKeyXmlFile.exists()); + } + /** * Runs an adb test with the provided configuration. * diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java index f3c76b609c25..8871348d0027 100644 --- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManagerInternal; import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.appwidget.PendingHostUpdate; import android.content.BroadcastReceiver; @@ -80,6 +81,7 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase { super.setUp(); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); LocalServices.removeServiceForTest(ShortcutServiceInternal.class); + LocalServices.removeServiceForTest(AppWidgetManagerInternal.class); mTestContext = new TestContext(); mPkgName = mTestContext.getOpPackageName(); diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java index 2326dfda7e12..d44476ed971d 100644 --- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -27,6 +27,7 @@ import static junit.framework.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -59,6 +60,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -83,6 +85,8 @@ public class BackupManagerServiceTest { @Mock private UserBackupManagerService mUserBackupManagerService; @Mock + private UserBackupManagerService mNonSystemUserBackupManagerService; + @Mock private Context mContextMock; @Mock private PrintWriter mPrintWriterMock; @@ -105,7 +109,7 @@ public class BackupManagerServiceTest { mUserServices = new SparseArray<>(); mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService); - mUserServices.append(NON_USER_SYSTEM, mUserBackupManagerService); + mUserServices.append(NON_USER_SYSTEM, mNonSystemUserBackupManagerService); when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock); when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock); @@ -512,6 +516,26 @@ public class BackupManagerServiceTest { mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]); verifyNoMoreInteractions(mUserBackupManagerService); + verifyNoMoreInteractions(mNonSystemUserBackupManagerService); + } + + /** + * Test that {@link BackupManagerService#dump()} dumps system user information before non-system + * user information. + */ + + @Test + public void testDump_systemUserFirst() { + String[] args = new String[0]; + mService.dumpWithoutCheckingPermission(mFileDescriptorStub, mPrintWriterMock, args); + + InOrder inOrder = + inOrder(mUserBackupManagerService, mNonSystemUserBackupManagerService); + inOrder.verify(mUserBackupManagerService) + .dump(mFileDescriptorStub, mPrintWriterMock, args); + inOrder.verify(mNonSystemUserBackupManagerService) + .dump(mFileDescriptorStub, mPrintWriterMock, args); + inOrder.verifyNoMoreInteractions(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java new file mode 100644 index 000000000000..d0767ccb6f87 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java @@ -0,0 +1,58 @@ +/* + * 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.compat; + +import android.content.pm.ApplicationInfo; + +class ApplicationInfoBuilder { + private boolean mIsDebuggable; + private int mTargetSdk; + private String mPackageName; + + private ApplicationInfoBuilder() { + mTargetSdk = -1; + } + + static ApplicationInfoBuilder create() { + return new ApplicationInfoBuilder(); + } + + ApplicationInfoBuilder withTargetSdk(int targetSdk) { + mTargetSdk = targetSdk; + return this; + } + + ApplicationInfoBuilder debuggable() { + mIsDebuggable = true; + return this; + } + + ApplicationInfoBuilder withPackageName(String packageName) { + mPackageName = packageName; + return this; + } + + ApplicationInfo build() { + final ApplicationInfo applicationInfo = new ApplicationInfo(); + if (mIsDebuggable) { + applicationInfo.flags |= ApplicationInfo.FLAG_DEBUGGABLE; + } + applicationInfo.packageName = mPackageName; + applicationInfo.targetSdkVersion = mTargetSdk; + return applicationInfo; + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java new file mode 100644 index 000000000000..328c71dbc7db --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -0,0 +1,99 @@ +/* + * 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.compat; + +import android.content.Context; + +import com.android.internal.compat.AndroidBuildClassifier; + +import java.util.ArrayList; + +/** + * Helper class for creating a CompatConfig. + */ +class CompatConfigBuilder { + private ArrayList<CompatChange> mChanges; + private AndroidBuildClassifier mBuildClassifier; + private Context mContext; + + private CompatConfigBuilder(AndroidBuildClassifier buildClassifier, Context context) { + mChanges = new ArrayList<>(); + mBuildClassifier = buildClassifier; + mContext = context; + } + + static CompatConfigBuilder create(AndroidBuildClassifier buildClassifier, Context context) { + return new CompatConfigBuilder(buildClassifier, context); + } + + CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) { + mChanges.add(new CompatChange(id, "", sdk, false, "")); + return this; + } + + CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) { + mChanges.add(new CompatChange(id, "", sdk, true, "")); + return this; + } + + CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) { + mChanges.add(new CompatChange(id, name, sdk, false, "")); + return this; + } + + CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id, + String description) { + mChanges.add(new CompatChange(id, "", sdk, false, description)); + return this; + } + + CompatConfigBuilder addEnabledChangeWithId(long id) { + mChanges.add(new CompatChange(id, "", -1, false, "")); + return this; + } + + CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) { + mChanges.add(new CompatChange(id, name, -1, false, "")); + return this; + } + CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) { + mChanges.add(new CompatChange(id, "", -1, false, description)); + return this; + } + + CompatConfigBuilder addDisabledChangeWithId(long id) { + mChanges.add(new CompatChange(id, "", -1, true, "")); + return this; + } + + CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) { + mChanges.add(new CompatChange(id, name, -1, true, "")); + return this; + } + + CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) { + mChanges.add(new CompatChange(id, "", -1, true, description)); + return this; + } + + CompatConfig build() { + CompatConfig config = new CompatConfig(mBuildClassifier, mContext); + for (CompatChange change : mChanges) { + config.addChange(change); + } + return config; + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index cb99c118a407..407f67e2fd8e 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -18,12 +18,25 @@ package com.android.server.compat; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; + +import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.compat.AndroidBuildClassifier; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.io.File; import java.io.FileOutputStream; @@ -34,12 +47,12 @@ import java.util.UUID; @RunWith(AndroidJUnit4.class) public class CompatConfigTest { - private ApplicationInfo makeAppInfo(String pName, int targetSdkVersion) { - ApplicationInfo ai = new ApplicationInfo(); - ai.packageName = pName; - ai.targetSdkVersion = targetSdkVersion; - return ai; - } + @Mock + private Context mContext; + @Mock + PackageManager mPackageManager; + @Mock + private AndroidBuildClassifier mBuildClassifier; private File createTempDir() { String base = System.getProperty("java.io.tmpdir"); @@ -54,112 +67,206 @@ public class CompatConfigTest { os.close(); } + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + // Assume userdebug/eng non-final build + when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); + when(mBuildClassifier.isFinalBuild()).thenReturn(false); + } + + @Test + public void testUnknownChangeEnabled() throws Exception { + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build())) + .isTrue(); + } + @Test - public void testUnknownChangeEnabled() { - CompatConfig pc = new CompatConfig(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue(); + public void testDisabledChangeDisabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build())) + .isFalse(); } @Test - public void testDisabledChangeDisabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, "")); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); + public void testTargetSdkChangeDisabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addTargetSdkChangeWithId(2, 1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(2).build())) + .isFalse(); } @Test - public void testTargetSdkChangeDisabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, null)); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse(); + public void testTargetSdkChangeEnabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addTargetSdkChangeWithId(2, 1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); } @Test - public void testTargetSdkChangeEnabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, "")); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); + public void testDisabledOverrideTargetSdkChange() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addTargetSdkDisabledChangeWithId(2, 1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isFalse(); } @Test - public void testDisabledOverrideTargetSdkChange() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null)); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isFalse(); + public void testGetDisabledChanges() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .addEnabledChangeWithId(2345L) + .build(); + + assertThat(compatConfig.getDisabledChanges( + ApplicationInfoBuilder.create().build())).asList().containsExactly(1234L); } @Test - public void testGetDisabledChanges() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); - pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false, null)); - assertThat(pc.getDisabledChanges( - makeAppInfo("com.some.package", 2))).asList().containsExactly(1234L); + public void testGetDisabledChangesSorted() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .addDisabledChangeWithId(123L) + .addDisabledChangeWithId(12L) + .build(); + + assertThat(compatConfig.getDisabledChanges(ApplicationInfoBuilder.create().build())) + .asList().containsExactly(12L, 123L, 1234L); } @Test - public void testGetDisabledChangesSorted() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null)); - pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true, null)); - pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true, null)); - assertThat(pc.getDisabledChanges( - makeAppInfo("com.some.package", 2))).asList().containsExactly(12L, 123L, 1234L); + public void testPackageOverrideEnabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + + compatConfig.addOverride(1234L, "com.some.package", true); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.some.package").build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.other.package").build())).isFalse(); } @Test - public void testPackageOverrideEnabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); // disabled - pc.addOverride(1234L, "com.some.package", true); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isFalse(); + public void testPackageOverrideDisabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1234L) + .build(); + + compatConfig.addOverride(1234L, "com.some.package", false); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.some.package").build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.other.package").build())).isTrue(); } @Test - public void testPackageOverrideDisabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null)); - pc.addOverride(1234L, "com.some.package", false); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue(); + public void testPackageOverrideUnknownPackage() throws Exception { + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + + compatConfig.addOverride(1234L, "com.some.package", false); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.some.package").build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.other.package").build())).isTrue(); } @Test - public void testPackageOverrideUnknownPackage() { - CompatConfig pc = new CompatConfig(); - pc.addOverride(1234L, "com.some.package", false); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue(); + public void testPreventAddOverride() throws Exception { + final long changeId = 1234L; + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + PackageManager packageManager = mock(PackageManager.class); + when(mContext.getPackageManager()).thenReturn(packageManager); + when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt())) + .thenReturn(applicationInfo); + + // Force the validator to prevent overriding the change by using a user build. + when(mBuildClassifier.isDebuggableBuild()).thenReturn(false); + when(mBuildClassifier.isFinalBuild()).thenReturn(true); + + assertThrows(SecurityException.class, + () -> compatConfig.addOverride(1234L, "com.some.package", true) + ); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse(); } @Test - public void testPackageOverrideUnknownChange() { - CompatConfig pc = new CompatConfig(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue(); + public void testPreventRemoveOverride() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt())) + .thenReturn(applicationInfo); + // Assume the override was allowed to be added. + compatConfig.addOverride(1234L, "com.some.package", true); + + // Validator allows turning on the change. + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); + + // Reject all override attempts. + // Force the validator to prevent overriding the change by using a user build. + when(mBuildClassifier.isDebuggableBuild()).thenReturn(false); + when(mBuildClassifier.isFinalBuild()).thenReturn(true); + // Try to turn off change, but validator prevents it. + assertThrows(SecurityException.class, + () -> compatConfig.removeOverride(1234L, "com.some.package")); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); } @Test - public void testRemovePackageOverride() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null)); - pc.addOverride(1234L, "com.some.package", false); - pc.removeOverride(1234L, "com.some.package"); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue(); + public void testRemovePackageOverride() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + + assertThat(compatConfig.addOverride(1234L, "com.some.package", false)).isTrue(); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse(); + + compatConfig.removeOverride(1234L, "com.some.package"); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); } @Test - public void testLookupChangeId() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null)); - pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false, null)); - assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(1234L); + public void testLookupChangeId() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithIdAndName(1234L, "MY_CHANGE") + .addEnabledChangeWithIdAndName(2345L, "MY_OTHER_CHANGE") + .build(); + + assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(1234L); } @Test - public void testLookupChangeIdNotPresent() { - CompatConfig pc = new CompatConfig(); - assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L); + public void testLookupChangeIdNotPresent() throws Exception { + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(-1L); } @Test @@ -172,14 +279,17 @@ public class CompatConfigTest { File dir = createTempDir(); writeToFile(dir, "platform_compat_config.xml", configXml); - - CompatConfig pc = new CompatConfig(); - pc.initConfigFromLib(dir); - - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); - assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); - assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + compatConfig.initConfigFromLib(dir); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1235L, + ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1236L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); } @Test @@ -195,15 +305,16 @@ public class CompatConfigTest { File dir = createTempDir(); writeToFile(dir, "libcore_platform_compat_config.xml", configXml1); writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2); - - CompatConfig pc = new CompatConfig(); - pc.initConfigFromLib(dir); - - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); - assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); - assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + compatConfig.initConfigFromLib(dir); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1235L, + ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1236L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); } } - - diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java new file mode 100644 index 000000000000..793296e88169 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.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.compat; + +import android.compat.Compatibility; + +import com.android.internal.compat.CompatibilityChangeConfig; + +import java.util.HashSet; +import java.util.Set; + +class CompatibilityChangeConfigBuilder { + private Set<Long> mEnabled; + private Set<Long> mDisabled; + + private CompatibilityChangeConfigBuilder() { + mEnabled = new HashSet<>(); + mDisabled = new HashSet<>(); + } + + static CompatibilityChangeConfigBuilder create() { + return new CompatibilityChangeConfigBuilder(); + } + + CompatibilityChangeConfigBuilder enable(Long id) { + mEnabled.add(id); + return this; + } + + CompatibilityChangeConfigBuilder disable(Long id) { + mDisabled.add(id); + return this; + } + + CompatibilityChangeConfig build() { + return new CompatibilityChangeConfig(new Compatibility.ChangeConfig(mEnabled, mDisabled)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java new file mode 100644 index 000000000000..ecd07bdc4544 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -0,0 +1,383 @@ +/* + * 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.compat; + +import static com.android.internal.compat.OverrideAllowedState.ALLOWED; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.compat.AndroidBuildClassifier; +import com.android.internal.compat.IOverrideValidator; +import com.android.internal.compat.OverrideAllowedState; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class OverrideValidatorImplTest { + private static final String PACKAGE_NAME = "my.package"; + private static final int TARGET_SDK = 10; + private static final int TARGET_SDK_BEFORE = 9; + private static final int TARGET_SDK_AFTER = 11; + + @Mock + private PackageManager mPackageManager; + @Mock + Context mContext; + + private AndroidBuildClassifier debuggableBuild() { + AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); + when(buildClassifier.isDebuggableBuild()).thenReturn(true); + return buildClassifier; + } + + private AndroidBuildClassifier betaBuild() { + AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); + when(buildClassifier.isDebuggableBuild()).thenReturn(false); + when(buildClassifier.isFinalBuild()).thenReturn(false); + return buildClassifier; + } + + private AndroidBuildClassifier finalBuild() { + AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); + when(buildClassifier.isDebuggableBuild()).thenReturn(false); + when(buildClassifier.isFinalBuild()).thenReturn(true); + return buildClassifier; + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + } + + @Test + public void getOverrideAllowedState_debugBuildAnyChangeDebugApp_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withTargetSdk(TARGET_SDK) + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + } + + @Test + public void getOverrideAllowedState_debugBuildAnyChangeReleaseApp_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + } + + @Test + public void getOverrideAllowedState_betaBuildTargetSdkChangeDebugApp_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withTargetSdk(TARGET_SDK) + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_BEFORE)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER)); + } + + @Test + public void getOverrideAllowedState_betaBuildEnabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addEnabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .debuggable() + .build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_betaBuildDisabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addDisabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_betaBuildAnyChangeReleaseApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + } + + @Test + public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withTargetSdk(TARGET_SDK) + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER)); + } + + @Test + public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK) + .debuggable() + .build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange).isEqualTo( + new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK, + TARGET_SDK_BEFORE)); + assertThat(stateTargetSdkEqualChange).isEqualTo( + new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK, TARGET_SDK)); + } + + @Test + public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addEnabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .debuggable().build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addDisabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .debuggable().build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_finalBuildAnyChangeReleaseApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java index c406876c5cee..ce5d6d9be770 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -26,21 +26,20 @@ import static org.mockito.Mockito.when; import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.testng.Assert.assertThrows; -import android.compat.Compatibility; import android.content.Context; import android.content.pm.PackageManager; -import com.android.internal.compat.CompatibilityChangeConfig; +import androidx.test.runner.AndroidJUnit4; -import com.google.common.collect.ImmutableSet; +import com.android.internal.compat.AndroidBuildClassifier; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.MockitoAnnotations; -@RunWith(MockitoJUnitRunner.class) +@RunWith(AndroidJUnit4.class) public class PlatformCompatTest { private static final String PACKAGE_NAME = "my.package"; @@ -50,84 +49,77 @@ public class PlatformCompatTest { private PackageManager mPackageManager; @Mock CompatChange.ChangeListener mListener1, mListener2; - + PlatformCompat mPlatformCompat; + CompatConfig mCompatConfig; + @Mock + private AndroidBuildClassifier mBuildClassifier; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow( new PackageManager.NameNotFoundException()); - CompatConfig.get().clearChanges(); + mCompatConfig = new CompatConfig(mBuildClassifier, mContext); + mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); + // Assume userdebug/eng non-final build + when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); + when(mBuildClassifier.isFinalBuild()).thenReturn(false); } @Test - public void testRegisterListenerToSameIdThrows() { - PlatformCompat pc = new PlatformCompat(mContext); - + public void testRegisterListenerToSameIdThrows() throws Exception { // Registering a listener to change 1 is successful. - pc.registerListener(1, mListener1); + mPlatformCompat.registerListener(1, mListener1); // Registering a listener to change 2 is successful. - pc.registerListener(2, mListener1); + mPlatformCompat.registerListener(2, mListener1); // Trying to register another listener to change id 1 fails. - assertThrows(IllegalStateException.class, () -> pc.registerListener(1, mListener1)); + assertThrows(IllegalStateException.class, + () -> mPlatformCompat.registerListener(1, mListener1)); } @Test - public void testRegisterListenerReturn() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())), + public void testRegisterListenerReturn() throws Exception { + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).build(), PACKAGE_NAME); // Change id 1 is known (added in setOverrides). - assertThat(pc.registerListener(1, mListener1)).isTrue(); + assertThat(mPlatformCompat.registerListener(1, mListener1)).isTrue(); // Change 2 is unknown. - assertThat(pc.registerListener(2, mListener1)).isFalse(); + assertThat(mPlatformCompat.registerListener(2, mListener1)).isFalse(); } @Test - public void testListenerCalledOnSetOverrides() { - PlatformCompat pc = new PlatformCompat(mContext); + public void testListenerCalledOnSetOverrides() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener1); - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener1); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerNotCalledOnWrongPackage() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener1); + public void testListenerNotCalledOnWrongPackage() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener1); - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, never()).onCompatChange("other.package"); } @Test - public void testListenerCalledOnSetOverridesTwoListeners() { - PlatformCompat pc = new PlatformCompat(mContext); - pc.registerListener(1, mListener1); + public void testListenerCalledOnSetOverridesTwoListeners() throws Exception { + mPlatformCompat.registerListener(1, mListener1); - final ImmutableSet<Long> enabled = ImmutableSet.of(1L); - final ImmutableSet<Long> disabled = ImmutableSet.of(2L); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -136,11 +128,10 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.registerListener(2, mListener2); + mPlatformCompat.registerListener(2, mListener2); - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -148,31 +139,23 @@ public class PlatformCompatTest { } @Test - public void testListenerCalledOnSetOverridesForTest() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener1); + public void testListenerCalledOnSetOverridesForTest() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener1); - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnSetOverridesTwoListenersForTest() { - PlatformCompat pc = new PlatformCompat(mContext); - pc.registerListener(1, mListener1); + public void testListenerCalledOnSetOverridesTwoListenersForTest() throws Exception { + mPlatformCompat.registerListener(1, mListener1); - final ImmutableSet<Long> enabled = ImmutableSet.of(1L); - final ImmutableSet<Long> disabled = ImmutableSet.of(2L); - - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -181,10 +164,10 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.registerListener(2, mListener2); - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.registerListener(2, mListener2); + + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -192,15 +175,12 @@ public class PlatformCompatTest { } @Test - public void testListenerCalledOnClearOverrides() { - PlatformCompat pc = new PlatformCompat(mContext); + public void testListenerCalledOnClearOverrides() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener2); - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener2); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); @@ -208,21 +188,18 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.clearOverrides(PACKAGE_NAME); + mPlatformCompat.clearOverrides(PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnClearOverridesMultipleOverrides() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener2); + public void testListenerCalledOnClearOverridesMultipleOverrides() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener2); - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME); @@ -230,21 +207,18 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.clearOverrides(PACKAGE_NAME); + mPlatformCompat.clearOverrides(PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnClearOverrideExists() { - PlatformCompat pc = new PlatformCompat(mContext); + public void testListenerCalledOnClearOverrideExists() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener2); - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener2); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); @@ -252,21 +226,17 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.clearOverride(1, PACKAGE_NAME); + mPlatformCompat.clearOverride(1, PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnClearOverrideDoesntExist() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); + public void testListenerCalledOnClearOverrideDoesntExist() throws Exception { + mPlatformCompat.registerListener(1, mListener1); - pc.clearOverride(1, PACKAGE_NAME); + mPlatformCompat.clearOverride(1, PACKAGE_NAME); // Listener not called when a non existing override is removed. verify(mListener1, never()).onCompatChange(PACKAGE_NAME); } - - } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index a16e14f61a66..175c7565a005 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -3636,6 +3636,29 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } + public void testIsOrganizationOwnedDevice() throws Exception { + setupProfileOwner(); + // Set up the user manager to return correct user info + UserInfo managedProfileUserInfo = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, + "managed profile", + UserInfo.FLAG_MANAGED_PROFILE); + when(getServices().userManager.getUsers()) + .thenReturn(Arrays.asList(managedProfileUserInfo)); + + // Any caller without the MANAGE_USERS permission should get a security exception. + assertExpectException(SecurityException.class, null, () -> + dpm.isOrganizationOwnedDeviceWithManagedProfile()); + // But when the right permission is granted, this should succeed. + mContext.permissions.add(android.Manifest.permission.MANAGE_USERS); + assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile()); + configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); + assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile()); + + // A random caller from another user should also be able to get the right result. + mContext.binder.callingUid = DpmMockContext.ANOTHER_UID; + assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile()); + } + public void testSetTime() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); 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 ebca240819e8..25d077823a3f 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -78,7 +78,7 @@ public class DisplayModeDirectorTest { int displayId = 0; // With no votes present, DisplayModeDirector should allow any refresh rate. - assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*defaultModeId=*/60, + assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/60, new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY)), createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs( displayId)); @@ -105,7 +105,7 @@ public class DisplayModeDirectorTest { director.injectVotesByDisplay(votesByDisplay); assertEquals( new DisplayModeDirector.DesiredDisplayModeSpecs( - /*defaultModeId=*/minFps + i, + /*baseModeId=*/minFps + i, new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i)), director.getDesiredDisplayModeSpecs(displayId)); } @@ -126,7 +126,7 @@ public class DisplayModeDirectorTest { votes.put(DisplayModeDirector.Vote.MIN_PRIORITY, DisplayModeDirector.Vote.forRefreshRates(70, 80)); director.injectVotesByDisplay(votesByDisplay); - assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*defaultModeId=*/70, + assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/70, new DisplayModeDirector.RefreshRateRange(70, 80)), director.getDesiredDisplayModeSpecs(displayId)); } diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java new file mode 100644 index 000000000000..1eb5eb51504a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START; +import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END; +import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START; +import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; +import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; +import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; +import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; +import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; +import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; +import static com.android.server.integrity.utils.TestUtils.getBits; +import static com.android.server.integrity.utils.TestUtils.getBytes; +import static com.android.server.integrity.utils.TestUtils.getValueBits; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.content.integrity.AtomicFormula; +import android.content.integrity.CompoundFormula; +import android.content.integrity.Rule; + +import com.android.server.integrity.parser.BinaryFileOperations; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.IOException; + +@RunWith(JUnit4.class) +public class BitTrackedInputStreamTest { + private static final String COMPOUND_FORMULA_START_BITS = + getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS); + private static final String COMPOUND_FORMULA_END_BITS = + getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS); + private static final String ATOMIC_FORMULA_START_BITS = + getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS); + private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS); + private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS); + private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS); + private static final String DENY = getBits(Rule.DENY, EFFECT_BITS); + + private static final String IS_NOT_HASHED = "0"; + private static final String START_BIT = "1"; + private static final String END_BIT = "1"; + + @Test + public void testBitOperationsCountBitsCorrectly() throws IOException { + String packageName = "com.test.app"; + byte[] testInput = + getBytes( + START_BIT + + COMPOUND_FORMULA_START_BITS + + NOT + + ATOMIC_FORMULA_START_BITS + + PACKAGE_NAME + + EQ + + IS_NOT_HASHED + + getBits(packageName.length(), VALUE_SIZE_BITS) + + getValueBits(packageName) + + COMPOUND_FORMULA_END_BITS + + DENY + + END_BIT); + + BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput); + + // Right after construction, the read bits count should be 0. + assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(0); + + // Get next 10 bits should result with 10 bits read. + bitTrackedInputStream.getNext(10); + assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(10); + + // When we move the cursor 8 bytes, we should point to 64 bits. + bitTrackedInputStream.setCursorToByteLocation(8); + assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(64); + + // Read until the end and the total size of the input stream should be available. + while (bitTrackedInputStream.hasNext()) { + bitTrackedInputStream.getNext(1); + } + assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(128); + } + + @Test + public void testBitInputStreamOperationsStillWork() throws IOException { + String packageName = "com.test.app"; + byte[] testInput = + getBytes( + IS_NOT_HASHED + + getBits(packageName.length(), VALUE_SIZE_BITS) + + getValueBits(packageName)); + + BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput); + assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(0); + + // Read until the string parameter. + String stringValue = BinaryFileOperations.getStringValue(bitTrackedInputStream); + + // Verify that the read bytes are counted. + assertThat(stringValue).isEqualTo(packageName); + assertThat(bitTrackedInputStream.getReadBitsCount()).isGreaterThan(0); + } + + @Test + public void testBitTrackedInputStream_moveCursorForwardFailsIfAlreadyRead() throws IOException { + String packageName = "com.test.app"; + byte[] testInput = + getBytes( + IS_NOT_HASHED + + getBits(packageName.length(), VALUE_SIZE_BITS) + + getValueBits(packageName)); + + BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput); + + // Read more than two bytes. + bitTrackedInputStream.getNext(20); + + // Ask to move the cursor to the second byte. + assertThrows( + IllegalStateException.class, + () -> bitTrackedInputStream.setCursorToByteLocation(2)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java index 5ecb8b5c8169..c7cc343dd77e 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.android.server.integrity.serializer; +package com.android.server.integrity.model; import static com.google.common.truth.Truth.assertThat; -import com.android.server.integrity.model.BitOutputStream; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index 9cc0ed85a044..51f5c755754c 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -48,6 +48,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -97,8 +98,10 @@ public class RuleBinaryParserTest { private static final byte[] DEFAULT_FORMAT_VERSION_BYTES = getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS)); + private static final List<RuleIndexRange> NO_INDEXING = Collections.emptyList(); + @Test - public void testBinaryStream_validCompoundFormula() throws Exception { + public void testBinaryStream_validCompoundFormula_noIndexing() throws Exception { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -131,13 +134,13 @@ public class RuleBinaryParserTest { /* isHashedValue= */ false))), Rule.DENY); - List<Rule> rules = binaryParser.parse(inputStream); + List<Rule> rules = binaryParser.parse(inputStream, NO_INDEXING); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @Test - public void testBinaryString_validCompoundFormula_notConnector() throws Exception { + public void testBinaryString_validCompoundFormula_notConnector_noIndexing() throws Exception { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -175,7 +178,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validCompoundFormula_andConnector() throws Exception { + public void testBinaryString_validCompoundFormula_andConnector_noIndexing() throws Exception { String packageName = "com.test.app"; String appCertificate = "test_cert"; String ruleBits = @@ -223,7 +226,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validCompoundFormula_orConnector() throws Exception { + public void testBinaryString_validCompoundFormula_orConnector_noIndexing() throws Exception { String packageName = "com.test.app"; String appCertificate = "test_cert"; String ruleBits = @@ -272,7 +275,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_stringValue() throws Exception { + public void testBinaryString_validAtomicFormula_stringValue_noIndexing() throws Exception { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -304,7 +307,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_hashedValue() throws Exception { + public void testBinaryString_validAtomicFormula_hashedValue_noIndexing() throws Exception { String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; String ruleBits = START_BIT @@ -337,7 +340,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_integerValue() throws Exception { + public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception { int versionCode = 1; String ruleBits = START_BIT @@ -365,7 +368,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_booleanValue() throws Exception { + public void testBinaryString_validAtomicFormula_booleanValue_noIndexing() throws Exception { String isPreInstalled = "1"; String ruleBits = START_BIT @@ -392,7 +395,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidAtomicFormula() throws Exception { + public void testBinaryString_invalidAtomicFormula_noIndexing() { int versionCode = 1; String ruleBits = START_BIT @@ -415,7 +418,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_withNoRuleList() throws RuleParseException { + public void testBinaryString_withNoRuleList_noIndexing() throws RuleParseException { ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length); rule.put(DEFAULT_FORMAT_VERSION_BYTES); RuleParser binaryParser = new RuleBinaryParser(); @@ -426,7 +429,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_withEmptyRule() throws RuleParseException { + public void testBinaryString_withEmptyRule_noIndexing() { String ruleBits = START_BIT; byte[] ruleBytes = getBytes(ruleBits); ByteBuffer rule = @@ -442,7 +445,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas() throws Exception { + public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas_noIndexing() { String packageName = "com.test.app"; String appCertificate = "test_cert"; String ruleBits = @@ -478,7 +481,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidOperator() throws Exception { + public void testBinaryString_invalidRule_invalidOperator_noIndexing() { int versionCode = 1; String ruleBits = START_BIT @@ -506,7 +509,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidEffect() throws Exception { + public void testBinaryString_invalidRule_invalidEffect_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -536,7 +539,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidConnector() throws Exception { + public void testBinaryString_invalidRule_invalidConnector_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -566,7 +569,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidKey() throws Exception { + public void testBinaryString_invalidRule_invalidKey_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -596,7 +599,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidSeparator() throws Exception { + public void testBinaryString_invalidRule_invalidSeparator_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -626,7 +629,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidEndMarker() throws Exception { + public void testBinaryString_invalidRule_invalidEndMarker_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -653,4 +656,65 @@ public class RuleBinaryParserTest { /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit", () -> binaryParser.parse(rule.array())); } + + @Test + public void testBinaryStream_multipleRules_indexingIdentifiesParsesIndexRangeCorrectly() + throws Exception { + String packageName2 = "com.test.2"; + + byte[] ruleBytes1 = getBytes(getRulesWithPackageName("com.test.1")); + byte[] ruleBytes2 = getBytes(getRulesWithPackageName(packageName2)); + byte[] ruleBytes3 = getBytes(getRulesWithPackageName("com.test.3")); + + ByteBuffer rule = + ByteBuffer.allocate( + DEFAULT_FORMAT_VERSION_BYTES.length + + ruleBytes1.length + + ruleBytes2.length + + ruleBytes3.length); + rule.put(DEFAULT_FORMAT_VERSION_BYTES); + rule.put(ruleBytes1); + rule.put(ruleBytes2); + rule.put(ruleBytes3); + InputStream inputStream = new ByteArrayInputStream(rule.array()); + + RuleParser binaryParser = new RuleBinaryParser(); + + List<RuleIndexRange> indexRanges = new ArrayList<>(); + indexRanges.add( + new RuleIndexRange( + DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length, + DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length + + ruleBytes2.length)); + List<Rule> rules = binaryParser.parse(inputStream, indexRanges); + + Rule expectedRule = + new Rule( + new CompoundFormula( + CompoundFormula.NOT, + Collections.singletonList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + packageName2, + /* isHashedValue= */ false))), + Rule.DENY); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + private static String getRulesWithPackageName(String packageName) { + return START_BIT + + COMPOUND_FORMULA_START_BITS + + NOT + + ATOMIC_FORMULA_START_BITS + + PACKAGE_NAME + + EQ + + IS_NOT_HASHED + + getBits(packageName.length(), VALUE_SIZE_BITS) + + getValueBits(packageName) + + COMPOUND_FORMULA_END_BITS + + DENY + + END_BIT; + + } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java new file mode 100644 index 000000000000..742952e056bc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; +import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY; +import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY; +import static com.android.server.integrity.utils.TestUtils.getBits; +import static com.android.server.integrity.utils.TestUtils.getBytes; +import static com.android.server.integrity.utils.TestUtils.getValueBits; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.integrity.AppInstallMetadata; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.List; + +@RunWith(JUnit4.class) +public class RuleIndexingControllerTest { + + @Test + public void verifyIndexRangeSearchIsCorrect() throws IOException { + InputStream inputStream = obtainDefaultIndexingMapForTest(); + + RuleIndexingController indexingController = new RuleIndexingController(inputStream); + + AppInstallMetadata appInstallMetadata = + new AppInstallMetadata.Builder() + .setPackageName("ddd") + .setAppCertificate("777") + .build(); + + List<RuleIndexRange> resultingIndexes = + indexingController.identifyRulesToEvaluate(appInstallMetadata); + + assertThat(resultingIndexes) + .containsExactly( + new RuleIndexRange(200, 300), + new RuleIndexRange(700, 800), + new RuleIndexRange(900, 945)); + } + + @Test + public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException { + InputStream inputStream = obtainDefaultIndexingMapForTest(); + + RuleIndexingController indexingController = new RuleIndexingController(inputStream); + + AppInstallMetadata appInstallMetadata = + new AppInstallMetadata.Builder() + .setPackageName("bbb") + .setAppCertificate("999") + .build(); + + List<RuleIndexRange> resultingIndexes = + indexingController.identifyRulesToEvaluate(appInstallMetadata); + + assertThat(resultingIndexes) + .containsExactly( + new RuleIndexRange(100, 200), + new RuleIndexRange(800, 900), + new RuleIndexRange(900, 945)); + } + + @Test + public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException { + InputStream inputStream = obtainDefaultIndexingMapForTest(); + + RuleIndexingController indexingController = new RuleIndexingController(inputStream); + + AppInstallMetadata appInstallMetadata = + new AppInstallMetadata.Builder() + .setPackageName("ccc") + .setAppCertificate("444") + .build(); + + List<RuleIndexRange> resultingIndexes = + indexingController.identifyRulesToEvaluate(appInstallMetadata); + + assertThat(resultingIndexes) + .containsExactly( + new RuleIndexRange(200, 300), + new RuleIndexRange(700, 800), + new RuleIndexRange(900, 945)); + } + + @Test + public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException { + byte[] stringBytes = + getBytes( + getKeyValueString(START_INDEXING_KEY, 100) + + getKeyValueString(END_INDEXING_KEY, 500) + + getKeyValueString(START_INDEXING_KEY, 500) + + getKeyValueString(END_INDEXING_KEY, 900) + + getKeyValueString(START_INDEXING_KEY, 900) + + getKeyValueString(END_INDEXING_KEY, 945)); + ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); + rule.put(stringBytes); + InputStream inputStream = new ByteArrayInputStream(rule.array()); + + RuleIndexingController indexingController = new RuleIndexingController(inputStream); + + AppInstallMetadata appInstallMetadata = + new AppInstallMetadata.Builder() + .setPackageName("ccc") + .setAppCertificate("444") + .build(); + + List<RuleIndexRange> resultingIndexes = + indexingController.identifyRulesToEvaluate(appInstallMetadata); + + assertThat(resultingIndexes) + .containsExactly( + new RuleIndexRange(100, 500), + new RuleIndexRange(500, 900), + new RuleIndexRange(900, 945)); + } + + private static InputStream obtainDefaultIndexingMapForTest() { + byte[] stringBytes = + getBytes( + getKeyValueString(START_INDEXING_KEY, 100) + + getKeyValueString("ccc", 200) + + getKeyValueString("eee", 300) + + getKeyValueString("hhh", 400) + + getKeyValueString(END_INDEXING_KEY, 500) + + getKeyValueString(START_INDEXING_KEY, 500) + + getKeyValueString("111", 600) + + getKeyValueString("444", 700) + + getKeyValueString("888", 800) + + getKeyValueString(END_INDEXING_KEY, 900) + + getKeyValueString(START_INDEXING_KEY, 900) + + getKeyValueString(END_INDEXING_KEY, 945)); + ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); + rule.put(stringBytes); + return new ByteArrayInputStream(rule.array()); + } + + private static String getKeyValueString(String key, int value) { + String isNotHashed = "0"; + return isNotHashed + + getBits(key.length(), VALUE_SIZE_BITS) + + getValueBits(key) + + getBits(value, /* numOfBits= */ 32); + } +} diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java index a14197b17529..6944aee7fcb9 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java @@ -73,7 +73,7 @@ public class RuleXmlParserTest { /* isHashedValue= */ false))), Rule.DENY); - List<Rule> rules = xmlParser.parse(inputStream); + List<Rule> rules = xmlParser.parse(inputStream, Collections.emptyList()); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @@ -623,7 +623,7 @@ public class RuleXmlParserTest { assertExpectException( RuleParseException.class, /* expectedExceptionMessageRegex */ "Rules must start with RuleList <RL> tag", - () -> xmlParser.parse(inputStream)); + () -> xmlParser.parse(inputStream, Collections.emptyList())); } private String generateTagWithAttribute( diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 28e6830515f0..c7dbad83e384 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -1273,11 +1273,11 @@ public class NetworkPolicyManagerServiceTest { history.recordData(start, end, new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0)); stats.clear(); - stats.addValues(IFACE_ALL, UID_A, SET_ALL, TAG_ALL, + stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL, DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0); - stats.addValues(IFACE_ALL, UID_B, SET_ALL, TAG_ALL, + stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL, DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0); - stats.addValues(IFACE_ALL, UID_C, SET_ALL, TAG_ALL, + stats.addEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL, DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0); reset(mNotifManager); @@ -1301,9 +1301,9 @@ public class NetworkPolicyManagerServiceTest { history.recordData(start, end, new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0)); stats.clear(); - stats.addValues(IFACE_ALL, UID_A, SET_ALL, TAG_ALL, + stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL, DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0); - stats.addValues(IFACE_ALL, UID_B, SET_ALL, TAG_ALL, + stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL, DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0); reset(mNotifManager); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java index c478ec472e61..15327b6e5463 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java @@ -156,8 +156,7 @@ public class PackageInstallerSessionTest { if (isMultiPackage) { params.isMultiPackage = true; } - InstallSource installSource = InstallSource.create("testInstaller", null, "testInstaller", - false); + InstallSource installSource = InstallSource.create("testInstaller", null, "testInstaller"); return new PackageInstallerSession( /* callback */ null, /* context */null, diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java new file mode 100644 index 000000000000..7666ab9ed5ae --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.power; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.hardware.SensorManager; +import android.hardware.display.AmbientDisplayConfiguration; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.Looper; +import android.os.ServiceManager; +import android.os.Vibrator; +import android.os.test.TestLooper; +import android.provider.Settings; +import android.testing.TestableContext; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.app.IBatteryStats; +import com.android.internal.os.BatteryStatsImpl; +import com.android.server.LocalServices; +import com.android.server.policy.WindowManagerPolicy; +import com.android.server.power.batterysaver.BatterySaverPolicy; +import com.android.server.power.batterysaver.BatterySavingStats; +import com.android.server.statusbar.StatusBarManagerInternal; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.power.Notifier} + */ +public class NotifierTest { + private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; + private static final int USER_ID = 0; + + @Mock private BatterySaverPolicy mBatterySaverPolicyMock; + @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; + @Mock private Notifier mNotifierMock; + @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; + @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; + @Mock private SystemPropertiesWrapper mSystemPropertiesMock; + @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; + @Mock private BatteryStatsImpl mBatteryStats; + @Mock private Vibrator mVibrator; + @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + + private PowerManagerService mService; + private Context mContextSpy; + private Resources mResourcesSpy; + private TestLooper mTestLooper = new TestLooper(); + private Notifier mNotifier; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + LocalServices.removeServiceForTest(StatusBarManagerInternal.class); + LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal); + + mContextSpy = spy(new TestableContext(InstrumentationRegistry.getContext())); + mResourcesSpy = spy(mContextSpy.getResources()); + when(mContextSpy.getResources()).thenReturn(mResourcesSpy); + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); + when(mContextSpy.getSystemService(Vibrator.class)).thenReturn(mVibrator); + + mService = new PowerManagerService(mContextSpy, mInjector); + } + + @Test + public void testVibrateEnabled_wiredCharging() { + createNotifier(); + + // GIVEN the charging vibration is enabled + enableChargingVibration(true); + + // WHEN wired charging starts + mNotifier.onWiredChargingStarted(USER_ID); + mTestLooper.dispatchAll(); + + // THEN the device vibrates once + verify(mVibrator, times(1)).vibrate(any(), any()); + } + + @Test + public void testVibrateDisabled_wiredCharging() { + createNotifier(); + + // GIVEN the charging vibration is disabled + enableChargingVibration(false); + + // WHEN wired charging starts + mNotifier.onWiredChargingStarted(USER_ID); + mTestLooper.dispatchAll(); + + // THEN the device doesn't vibrate + verify(mVibrator, never()).vibrate(any(), any()); + } + + @Test + public void testVibrateEnabled_wirelessCharging() { + createNotifier(); + + // GIVEN the charging vibration is enabled + enableChargingVibration(true); + + // WHEN wireless charging starts + mNotifier.onWirelessChargingStarted(5, USER_ID); + mTestLooper.dispatchAll(); + + // THEN the device vibrates once + verify(mVibrator, times(1)).vibrate(any(), any()); + } + + @Test + public void testVibrateDisabled_wirelessCharging() { + createNotifier(); + + // GIVEN the charging vibration is disabeld + enableChargingVibration(false); + + // WHEN wireless charging starts + mNotifier.onWirelessChargingStarted(5, USER_ID); + mTestLooper.dispatchAll(); + + // THEN the device doesn't vibrate + verify(mVibrator, never()).vibrate(any(), any()); + } + + @Test + public void testVibrateEnabled_dndOn() { + createNotifier(); + + // GIVEN the charging vibration is enabled but dnd is on + enableChargingVibration(true); + enableChargingFeedback( + /* chargingFeedbackEnabled */ true, + /* dndOn */ true); + + // WHEN wired charging starts + mNotifier.onWiredChargingStarted(USER_ID); + mTestLooper.dispatchAll(); + + // THEN the device doesn't vibrate + verify(mVibrator, never()).vibrate(any(), any()); + } + + @Test + public void testWirelessAnimationEnabled() { + // GIVEN the wireless charging animation is enabled + when(mResourcesSpy.getBoolean( + com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim)) + .thenReturn(true); + createNotifier(); + + // WHEN wireless charging starts + mNotifier.onWirelessChargingStarted(5, USER_ID); + mTestLooper.dispatchAll(); + + // THEN the charging animation is triggered + verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5); + } + + @Test + public void testWirelessAnimationDisabled() { + // GIVEN the wireless charging animation is disabled + when(mResourcesSpy.getBoolean( + com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim)) + .thenReturn(false); + createNotifier(); + + // WHEN wireless charging starts + mNotifier.onWirelessChargingStarted(5, USER_ID); + mTestLooper.dispatchAll(); + + // THEN the charging animation never gets called + verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt()); + } + + private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() { + @Override + Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, + SuspendBlocker suspendBlocker, WindowManagerPolicy policy) { + return mNotifierMock; + } + + @Override + SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { + return super.createSuspendBlocker(service, name); + } + + @Override + BatterySaverPolicy createBatterySaverPolicy( + Object lock, Context context, BatterySavingStats batterySavingStats) { + return mBatterySaverPolicyMock; + } + + @Override + PowerManagerService.NativeWrapper createNativeWrapper() { + return mNativeWrapperMock; + } + + @Override + WirelessChargerDetector createWirelessChargerDetector( + SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) { + return mWirelessChargerDetectorMock; + } + + @Override + AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) { + return mAmbientDisplayConfigurationMock; + } + + @Override + InattentiveSleepWarningController createInattentiveSleepWarningController() { + return mInattentiveSleepWarningControllerMock; + } + + @Override + public SystemPropertiesWrapper createSystemPropertiesWrapper() { + return mSystemPropertiesMock; + } + }; + + private void enableChargingFeedback(boolean chargingFeedbackEnabled, boolean dndOn) { + // enable/disable charging feedback + Settings.Secure.putIntForUser( + mContextSpy.getContentResolver(), + Settings.Secure.CHARGING_SOUNDS_ENABLED, + chargingFeedbackEnabled ? 1 : 0, + USER_ID); + + // toggle on/off dnd + Settings.Global.putInt( + mContextSpy.getContentResolver(), + Settings.Global.ZEN_MODE, + dndOn ? Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS + : Settings.Global.ZEN_MODE_OFF); + } + + private void enableChargingVibration(boolean enable) { + enableChargingFeedback(true, false); + + Settings.Secure.putIntForUser( + mContextSpy.getContentResolver(), + Settings.Secure.CHARGING_VIBRATION_ENABLED, + enable ? 1 : 0, + USER_ID); + } + + private void createNotifier() { + mNotifier = new Notifier( + mTestLooper.getLooper(), + mContextSpy, + IBatteryStats.Stub.asInterface(ServiceManager.getService( + BatteryStats.SERVICE_NAME)), + mInjector.createSuspendBlocker(mService, "testBlocker"), + null); + } +} diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 71b568cc06c5..ae5369204428 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -37,7 +37,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import androidx.test.runner.AndroidJUnit4; diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index ca6fd08092df..aaf9799de777 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -30,7 +30,7 @@ import android.content.Intent; import android.icu.util.Calendar; import android.icu.util.GregorianCalendar; import android.icu.util.TimeZone; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import androidx.test.runner.AndroidJUnit4; diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java index 239d413c12d2..f1e9191ddb4f 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java @@ -18,7 +18,7 @@ package com.android.server.timedetector; import static org.junit.Assert.assertEquals; -import android.util.TimestampedValue; +import android.os.TimestampedValue; import androidx.test.runner.AndroidJUnit4; diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 12ba219d0365..1dd7e64690c7 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -365,8 +365,9 @@ public class AppStandbyControllerTests { public void testSetAppStandbyBucket() throws Exception { // For a known package, standby bucket should be set properly reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + mInjector.mElapsedRealtime = HOUR_MS; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, - REASON_MAIN_TIMEOUT, HOUR_MS); + REASON_MAIN_TIMEOUT); assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); // For an unknown package, standby bucket should not be set, hence NEVER is returned @@ -374,7 +375,7 @@ public class AppStandbyControllerTests { mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID); isPackageInstalled = false; // Mock package is not installed mController.setAppStandbyBucket(PACKAGE_UNKNOWN, USER_ID, STANDBY_BUCKET_ACTIVE, - REASON_MAIN_TIMEOUT, HOUR_MS); + REASON_MAIN_TIMEOUT); isPackageInstalled = true; // Reset mocked variable for other tests assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN)); } @@ -468,12 +469,13 @@ public class AppStandbyControllerTests { @Test public void testPredictionTimedout() throws Exception { // Set it to timeout or usage, so that prediction can override it + mInjector.mElapsedRealtime = HOUR_MS; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, - REASON_MAIN_TIMEOUT, HOUR_MS); + REASON_MAIN_TIMEOUT); assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, - REASON_MAIN_PREDICTED, HOUR_MS); + REASON_MAIN_PREDICTED); assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); // Fast forward 12 hours @@ -497,29 +499,31 @@ public class AppStandbyControllerTests { @Test public void testOverrides() throws Exception { // Can force to NEVER + mInjector.mElapsedRealtime = HOUR_MS; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, - REASON_MAIN_FORCED, 1 * HOUR_MS); + REASON_MAIN_FORCED); assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1)); // Prediction can't override FORCED reason mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, - REASON_MAIN_FORCED, 1 * HOUR_MS); + REASON_MAIN_FORCED); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, - REASON_MAIN_PREDICTED, 1 * HOUR_MS); + REASON_MAIN_PREDICTED); assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1)); // Prediction can't override NEVER + mInjector.mElapsedRealtime = 2 * HOUR_MS; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, - REASON_MAIN_DEFAULT, 2 * HOUR_MS); + REASON_MAIN_DEFAULT); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, - REASON_MAIN_PREDICTED, 2 * HOUR_MS); + REASON_MAIN_PREDICTED); assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1)); // Prediction can't set to NEVER mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, - REASON_MAIN_USAGE, 2 * HOUR_MS); + REASON_MAIN_USAGE); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, - REASON_MAIN_PREDICTED, 2 * HOUR_MS); + REASON_MAIN_PREDICTED); assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); } @@ -530,7 +534,7 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime = 2000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, - REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_ACTIVE); // bucketing works after timeout @@ -554,15 +558,17 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_ACTIVE); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, - REASON_MAIN_PREDICTED, 1000); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_ACTIVE); + mInjector.mElapsedRealtime = 2000 + mController.mStrongUsageTimeoutMillis; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, - REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_WORKING_SET); + mInjector.mElapsedRealtime = 2000 + mController.mNotificationSeenTimeoutMillis; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, - REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_FREQUENT); } @@ -582,18 +588,18 @@ public class AppStandbyControllerTests { // Still in ACTIVE after first USER_INTERACTION times out mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, - REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_ACTIVE); // Both timed out, so NOTIFICATION_SEEN timeout should be effective mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, - REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_WORKING_SET); mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, - REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_RARE); } @@ -625,7 +631,7 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100; // Make sure app is in NEVER bucket mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, - REASON_MAIN_FORCED, mInjector.mElapsedRealtime); + REASON_MAIN_FORCED); mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_NEVER); @@ -670,7 +676,7 @@ public class AppStandbyControllerTests { // Predict to ACTIVE mInjector.mElapsedRealtime += 1000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, - REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_ACTIVE); // CheckIdleStates should not change the prediction @@ -687,7 +693,7 @@ public class AppStandbyControllerTests { // Predict to FREQUENT mInjector.mElapsedRealtime = RARE_THRESHOLD; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, - REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime); + REASON_MAIN_PREDICTED); assertBucket(STANDBY_BUCKET_FREQUENT); // Add a short timeout event 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 73b2f6b2dc37..a3e94599cad3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -521,11 +521,12 @@ public class ActivityRecordTests extends ActivityTestsBase { } @Test - public void testShouldPauseWhenMakeClientVisible() { + public void testShouldStartWhenMakeClientActive() { ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build(); topActivity.setOccludesParent(false); mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); - mActivity.makeClientVisible(); + mActivity.setVisibility(true); + mActivity.makeActiveIfNeeded(null /* activeActivity */); assertEquals(STARTED, mActivity.getState()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java index 574517a00f6f..71390dbbe4a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java @@ -40,7 +40,7 @@ import org.mockito.MockitoAnnotations; * Tests for the {@link ActivityStack} class. * * Build/Install/Run: - * atest FrameworksServicesTests:AnimatingActivityRegistryTest + * atest WmTests:AnimatingActivityRegistryTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 1311889d5605..f6213bd94ddd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -42,7 +42,7 @@ import org.junit.runner.RunWith; /** * Build/Install/Run: - * atest FrameworksServicesTests:AppTransitionControllerTest + * atest WmTests:AppTransitionControllerTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java index 0ad0f95f64c1..1dda535cfb95 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -60,7 +60,7 @@ import org.junit.runner.RunWith; * appropriately. * * Build/Install/Run: - * atest FrameworksServicesTests:BoundsAnimationControllerTests + * atest WmTests:BoundsAnimationControllerTests */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index 0aa6961d20b3..e8f7849ef96a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -40,7 +40,7 @@ import org.junit.runner.RunWith; /** * Build/Install/Run: - * atest FrameworksServicesTests:DimmerTests + * atest WmTests:DimmerTests */ @Presubmit @RunWith(WindowTestRunner.class) diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 73dd2df27e95..f2ba97c269d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -57,6 +57,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; +import static com.google.common.truth.Truth.assertThat; + import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -86,6 +88,7 @@ import android.view.IWindowManager; import android.view.MotionEvent; import android.view.Surface; import android.view.ViewRootImpl; +import android.view.WindowManager; import android.view.test.InsetsModeSession; import androidx.test.filters.SmallTest; @@ -561,8 +564,7 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); - dc.setLayoutNeeded(); - dc.performLayout(true /* initial */, false /* updateImeWindows */); + performLayout(dc); assertThat(win.mLayoutSeq, is(dc.mLayoutSeq)); } @@ -829,8 +831,7 @@ public class DisplayContentTests extends WindowTestsBase { win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40))); - dc.setLayoutNeeded(); - dc.performLayout(true /* initial */, false /* updateImeWindows */); + performLayout(dc); win.setHasSurface(true); dc.updateSystemGestureExclusion(); @@ -866,8 +867,7 @@ public class DisplayContentTests extends WindowTestsBase { win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50))); - dc.setLayoutNeeded(); - dc.performLayout(true /* initial */, false /* updateImeWindows */); + performLayout(dc); win.setHasSurface(true); win2.setHasSurface(true); @@ -898,8 +898,7 @@ public class DisplayContentTests extends WindowTestsBase { win2.getAttrs().height = 10; win2.setSystemGestureExclusion(Collections.emptyList()); - dc.setLayoutNeeded(); - dc.performLayout(true /* initial */, false /* updateImeWindows */); + performLayout(dc); win.setHasSurface(true); win2.setHasSurface(true); @@ -922,8 +921,7 @@ public class DisplayContentTests extends WindowTestsBase { | SYSTEM_UI_FLAG_IMMERSIVE_STICKY; win.mActivityRecord.mTargetSdk = P; - dc.setLayoutNeeded(); - dc.performLayout(true /* initial */, false /* updateImeWindows */); + performLayout(dc); win.setHasSurface(true); @@ -935,6 +933,22 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testRequestResizeForEmptyFrames() { + final WindowState win = mChildAppWindowAbove; + makeWindowVisible(win, win.getParentWindow()); + win.setRequestedSize(mDisplayContent.mBaseDisplayWidth, 0 /* height */); + win.mAttrs.width = win.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT; + win.mAttrs.gravity = Gravity.CENTER; + performLayout(mDisplayContent); + + // The frame is empty because the requested height is zero. + assertTrue(win.getFrameLw().isEmpty()); + // The window should be scheduled to resize then the client may report a new non-empty size. + win.updateResizingWindowIfNeeded(); + assertThat(mWm.mResizingWindows).contains(win); + } + + @Test public void testOrientationChangeLogging() { MetricsLogger mockLogger = mock(MetricsLogger.class); Configuration oldConfig = new Configuration(); @@ -1011,6 +1025,11 @@ public class DisplayContentTests extends WindowTestsBase { mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */); } + private void performLayout(DisplayContent dc) { + dc.setLayoutNeeded(); + dc.performLayout(true /* initial */, false /* updateImeWindows */); + } + /** * Create DisplayContent that does not update display base/initial values from device to keep * the values set by test. diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 112479b3b9a0..1a575962b961 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -61,7 +61,7 @@ import org.mockito.MockitoAnnotations; /** * Build/Install/Run: - * atest FrameworksServicesTests:RemoteAnimationControllerTest + * atest WmTests:RemoteAnimationControllerTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java index e0112809b22b..bac2bcae30de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -60,7 +60,7 @@ import java.util.concurrent.CountDownLatch; * Test class for {@link SurfaceAnimationRunner}. * * Build/Install/Run: - * atest FrameworksServicesTests:SurfaceAnimationRunnerTest + * atest WmTests:SurfaceAnimationRunnerTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java index 0274b7d788ff..e71247173930 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java @@ -38,7 +38,7 @@ import org.mockito.MockitoAnnotations; * Test class for {@link TaskSnapshotCache}. * * Build/Install/Run: - * atest FrameworksServicesTests:TaskSnapshotCacheTest + * atest WmTests:TaskSnapshotCacheTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index 8fe0cdbbd872..2e485dd1bec3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -53,7 +53,7 @@ import org.mockito.Mockito; * Test class for {@link TaskSnapshotController}. * * Build/Install/Run: - * * atest FrameworksServicesTests:TaskSnapshotControllerTest + * * atest WmTests:TaskSnapshotControllerTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index b5a5790a3a8c..eb8eb98fd484 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -47,7 +47,7 @@ import java.util.function.Predicate; * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader} * * Build/Install/Run: - * atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest + * atest WmTests:TaskSnapshotPersisterLoaderTest */ @MediumTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index 2d418ffffcf6..ed87f3a0c604 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -53,7 +53,7 @@ import org.junit.runner.RunWith; * Test class for {@link TaskSnapshotSurface}. * * Build/Install/Run: - * atest FrameworksServicesTests:TaskSnapshotSurfaceTest + * atest WmTests:TaskSnapshotSurfaceTest */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java index 8936aadd5f92..3c0dd1e897f5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java @@ -39,7 +39,7 @@ import java.util.function.Consumer; * Tests for {@link WindowContainer#forAllWindows} and various implementations. * * Build/Install/Run: - * atest FrameworksServicesTests:WindowContainerTraversalTests + * atest WmTests:WindowContainerTraversalTests */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index e081ca374808..7e248f854dcf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -92,7 +92,7 @@ import java.util.List; * Tests for the {@link WindowState} class. * * Build/Install/Run: - * atest FrameworksServicesTests:WindowStateTests + * atest WmTests:WindowStateTests */ @SmallTest @Presubmit diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index ea8831571639..31a7f2437536 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -44,6 +44,7 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.IWindow; import android.view.SurfaceControl.Transaction; +import android.view.View; import android.view.WindowManager; import com.android.server.AttributeCache; @@ -312,6 +313,16 @@ class WindowTestsBase extends SystemServiceTestsBase { } } + static void makeWindowVisible(WindowState... windows) { + for (WindowState win : windows) { + win.mViewVisibility = View.VISIBLE; + win.mRelayoutCalled = true; + win.mHasSurface = true; + win.mHidden = false; + win.showLw(false /* doAnimation */, false /* requestAnim */); + } + } + /** Creates a {@link ActivityStack} and adds it to the specified {@link DisplayContent}. */ ActivityStack createTaskStackOnDisplay(DisplayContent dc) { return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc); diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java new file mode 100644 index 000000000000..9cda08458640 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java @@ -0,0 +1,153 @@ +/* + * 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.wm.utils; + +import static android.graphics.Bitmap.Config.ARGB_8888; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; +import android.graphics.Matrix; +import android.graphics.PointF; +import android.view.Surface; + +import org.junit.Before; +import org.junit.Test; + +public class RotationAnimationUtilsTest { + + private static final int BITMAP_HEIGHT = 100; + private static final int BITMAP_WIDTH = 100; + private static final int POINT_WIDTH = 1000; + private static final int POINT_HEIGHT = 2000; + + private ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.DISPLAY_P3); + private Matrix mMatrix; + + @Before + public void setup() { + mMatrix = new Matrix(); + } + + @Test + public void blackLuma() { + Bitmap swBitmap = createBitmap(0); + GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); + float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + assertEquals(0, borderLuma, 0); + } + + @Test + public void whiteLuma() { + Bitmap swBitmap = createBitmap(1); + GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); + float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + assertEquals(1, borderLuma, 0); + } + + @Test + public void whiteImageBlackBorderLuma() { + Bitmap swBitmap = createBitmap(1); + setBorderLuma(swBitmap, 0); + GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); + float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + assertEquals(0, borderLuma, 0); + } + + @Test + public void blackImageWhiteBorderLuma() { + Bitmap swBitmap = createBitmap(0); + setBorderLuma(swBitmap, 1); + GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); + float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + assertEquals(1, borderLuma, 0); + } + + @Test + public void rotate_0_bottomRight() { + RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_0, + POINT_WIDTH, POINT_HEIGHT, mMatrix); + PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT); + assertEquals(POINT_WIDTH, newPoints.x, 0); + assertEquals(POINT_HEIGHT, newPoints.y, 0); + } + + @Test + public void rotate_90_bottomRight() { + RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_90, + POINT_WIDTH, POINT_HEIGHT, mMatrix); + PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT); + assertEquals(0, newPoints.x, 0); + assertEquals(POINT_WIDTH, newPoints.y, 0); + } + + @Test + public void rotate_180_bottomRight() { + RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_180, + POINT_WIDTH, POINT_HEIGHT, mMatrix); + PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT); + assertEquals(0, newPoints.x, 0); + assertEquals(0, newPoints.y, 0); + } + + @Test + public void rotate_270_bottomRight() { + RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_270, + POINT_WIDTH, POINT_HEIGHT, mMatrix); + PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT); + assertEquals(POINT_HEIGHT, newPoints.x, 0); + assertEquals(0, newPoints.y, 0); + } + + private PointF checkMappedPoints(int x, int y) { + final float[] fs = new float[] {x, y}; + mMatrix.mapPoints(fs); + return new PointF(fs[0], fs[1]); + } + + private Bitmap createBitmap(float luma) { + Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, ARGB_8888); + for (int i = 0; i < BITMAP_WIDTH; i++) { + for (int j = 0; j < BITMAP_HEIGHT; j++) { + bitmap.setPixel(i, j, Color.argb(1, luma, luma, luma)); + } + } + return bitmap; + } + + private GraphicBuffer swBitmapToGraphicsBuffer(Bitmap swBitmap) { + Bitmap hwBitmap = swBitmap.copy(Bitmap.Config.HARDWARE, false); + return hwBitmap.createGraphicBufferHandle(); + } + + private void setBorderLuma(Bitmap swBitmap, float luma) { + int i; + int width = swBitmap.getWidth(); + int height = swBitmap.getHeight(); + for (i = 0; i < width; i++) { + swBitmap.setPixel(i, 0, Color.argb(1, luma, luma, luma)); + swBitmap.setPixel(i, height - 1, Color.argb(1, luma, luma, luma)); + } + for (i = 0; i < height; i++) { + swBitmap.setPixel(0, i, Color.argb(1, luma, luma, luma)); + swBitmap.setPixel(width - 1, i, Color.argb(1, luma, luma, luma)); + } + } +} diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index c900f386b438..8397aa485595 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1568,44 +1568,16 @@ public class UsageStatsService extends SystemService implements } @Override - public void setAppStandbyBucket(String packageName, - int bucket, int userId) { + public void setAppStandbyBucket(String packageName, int bucket, int userId) { getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE, "No permission to change app standby state"); - if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE - || bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) { - throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket); - } final int callingUid = Binder.getCallingUid(); - try { - userId = ActivityManager.getService().handleIncomingUser( - Binder.getCallingPid(), callingUid, userId, false, true, - "setAppStandbyBucket", null); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID; - final boolean systemCaller = UserHandle.isCore(callingUid); - final int reason = systemCaller - ? UsageStatsManager.REASON_MAIN_FORCED - : UsageStatsManager.REASON_MAIN_PREDICTED; + final int callingPid = Binder.getCallingPid(); final long token = Binder.clearCallingIdentity(); try { - final int packageUid = mPackageManagerInternal.getPackageUid(packageName, - PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE, userId); - // Caller cannot set their own standby state - if (packageUid == callingUid) { - throw new IllegalArgumentException("Cannot set your own standby bucket"); - } - if (packageUid < 0) { - throw new IllegalArgumentException( - "Cannot set standby bucket for non existent package (" + packageName - + ")"); - } - mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason, - SystemClock.elapsedRealtime(), shellCaller); + mAppStandby.setAppStandbyBucket(packageName, bucket, userId, + callingUid, callingPid); } finally { Binder.restoreCallingIdentity(token); } @@ -1643,37 +1615,11 @@ public class UsageStatsService extends SystemService implements "No permission to change app standby state"); final int callingUid = Binder.getCallingUid(); - try { - userId = ActivityManager.getService().handleIncomingUser( - Binder.getCallingPid(), callingUid, userId, false, true, - "setAppStandbyBucket", null); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID; - final int reason = shellCaller - ? UsageStatsManager.REASON_MAIN_FORCED - : UsageStatsManager.REASON_MAIN_PREDICTED; + final int callingPid = Binder.getCallingPid(); final long token = Binder.clearCallingIdentity(); try { - final long elapsedRealtime = SystemClock.elapsedRealtime(); - List<AppStandbyInfo> bucketList = appBuckets.getList(); - for (AppStandbyInfo bucketInfo : bucketList) { - final String packageName = bucketInfo.mPackageName; - final int bucket = bucketInfo.mStandbyBucket; - if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE - || bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) { - throw new IllegalArgumentException( - "Cannot set the standby bucket to " + bucket); - } - // Caller cannot set their own standby state - if (mPackageManagerInternal.getPackageUid(packageName, - PackageManager.MATCH_ANY_USER, userId) == callingUid) { - throw new IllegalArgumentException("Cannot set your own standby bucket"); - } - mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason, - elapsedRealtime, shellCaller); - } + mAppStandby.setAppStandbyBuckets(appBuckets.getList(), userId, + callingUid, callingPid); } finally { Binder.restoreCallingIdentity(token); } diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java index b75510b576f6..488ee78f6230 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java +++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java @@ -83,7 +83,7 @@ import com.android.server.wm.ActivityMetricsLaunchObserver; * could transition to INTENT_STARTED. * * <p> If any bad transition happened, the state becomse UNKNOWN. The UNKNOWN state - * could be * accumulated, because during the UNKNOWN state more IntentStarted may + * could be accumulated, because during the UNKNOWN state more IntentStarted may * be triggered. To recover from UNKNOWN to INIT, all the accumualted IntentStarted * should termniate. * @@ -100,7 +100,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onIntentStarted(@NonNull Intent intent, long timestampNs) { if (state == State.UNKNOWN) { - Log.e(TAG, "IntentStarted during UNKNOWN." + intent); + Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent); incAccIntentStartedEvents(); return; } @@ -110,32 +110,32 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { state != State.ACTIVITY_CANCELLED && state != State.ACTIVITY_FINISHED && state != State.REPORT_FULLY_DRAWN) { - Log.e(TAG, + Log.wtf(TAG, String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED)); incAccIntentStartedEvents(); incAccIntentStartedEvents(); return; } - Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_STARTED)); + Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED)); state = State.INTENT_STARTED; } @Override public void onIntentFailed() { if (state == State.UNKNOWN) { - Log.e(TAG, "IntentFailed during UNKNOWN."); + Log.wtf(TAG, "IntentFailed during UNKNOWN."); decAccIntentStartedEvents(); return; } if (state != State.INTENT_STARTED) { - Log.e(TAG, + Log.wtf(TAG, String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED)); incAccIntentStartedEvents(); return; } - Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_FAILED)); + Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED)); state = State.INTENT_FAILED; } @@ -143,11 +143,11 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, @Temperature int temperature) { if (state == State.UNKNOWN) { - Log.e(TAG, "onActivityLaunched during UNKNOWN."); + Log.wtf(TAG, "onActivityLaunched during UNKNOWN."); return; } if (state != State.INTENT_STARTED) { - Log.e(TAG, + Log.wtf(TAG, String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED)); incAccIntentStartedEvents(); return; @@ -160,12 +160,12 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) { if (state == State.UNKNOWN) { - Log.e(TAG, "onActivityLaunchCancelled during UNKNOWN."); + Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN."); decAccIntentStartedEvents(); return; } if (state != State.ACTIVITY_LAUNCHED) { - Log.e(TAG, + Log.wtf(TAG, String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED)); incAccIntentStartedEvents(); return; @@ -179,13 +179,13 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity, long timestampNs) { if (state == State.UNKNOWN) { - Log.e(TAG, "onActivityLaunchFinished during UNKNOWN."); + Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN."); decAccIntentStartedEvents(); return; } if (state != State.ACTIVITY_LAUNCHED) { - Log.e(TAG, + Log.wtf(TAG, String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED)); incAccIntentStartedEvents(); return; @@ -199,7 +199,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity, long timestampNs) { if (state == State.UNKNOWN) { - Log.e(TAG, "onReportFullyDrawn during UNKNOWN."); + Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN."); return; } if (state == State.INIT) { @@ -207,7 +207,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { } if (state != State.ACTIVITY_FINISHED) { - Log.e(TAG, + Log.wtf(TAG, String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN)); return; } @@ -230,7 +230,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { private void incAccIntentStartedEvents() { if (accIntentStartedEvents < 0) { throw new AssertionError( - String.format("The number of unknows cannot be negative")); + String.format("The number of unknowns cannot be negative")); } if (accIntentStartedEvents == 0) { state = State.UNKNOWN; @@ -243,7 +243,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { private void decAccIntentStartedEvents() { if (accIntentStartedEvents <= 0) { throw new AssertionError( - String.format("The number of unknows cannot be negative")); + String.format("The number of unknowns cannot be negative")); } if(accIntentStartedEvents == 1) { state = State.INIT; diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java index 8b8c86be7b0a..ea641f866b98 100644 --- a/telecomm/java/android/telecom/AudioState.java +++ b/telecomm/java/android/telecom/AudioState.java @@ -19,7 +19,7 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 86ad795b9ea2..a8852a849604 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -20,12 +20,11 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.os.ParcelFileDescriptor; import com.android.internal.telecom.IVideoProvider; diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java index a5d25e2ce4bb..fb6f99405759 100644 --- a/telecomm/java/android/telecom/CallerInfo.java +++ b/telecomm/java/android/telecom/CallerInfo.java @@ -17,7 +17,7 @@ package android.telecom; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -41,8 +41,8 @@ import com.android.i18n.phonenumbers.NumberParseException; import com.android.i18n.phonenumbers.PhoneNumberUtil; import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder; - import com.android.internal.annotations.VisibleForTesting; + import java.util.Locale; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 8808339b1664..f205ec64f49b 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -21,9 +21,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; import android.app.Notification; import android.bluetooth.BluetoothDevice; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.hardware.camera2.CameraManager; import android.net.Uri; diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java index 7d4ee7686512..4f6a9d6450f8 100644 --- a/telecomm/java/android/telecom/Log.java +++ b/telecomm/java/android/telecom/Log.java @@ -16,7 +16,7 @@ package android.telecom; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.net.Uri; import android.os.Build; diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index a234bb0af8fa..be4e2f4c65e1 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -16,22 +16,21 @@ package android.telecom; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.telecom.Call.Details.CallDirection; +import com.android.internal.telecom.IVideoProvider; + import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.android.internal.telecom.IVideoProvider; - /** * Information about a call that is used between InCallService and Telecom. * @hide diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java index 61a639a1a235..a427ed612b31 100644 --- a/telecomm/java/android/telecom/Phone.java +++ b/telecomm/java/android/telecom/Phone.java @@ -17,8 +17,8 @@ package android.telecom; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothDevice; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; import android.util.ArrayMap; diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java index eb568e04ebf3..e1bcb5fbdf00 100644 --- a/telecomm/java/android/telecom/PhoneAccountHandle.java +++ b/telecomm/java/android/telecom/PhoneAccountHandle.java @@ -18,7 +18,7 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.os.Build; import android.os.Parcel; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index af3c55abf00c..c4c1e21e7c41 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -26,7 +26,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -49,6 +49,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -882,7 +883,8 @@ public class TelecomManager { */ public TelecomManager(Context context, ITelecomService telecomServiceImpl) { Context appContext = context.getApplicationContext(); - if (appContext != null) { + if (appContext != null && Objects.equals(context.getFeatureId(), + appContext.getFeatureId())) { mContext = appContext; } else { mContext = context; @@ -916,7 +918,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme, - mContext.getOpPackageName()); + mContext.getOpPackageName(), mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e); @@ -1113,7 +1115,8 @@ public class TelecomManager { public List<PhoneAccountHandle> getSelfManagedPhoneAccounts() { try { if (isServiceConnected()) { - return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName()); + return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(), + mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e); @@ -1138,8 +1141,8 @@ public class TelecomManager { boolean includeDisabledAccounts) { try { if (isServiceConnected()) { - return getTelecomService().getCallCapablePhoneAccounts( - includeDisabledAccounts, mContext.getOpPackageName()); + return getTelecomService().getCallCapablePhoneAccounts(includeDisabledAccounts, + mContext.getOpPackageName(), mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" + @@ -1442,7 +1445,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().isVoiceMailNumber(accountHandle, number, - mContext.getOpPackageName()); + mContext.getOpPackageName(), mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e); @@ -1464,7 +1467,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getVoiceMailNumber(accountHandle, - mContext.getOpPackageName()); + mContext.getOpPackageName(), mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e); @@ -1485,7 +1488,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getLine1Number(accountHandle, - mContext.getOpPackageName()); + mContext.getOpPackageName(), mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e); @@ -1506,7 +1509,8 @@ public class TelecomManager { public boolean isInCall() { try { if (isServiceConnected()) { - return getTelecomService().isInCall(mContext.getOpPackageName()); + return getTelecomService().isInCall(mContext.getOpPackageName(), + mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling isInCall().", e); @@ -1531,7 +1535,8 @@ public class TelecomManager { public boolean isInManagedCall() { try { if (isServiceConnected()) { - return getTelecomService().isInManagedCall(mContext.getOpPackageName()); + return getTelecomService().isInManagedCall(mContext.getOpPackageName(), + mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling isInManagedCall().", e); @@ -1711,7 +1716,8 @@ public class TelecomManager { public boolean isTtySupported() { try { if (isServiceConnected()) { - return getTelecomService().isTtySupported(mContext.getOpPackageName()); + return getTelecomService().isTtySupported(mContext.getOpPackageName(), + mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get TTY supported state.", e); @@ -1735,7 +1741,8 @@ public class TelecomManager { public @TtyMode int getCurrentTtyMode() { try { if (isServiceConnected()) { - return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName()); + return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName(), + mContext.getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e); @@ -1925,7 +1932,8 @@ public class TelecomManager { ITelecomService service = getTelecomService(); if (service != null) { try { - service.showInCallScreen(showDialpad, mContext.getOpPackageName()); + service.showInCallScreen(showDialpad, mContext.getOpPackageName(), + mContext.getFeatureId()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#showCallScreen", e); } @@ -1988,7 +1996,7 @@ public class TelecomManager { } try { service.placeCall(address, extras == null ? new Bundle() : extras, - mContext.getOpPackageName()); + mContext.getOpPackageName(), mContext.getFeatureId()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#placeCall", e); } diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java index 4a1aa0a8ffa4..109e7f829f2e 100644 --- a/telecomm/java/android/telecom/VideoCallImpl.java +++ b/telecomm/java/android/telecom/VideoCallImpl.java @@ -16,7 +16,7 @@ package android.telecom; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; import android.os.Handler; diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java index 64e6ca3416e3..4197f3cfc6c3 100644 --- a/telecomm/java/android/telecom/VideoProfile.java +++ b/telecomm/java/android/telecom/VideoProfile.java @@ -19,7 +19,6 @@ package android.telecom; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; -import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 204c37e9aa38..c54da6b4d527 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -35,12 +35,13 @@ interface ITelecomService { * * @param showDialpad if true, make the dialpad visible initially. */ - void showInCallScreen(boolean showDialpad, String callingPackage); + void showInCallScreen(boolean showDialpad, String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#getDefaultOutgoingPhoneAccount */ - PhoneAccountHandle getDefaultOutgoingPhoneAccount(in String uriScheme, String callingPackage); + PhoneAccountHandle getDefaultOutgoingPhoneAccount(in String uriScheme, String callingPackage, + String callingFeatureId); /** * @see TelecomServiceImpl#getUserSelectedOutgoingPhoneAccount @@ -56,12 +57,13 @@ interface ITelecomService { * @see TelecomServiceImpl#getCallCapablePhoneAccounts */ List<PhoneAccountHandle> getCallCapablePhoneAccounts( - boolean includeDisabledAccounts, String callingPackage); + boolean includeDisabledAccounts, String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#getSelfManagedPhoneAccounts */ - List<PhoneAccountHandle> getSelfManagedPhoneAccounts(String callingPackage); + List<PhoneAccountHandle> getSelfManagedPhoneAccounts(String callingPackage, + String callingFeatureId); /** * @see TelecomManager#getPhoneAccountsSupportingScheme @@ -123,17 +125,19 @@ interface ITelecomService { * @see TelecomServiceImpl#isVoiceMailNumber */ boolean isVoiceMailNumber(in PhoneAccountHandle accountHandle, String number, - String callingPackage); + String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#getVoiceMailNumber */ - String getVoiceMailNumber(in PhoneAccountHandle accountHandle, String callingPackage); + String getVoiceMailNumber(in PhoneAccountHandle accountHandle, String callingPackage, + String callingFeatureId); /** * @see TelecomServiceImpl#getLine1Number */ - String getLine1Number(in PhoneAccountHandle accountHandle, String callingPackage); + String getLine1Number(in PhoneAccountHandle accountHandle, String callingPackage, + String callingFeatureId); /** * @see TelecomServiceImpl#getDefaultPhoneApp @@ -172,12 +176,12 @@ interface ITelecomService { /** * @see TelecomServiceImpl#isInCall */ - boolean isInCall(String callingPackage); + boolean isInCall(String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#isInManagedCall */ - boolean isInManagedCall(String callingPackage); + boolean isInManagedCall(String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#isRinging @@ -229,12 +233,12 @@ interface ITelecomService { /** * @see TelecomServiceImpl#isTtySupported */ - boolean isTtySupported(String callingPackage); + boolean isTtySupported(String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#getCurrentTtyMode */ - int getCurrentTtyMode(String callingPackage); + int getCurrentTtyMode(String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#addNewIncomingCall @@ -249,7 +253,7 @@ interface ITelecomService { /** * @see TelecomServiceImpl#placeCall */ - void placeCall(in Uri handle, in Bundle extras, String callingPackage); + void placeCall(in Uri handle, in Bundle extras, String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#enablePhoneAccount diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java index f39981fdf25d..aaafee29e24a 100644 --- a/telephony/common/android/telephony/LocationAccessPolicy.java +++ b/telephony/common/android/telephony/LocationAccessPolicy.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index eb02ea6f5e40..368f8f1dab2e 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -25,7 +25,7 @@ import android.content.res.Resources; import android.os.RemoteException; import android.permission.IPermissionManager; import android.provider.Settings; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java index 5fb4e90b9666..a36ff9341275 100644 --- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java @@ -16,10 +16,10 @@ package com.android.internal.telephony; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.os.Build; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.text.TextUtils; import android.util.SparseIntArray; diff --git a/telephony/common/com/android/internal/telephony/HbpcdUtils.java b/telephony/common/com/android/internal/telephony/HbpcdUtils.java index 2f3194214be6..45a563c09394 100644 --- a/telephony/common/com/android/internal/telephony/HbpcdUtils.java +++ b/telephony/common/com/android/internal/telephony/HbpcdUtils.java @@ -19,7 +19,7 @@ package com.android.internal.telephony; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch; import com.android.internal.telephony.HbpcdLookup.MccIdd; diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java index b30258906368..afb9b6f52bdb 100644 --- a/telephony/common/com/android/internal/telephony/SmsApplication.java +++ b/telephony/common/com/android/internal/telephony/SmsApplication.java @@ -40,7 +40,7 @@ import android.os.UserHandle; import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.telephony.PackageChangeReceiver; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.TelephonyManager; import android.util.Log; diff --git a/telephony/common/com/android/internal/telephony/SmsConstants.java b/telephony/common/com/android/internal/telephony/SmsConstants.java index 19f52b0ef429..3aa8bbf607d1 100644 --- a/telephony/common/com/android/internal/telephony/SmsConstants.java +++ b/telephony/common/com/android/internal/telephony/SmsConstants.java @@ -15,7 +15,7 @@ */ package com.android.internal.telephony; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * SMS Constants and must be the same as the corresponding diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java index 06c08f56aa1f..06c37288a1a6 100644 --- a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java +++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java @@ -24,7 +24,7 @@ import android.os.PersistableBundle; import android.os.SystemProperties; import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.TelephonyManager; import android.text.TextUtils; diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 80a55b2a1147..4109ca6bd7d0 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -28,7 +28,7 @@ import android.os.Binder; import android.os.Build; import android.os.Process; import android.os.UserHandle; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java index 2abcc76fdccc..2abcc76fdccc 100644 --- a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java diff --git a/telephony/java/android/service/carrier/CarrierIdentifier.java b/telephony/java/android/service/carrier/CarrierIdentifier.java index af5bf7475f95..7957c258b54f 100644 --- a/telephony/java/android/service/carrier/CarrierIdentifier.java +++ b/telephony/java/android/service/carrier/CarrierIdentifier.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.TelephonyManager; import com.android.internal.telephony.uicc.IccUtils; diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java index 6c357ccdd03d..8450a9018634 100644 --- a/telephony/java/android/service/euicc/EuiccProfileInfo.java +++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java @@ -19,7 +19,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; diff --git a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java index c7a985160730..2382f657c9ee 100644 --- a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java +++ b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java @@ -17,7 +17,7 @@ package android.service.euicc; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; diff --git a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java index abd4065c754a..d0fb51180c1d 100644 --- a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java +++ b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java @@ -17,7 +17,7 @@ package android.service.euicc; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java index 9753d8be4065..097041f672ac 100644 --- a/telephony/java/android/telephony/AnomalyReporter.java +++ b/telephony/java/android/telephony/AnomalyReporter.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index fdf88497db02..6a622378dac7 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -24,12 +26,11 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.PersistableBundle; import android.os.RemoteException; -import android.os.ServiceManager; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; import android.telephony.ims.ImsReasonInfo; @@ -3124,7 +3125,6 @@ public class CarrierConfigManager { * EAP-AKA: "0" * EAP-SIM: "1" * EAP-AKA_PRIME: "6" - * @hide */ public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; @@ -3399,12 +3399,17 @@ public class CarrierConfigManager { /** Prefix of all Ims.KEY_* constants. */ public static final String KEY_PREFIX = "ims."; - //TODO: Add configs related to IMS. + /** + * Delay in milliseconds to turn off wifi when IMS is registered over wifi. + */ + public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT = + KEY_PREFIX + "wifi_off_deferring_time_int"; private Ims() {} private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); + defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_INT, 0); return defaults; } } @@ -4205,8 +4210,11 @@ public class CarrierConfigManager { /** @hide */ @Nullable private ICarrierConfigLoader getICarrierConfigLoader() { - return ICarrierConfigLoader.Stub - .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE)); + return ICarrierConfigLoader.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getCarrierConfigServiceRegisterer() + .get()); } /** diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java index 84be4e8b9ba4..719ba8d98773 100644 --- a/telephony/java/android/telephony/CbGeoUtils.java +++ b/telephony/java/android/telephony/CbGeoUtils.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.annotation.SystemApi; import android.text.TextUtils; diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java index ce32dce834db..8e703fee3126 100644 --- a/telephony/java/android/telephony/CellIdentity.java +++ b/telephony/java/android/telephony/CellIdentity.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -191,6 +193,7 @@ public abstract class CellIdentity implements Parcelable { * * @hide */ + @SystemApi public abstract @NonNull CellIdentity sanitizeLocationInfo(); @Override diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 2ecdfce92825..49f425acead6 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -18,7 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index 15c91752badf..bc4655069dba 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -18,7 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java index 2b1387c313ae..acb21f450243 100644 --- a/telephony/java/android/telephony/CellInfoCdma.java +++ b/telephony/java/android/telephony/CellInfoCdma.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java index 4f7c7a93d19b..79a9d44f36a2 100644 --- a/telephony/java/android/telephony/CellInfoGsm.java +++ b/telephony/java/android/telephony/CellInfoGsm.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java index 6d19261c8932..fed3ebf4af03 100644 --- a/telephony/java/android/telephony/CellInfoLte.java +++ b/telephony/java/android/telephony/CellInfoLte.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java index f1305f5ca768..58ff8c9558d9 100644 --- a/telephony/java/android/telephony/CellInfoTdscdma.java +++ b/telephony/java/android/telephony/CellInfoTdscdma.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java index ee5fec838d2d..33f6a555414c 100644 --- a/telephony/java/android/telephony/CellInfoWcdma.java +++ b/telephony/java/android/telephony/CellInfoWcdma.java @@ -18,7 +18,7 @@ package android.telephony; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import java.util.Objects; diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java index 64776e377fa4..11931996009c 100644 --- a/telephony/java/android/telephony/CellLocation.java +++ b/telephony/java/android/telephony/CellLocation.java @@ -19,7 +19,6 @@ package android.telephony; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.os.RemoteException; -import android.os.ServiceManager; import android.telephony.cdma.CdmaCellLocation; import android.telephony.gsm.GsmCellLocation; @@ -38,7 +37,11 @@ public abstract class CellLocation { */ public static void requestLocationUpdate() { try { - ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.getService("phone")); + ITelephony phone = ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); if (phone != null) { phone.updateServiceLocation(); } diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java index 199843905854..cab3b0cd3c47 100644 --- a/telephony/java/android/telephony/CellSignalStrengthCdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java @@ -20,7 +20,7 @@ import android.annotation.IntRange; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import java.util.Objects; diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index a9f3487a0880..28052aa93486 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntRange; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index a6ba9c279ae3..2ef2a52977ff 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntRange; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index f31fafe36508..4d67bcf536cf 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntDef; import android.annotation.IntRange; import android.os.Parcel; @@ -155,7 +157,17 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa * @param ss signal strength from modem. */ public CellSignalStrengthNr(android.hardware.radio.V1_4.NrSignalStrength ss) { - this(ss.csiRsrp, ss.csiRsrq, ss.csiSinr, ss.ssRsrp, ss.ssRsrq, ss.ssSinr); + this(flip(ss.csiRsrp), flip(ss.csiRsrq), ss.csiSinr, flip(ss.ssRsrp), flip(ss.ssRsrq), + ss.ssSinr); + } + + /** + * Flip sign cell strength value when taking in the value from hal + * @param val cell strength value + * @return flipped value + */ + private static int flip(int val) { + return val != CellInfo.UNAVAILABLE ? -val : val; } /** diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java index f4a3dbb37988..3bd9d5810136 100644 --- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntRange; import android.annotation.NonNull; import android.os.Parcel; diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java index 34b13858f104..535e9520074d 100644 --- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntRange; import android.annotation.StringDef; import android.compat.annotation.UnsupportedAppUsage; diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java index 202da6817cb5..a6dedf761636 100644 --- a/telephony/java/android/telephony/NetworkScan.java +++ b/telephony/java/android/telephony/NetworkScan.java @@ -16,10 +16,10 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntDef; -import android.content.Context; import android.os.RemoteException; -import android.os.ServiceManager; import com.android.internal.telephony.ITelephony; @@ -148,6 +148,9 @@ public class NetworkScan { private ITelephony getITelephony() { return ITelephony.Stub.asInterface( - ServiceManager.getService(Context.TELEPHONY_SERVICE)); + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); } } diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java index 8c5e10788b89..844289ce75d4 100644 --- a/telephony/java/android/telephony/NetworkService.java +++ b/telephony/java/android/telephony/NetworkService.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java index 89b96654451e..214ab41ae4f2 100644 --- a/telephony/java/android/telephony/NetworkServiceCallback.java +++ b/telephony/java/android/telephony/NetworkServiceCallback.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 6e86a4211834..2f9e6ac0f9ff 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 8139330cfc59..2c62d0667e19 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -545,7 +547,7 @@ public class ServiceState implements Parcelable { * @see #STATE_EMERGENCY_ONLY * @see #STATE_POWER_OFF * - * @return current data registration state {@link RegState} + * @return current data registration state * * @hide */ @@ -562,7 +564,7 @@ public class ServiceState implements Parcelable { * @see #STATE_EMERGENCY_ONLY * @see #STATE_POWER_OFF * - * @return current data registration state {@link RegState} + * @return current data registration state * * @hide */ @@ -2044,4 +2046,27 @@ public class ServiceState implements Parcelable { public boolean isIwlanPreferred() { return mIsIwlanPreferred; } + /** + * @return {@code true}Returns True whenever the modem is searching for service. + * To check both CS and PS domain + */ + + public boolean isSearching() { + NetworkRegistrationInfo psRegState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + + if (psRegState != null && psRegState.getRegistrationState() + == NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) { + return true; + } + + NetworkRegistrationInfo csRegState = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + + if (csRegState != null && csRegState.getRegistrationState() + == NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) { + return true; + } + return false; + } } diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 3350a3371504..1f7d55f2758a 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index fbe355e5c78b..db33be313941 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -35,7 +35,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; -import android.os.ServiceManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -745,7 +744,11 @@ public final class SmsManager { "Invalid pdu format. format must be either 3gpp or 3gpp2"); } try { - ISms iSms = ISms.Stub.asInterface(ServiceManager.getService("isms")); + ISms iSms = ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); if (iSms != null) { iSms.injectSmsPduForSubscriber( getSubscriptionId(), pdu, format, receivedIntent); @@ -1535,7 +1538,10 @@ public final class SmsManager { private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface( - ServiceManager.getService(Context.TELEPHONY_SERVICE)); + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); if (binder == null) { throw new RuntimeException("Could not find Telephony Service."); } @@ -1573,7 +1579,11 @@ public final class SmsManager { } private static ISms getISmsService() { - return ISms.Stub.asInterface(ServiceManager.getService("isms")); + return ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); } /** @@ -2007,7 +2017,11 @@ public final class SmsManager { public boolean isSMSPromptEnabled() { ISms iSms = null; try { - iSms = ISms.Stub.asInterface(ServiceManager.getService("isms")); + iSms = ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); return iSms.isSMSPromptEnabled(); } catch (RemoteException ex) { return false; @@ -2434,14 +2448,18 @@ public final class SmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is successfully sent, or failed * @throws IllegalArgumentException if contentUri is empty + * @deprecated use {@link MmsManager#sendMultimediaMessage} instead. */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } - MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri, - locationUrl, configOverrides, sentIntent); + MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); + if (m != null) { + m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides, + sentIntent); + } } /** @@ -2465,6 +2483,7 @@ public final class SmsManager { * @param downloadedIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty + * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead. */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { @@ -2474,8 +2493,11 @@ public final class SmsManager { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } - MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl, - contentUri, configOverrides, downloadedIntent); + MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); + if (m != null) { + m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri, + configOverrides, downloadedIntent); + } } // MMS send/download failure result codes @@ -2517,9 +2539,9 @@ public final class SmsManager { * </p> * * @return the bundle key/values pairs that contains MMS configuration values + * or an empty bundle if they cannot be found. */ - @Nullable - public Bundle getCarrierConfigValues() { + @NonNull public Bundle getCarrierConfigValues() { try { ISms iSms = getISmsService(); if (iSms != null) { @@ -2528,7 +2550,7 @@ public final class SmsManager { } catch (RemoteException ex) { // ignore it } - return null; + return new Bundle(); } /** diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index c0bc29d26c6c..c217b8b83c26 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; import android.annotation.Nullable; diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 2c0a1c94dc8d..c24eeb74f6cd 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 22eed6e026af..4510fede4e8e 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED; import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED; @@ -53,7 +55,6 @@ import android.os.Looper; import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.provider.Telephony.SimInfo; import android.telephony.euicc.EuiccManager; import android.telephony.ims.ImsMmTelManager; @@ -265,7 +266,7 @@ public class SubscriptionManager { * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id"; + public static final String UNIQUE_KEY_SUBSCRIPTION_ID = SimInfo.UNIQUE_KEY_SUBSCRIPTION_ID; /** * TelephonyProvider column name for a unique identifier for the subscription within the @@ -274,18 +275,18 @@ public class SubscriptionManager { * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String ICC_ID = "icc_id"; + public static final String ICC_ID = SimInfo.ICC_ID; /** * TelephonyProvider column name for user SIM_SlOT_INDEX * <P>Type: INTEGER (int)</P> */ /** @hide */ - public static final String SIM_SLOT_INDEX = "sim_id"; + public static final String SIM_SLOT_INDEX = SimInfo.SIM_SLOT_INDEX; /** SIM is not inserted */ /** @hide */ - public static final int SIM_NOT_INSERTED = -1; + public static final int SIM_NOT_INSERTED = SimInfo.SIM_NOT_INSERTED; /** * The slot-index for Bluetooth Remote-SIM subscriptions @@ -300,23 +301,7 @@ public class SubscriptionManager { * Default value is 0. */ /** @hide */ - public static final String SUBSCRIPTION_TYPE = "subscription_type"; - - /** - * TelephonyProvider column name white_listed_apn_data. - * It's a bitmask of APN types that will be allowed on this subscription even if it's metered - * and mobile data is turned off by the user. - * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS - * data connection to setup even if MMS is metered and mobile_data is turned off on that - * subscription. - * - * Default value is 0. - * - * @deprecated Replaced by {@link #DATA_ENABLED_OVERRIDE_RULES} - * @hide - */ - @Deprecated - public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data"; + public static final String SUBSCRIPTION_TYPE = SimInfo.SUBSCRIPTION_TYPE; /** * TelephonyProvider column name data_enabled_override_rules. @@ -329,7 +314,15 @@ public class SubscriptionManager { * * @hide */ - public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules"; + public static final String DATA_ENABLED_OVERRIDE_RULES = SimInfo.DATA_ENABLED_OVERRIDE_RULES; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SUBSCRIPTION_TYPE_"}, + value = { + SUBSCRIPTION_TYPE_LOCAL_SIM, + SUBSCRIPTION_TYPE_REMOTE_SIM}) + public @interface SubscriptionType {} /** * This constant is to designate a subscription as a Local-SIM Subscription. @@ -337,7 +330,7 @@ public class SubscriptionManager { * device. * </p> */ - public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; + public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM; /** * This constant is to designate a subscription as a Remote-SIM Subscription. @@ -363,29 +356,21 @@ public class SubscriptionManager { * was never seen before. * </p> */ - public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"SUBSCRIPTION_TYPE_"}, - value = { - SUBSCRIPTION_TYPE_LOCAL_SIM, - SUBSCRIPTION_TYPE_REMOTE_SIM}) - public @interface SubscriptionType {} + public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = SimInfo.SUBSCRIPTION_TYPE_REMOTE_SIM; /** * TelephonyProvider column name for user displayed name. * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String DISPLAY_NAME = "display_name"; + public static final String DISPLAY_NAME = SimInfo.DISPLAY_NAME; /** * TelephonyProvider column name for the service provider name for the SIM. * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String CARRIER_NAME = "carrier_name"; + public static final String CARRIER_NAME = SimInfo.CARRIER_NAME; /** * Default name resource @@ -399,44 +384,44 @@ public class SubscriptionManager { * * @hide */ - public static final String NAME_SOURCE = "name_source"; + public static final String NAME_SOURCE = SimInfo.NAME_SOURCE; /** * The name_source is the default, which is from the carrier id. * @hide */ - public static final int NAME_SOURCE_DEFAULT_SOURCE = 0; + public static final int NAME_SOURCE_DEFAULT = SimInfo.NAME_SOURCE_DEFAULT; /** * The name_source is from SIM EF_SPN. * @hide */ - public static final int NAME_SOURCE_SIM_SPN = 1; + public static final int NAME_SOURCE_SIM_SPN = SimInfo.NAME_SOURCE_SIM_SPN; /** * The name_source is from user input * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final int NAME_SOURCE_USER_INPUT = 2; + public static final int NAME_SOURCE_USER_INPUT = SimInfo.NAME_SOURCE_USER_INPUT; /** * The name_source is carrier (carrier app, carrier config, etc.) * @hide */ - public static final int NAME_SOURCE_CARRIER = 3; + public static final int NAME_SOURCE_CARRIER = SimInfo.NAME_SOURCE_CARRIER; /** * The name_source is from SIM EF_PNN. * @hide */ - public static final int NAME_SOURCE_SIM_PNN = 4; + public static final int NAME_SOURCE_SIM_PNN = SimInfo.NAME_SOURCE_SIM_PNN; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"NAME_SOURCE_"}, value = { - NAME_SOURCE_DEFAULT_SOURCE, + NAME_SOURCE_DEFAULT, NAME_SOURCE_SIM_SPN, NAME_SOURCE_USER_INPUT, NAME_SOURCE_CARRIER, @@ -449,67 +434,30 @@ public class SubscriptionManager { * <P>Type: INTEGER (int)</P> */ /** @hide */ - public static final String COLOR = "color"; - - /** @hide */ - public static final int COLOR_1 = 0; - - /** @hide */ - public static final int COLOR_2 = 1; - - /** @hide */ - public static final int COLOR_3 = 2; - - /** @hide */ - public static final int COLOR_4 = 3; - - /** @hide */ - public static final int COLOR_DEFAULT = COLOR_1; + public static final String COLOR = SimInfo.COLOR; /** * TelephonyProvider column name for the phone number of a SIM. * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String NUMBER = "number"; + public static final String NUMBER = SimInfo.NUMBER; /** - * TelephonyProvider column name for the number display format of a SIM. + * TelephonyProvider column name for whether data roaming is enabled. * <P>Type: INTEGER (int)</P> */ /** @hide */ - public static final String DISPLAY_NUMBER_FORMAT = "display_number_format"; - - /** @hide */ - public static final int DISPLAY_NUMBER_NONE = 0; - - /** @hide */ - public static final int DISPLAY_NUMBER_FIRST = 1; - - /** @hide */ - public static final int DISPLAY_NUMBER_LAST = 2; - - /** @hide */ - public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST; - - /** - * TelephonyProvider column name for permission for data roaming of a SIM. - * <P>Type: INTEGER (int)</P> - */ - /** @hide */ - public static final String DATA_ROAMING = "data_roaming"; + public static final String DATA_ROAMING = SimInfo.DATA_ROAMING; /** Indicates that data roaming is enabled for a subscription */ - public static final int DATA_ROAMING_ENABLE = 1; + public static final int DATA_ROAMING_ENABLE = SimInfo.DATA_ROAMING_ENABLE; /** Indicates that data roaming is disabled for a subscription */ - public static final int DATA_ROAMING_DISABLE = 0; - - /** @hide */ - public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE; + public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE; /** @hide */ - public static final int SIM_PROVISIONED = 0; + public static final int DATA_ROAMING_DEFAULT = SimInfo.DATA_ROAMING_DEFAULT; /** * TelephonyProvider column name for subscription carrier id. @@ -517,61 +465,61 @@ public class SubscriptionManager { * <p>Type: INTEGER (int) </p> * @hide */ - public static final String CARRIER_ID = "carrier_id"; + public static final String CARRIER_ID = SimInfo.CARRIER_ID; /** * @hide A comma-separated list of EHPLMNs associated with the subscription * <P>Type: TEXT (String)</P> */ - public static final String EHPLMNS = "ehplmns"; + public static final String EHPLMNS = SimInfo.EHPLMNS; /** * @hide A comma-separated list of HPLMNs associated with the subscription * <P>Type: TEXT (String)</P> */ - public static final String HPLMNS = "hplmns"; + public static final String HPLMNS = SimInfo.HPLMNS; /** * TelephonyProvider column name for the MCC associated with a SIM, stored as a string. * <P>Type: TEXT (String)</P> * @hide */ - public static final String MCC_STRING = "mcc_string"; + public static final String MCC_STRING = SimInfo.MCC_STRING; /** * TelephonyProvider column name for the MNC associated with a SIM, stored as a string. * <P>Type: TEXT (String)</P> * @hide */ - public static final String MNC_STRING = "mnc_string"; + public static final String MNC_STRING = SimInfo.MNC_STRING; /** * TelephonyProvider column name for the MCC associated with a SIM. * <P>Type: INTEGER (int)</P> * @hide */ - public static final String MCC = "mcc"; + public static final String MCC = SimInfo.MCC; /** * TelephonyProvider column name for the MNC associated with a SIM. * <P>Type: INTEGER (int)</P> * @hide */ - public static final String MNC = "mnc"; + public static final String MNC = SimInfo.MNC; /** * TelephonyProvider column name for the iso country code associated with a SIM. * <P>Type: TEXT (String)</P> * @hide */ - public static final String ISO_COUNTRY_CODE = "iso_country_code"; + public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE; /** * TelephonyProvider column name for the sim provisioning status associated with a SIM. * <P>Type: INTEGER (int)</P> * @hide */ - public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status"; + public static final String SIM_PROVISIONING_STATUS = SimInfo.SIM_PROVISIONING_STATUS; /** * TelephonyProvider column name for whether a subscription is embedded (that is, present on an @@ -579,7 +527,7 @@ public class SubscriptionManager { * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded. * @hide */ - public static final String IS_EMBEDDED = "is_embedded"; + public static final String IS_EMBEDDED = SimInfo.IS_EMBEDDED; /** * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the @@ -587,7 +535,7 @@ public class SubscriptionManager { * <P>Type: TEXT (String)</P> * @hide */ - public static final String CARD_ID = "card_id"; + public static final String CARD_ID = SimInfo.CARD_ID; /** * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from @@ -595,7 +543,7 @@ public class SubscriptionManager { * <p>TYPE: BLOB * @hide */ - public static final String ACCESS_RULES = "access_rules"; + public static final String ACCESS_RULES = SimInfo.ACCESS_RULES; /** * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from @@ -605,7 +553,7 @@ public class SubscriptionManager { * @hide */ public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = - "access_rules_from_carrier_configs"; + SimInfo.ACCESS_RULES_FROM_CARRIER_CONFIGS; /** * TelephonyProvider column name identifying whether an embedded subscription is on a removable @@ -615,79 +563,79 @@ public class SubscriptionManager { * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable. * @hide */ - public static final String IS_REMOVABLE = "is_removable"; + public static final String IS_REMOVABLE = SimInfo.IS_REMOVABLE; /** * TelephonyProvider column name for extreme threat in CB settings * @hide */ - public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts"; + public static final String CB_EXTREME_THREAT_ALERT = SimInfo.CB_EXTREME_THREAT_ALERT; /** * TelephonyProvider column name for severe threat in CB settings *@hide */ - public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts"; + public static final String CB_SEVERE_THREAT_ALERT = SimInfo.CB_SEVERE_THREAT_ALERT; /** * TelephonyProvider column name for amber alert in CB settings *@hide */ - public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts"; + public static final String CB_AMBER_ALERT = SimInfo.CB_AMBER_ALERT; /** * TelephonyProvider column name for emergency alert in CB settings *@hide */ - public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts"; + public static final String CB_EMERGENCY_ALERT = SimInfo.CB_EMERGENCY_ALERT; /** * TelephonyProvider column name for alert sound duration in CB settings *@hide */ - public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration"; + public static final String CB_ALERT_SOUND_DURATION = SimInfo.CB_ALERT_SOUND_DURATION; /** * TelephonyProvider column name for alert reminder interval in CB settings *@hide */ - public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; + public static final String CB_ALERT_REMINDER_INTERVAL = SimInfo.CB_ALERT_REMINDER_INTERVAL; /** * TelephonyProvider column name for enabling vibrate in CB settings *@hide */ - public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate"; + public static final String CB_ALERT_VIBRATE = SimInfo.CB_ALERT_VIBRATE; /** * TelephonyProvider column name for enabling alert speech in CB settings *@hide */ - public static final String CB_ALERT_SPEECH = "enable_alert_speech"; + public static final String CB_ALERT_SPEECH = SimInfo.CB_ALERT_SPEECH; /** * TelephonyProvider column name for ETWS test alert in CB settings *@hide */ - public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts"; + public static final String CB_ETWS_TEST_ALERT = SimInfo.CB_ETWS_TEST_ALERT; /** * TelephonyProvider column name for enable channel50 alert in CB settings *@hide */ - public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts"; + public static final String CB_CHANNEL_50_ALERT = SimInfo.CB_CHANNEL_50_ALERT; /** * TelephonyProvider column name for CMAS test alert in CB settings *@hide */ - public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts"; + public static final String CB_CMAS_TEST_ALERT = SimInfo.CB_CMAS_TEST_ALERT; /** * TelephonyProvider column name for Opt out dialog in CB settings *@hide */ - public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; + public static final String CB_OPT_OUT_DIALOG = SimInfo.CB_OPT_OUT_DIALOG; /** * TelephonyProvider column name for enable Volte. @@ -696,37 +644,37 @@ public class SubscriptionManager { * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. *@hide */ - public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled"; + public static final String ENHANCED_4G_MODE_ENABLED = SimInfo.ENHANCED_4G_MODE_ENABLED; /** * TelephonyProvider column name for enable VT (Video Telephony over IMS) *@hide */ - public static final String VT_IMS_ENABLED = "vt_ims_enabled"; + public static final String VT_IMS_ENABLED = SimInfo.VT_IMS_ENABLED; /** * TelephonyProvider column name for enable Wifi calling *@hide */ - public static final String WFC_IMS_ENABLED = "wfc_ims_enabled"; + public static final String WFC_IMS_ENABLED = SimInfo.WFC_IMS_ENABLED; /** * TelephonyProvider column name for Wifi calling mode *@hide */ - public static final String WFC_IMS_MODE = "wfc_ims_mode"; + public static final String WFC_IMS_MODE = SimInfo.WFC_IMS_MODE; /** * TelephonyProvider column name for Wifi calling mode in roaming *@hide */ - public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode"; + public static final String WFC_IMS_ROAMING_MODE = SimInfo.WFC_IMS_ROAMING_MODE; /** * TelephonyProvider column name for enable Wifi calling in roaming *@hide */ - public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled"; + public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.WFC_IMS_ROAMING_ENABLED; /** * TelephonyProvider column name for whether a subscription is opportunistic, that is, @@ -735,7 +683,7 @@ public class SubscriptionManager { * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic. * @hide */ - public static final String IS_OPPORTUNISTIC = "is_opportunistic"; + public static final String IS_OPPORTUNISTIC = SimInfo.IS_OPPORTUNISTIC; /** * TelephonyProvider column name for group ID. Subscriptions with same group ID @@ -744,7 +692,7 @@ public class SubscriptionManager { * * @hide */ - public static final String GROUP_UUID = "group_uuid"; + public static final String GROUP_UUID = SimInfo.GROUP_UUID; /** * TelephonyProvider column name for group owner. It's the package name who created @@ -752,14 +700,7 @@ public class SubscriptionManager { * * @hide */ - public static final String GROUP_OWNER = "group_owner"; - - /** - * TelephonyProvider column name for whether a subscription is metered or not, that is, whether - * the network it connects to charges for subscription or not. For example, paid CBRS or unpaid. - * @hide - */ - public static final String IS_METERED = "is_metered"; + public static final String GROUP_OWNER = SimInfo.GROUP_OWNER; /** * TelephonyProvider column name for the profile class of a subscription @@ -767,7 +708,7 @@ public class SubscriptionManager { * <P>Type: INTEGER (int)</P> * @hide */ - public static final String PROFILE_CLASS = "profile_class"; + public static final String PROFILE_CLASS = SimInfo.PROFILE_CLASS; /** * Profile class of the subscription @@ -775,11 +716,11 @@ public class SubscriptionManager { */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "PROFILE_CLASS_" }, value = { - PROFILE_CLASS_TESTING, - PROFILE_CLASS_PROVISIONING, - PROFILE_CLASS_OPERATIONAL, - PROFILE_CLASS_UNSET, - PROFILE_CLASS_DEFAULT + SimInfo.PROFILE_CLASS_TESTING, + SimInfo.PROFILE_CLASS_PROVISIONING, + SimInfo.PROFILE_CLASS_OPERATIONAL, + SimInfo.PROFILE_CLASS_UNSET, + SimInfo.PROFILE_CLASS_DEFAULT }) public @interface ProfileClass {} @@ -791,7 +732,7 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_TESTING = 0; + public static final int PROFILE_CLASS_TESTING = SimInfo.PROFILE_CLASS_TESTING; /** * A provisioning profile is pre-loaded onto the eUICC and @@ -800,7 +741,7 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_PROVISIONING = 1; + public static final int PROFILE_CLASS_PROVISIONING = SimInfo.PROFILE_CLASS_PROVISIONING; /** * An operational profile can be pre-loaded or downloaded @@ -809,7 +750,7 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_OPERATIONAL = 2; + public static final int PROFILE_CLASS_OPERATIONAL = SimInfo.PROFILE_CLASS_OPERATIONAL; /** * The profile class is unset. This occurs when profile class @@ -818,14 +759,14 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_UNSET = -1; + public static final int PROFILE_CLASS_UNSET = SimInfo.PROFILE_CLASS_UNSET; /** * Default profile class * @hide */ @SystemApi - public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET; + public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_DEFAULT; /** * IMSI (International Mobile Subscriber Identity). @@ -833,13 +774,13 @@ public class SubscriptionManager { * @hide */ //TODO: add @SystemApi - public static final String IMSI = "imsi"; + public static final String IMSI = SimInfo.IMSI; /** * Whether uicc applications is set to be enabled or disabled. By default it's enabled. * @hide */ - public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled"; + public static final String UICC_APPLICATIONS_ENABLED = SimInfo.UICC_APPLICATIONS_ENABLED; /** * Broadcast Action: The user has changed one of the default subs related to @@ -1023,8 +964,11 @@ public class SubscriptionManager { private final INetworkPolicyManager getNetworkPolicy() { if (mNetworkPolicy == null) { - mNetworkPolicy = INetworkPolicyManager.Stub - .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + mNetworkPolicy = INetworkPolicyManager.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getNetworkPolicyServiceRegisterer() + .get()); } return mNetworkPolicy; } @@ -1041,6 +985,23 @@ public class SubscriptionManager { */ public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { if (listener == null) return; + addOnSubscriptionsChangedListener(listener.mExecutor, listener); + } + + /** + * Register for changes to the list of active {@link SubscriptionInfo} records or to the + * individual records themselves. When a change occurs the onSubscriptionsChanged method of + * the listener will be invoked immediately if there has been a notification. The + * onSubscriptionChanged method will also be triggered once initially when calling this + * function. + * + * @param listener an instance of {@link OnSubscriptionsChangedListener} with + * onSubscriptionsChanged overridden. + * @param executor the executor that will execute callbacks. + */ + public void addOnSubscriptionsChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnSubscriptionsChangedListener listener) { String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>"; if (DBG) { logd("register OnSubscriptionsChangedListener pkgName=" + pkgName @@ -1052,7 +1013,7 @@ public class SubscriptionManager { mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); if (telephonyRegistryManager != null) { telephonyRegistryManager.addOnSubscriptionsChangedListener(listener, - listener.mExecutor); + executor); } } @@ -1185,7 +1146,11 @@ public class SubscriptionManager { SubscriptionInfo subInfo = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1219,7 +1184,11 @@ public class SubscriptionManager { SubscriptionInfo result = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1253,7 +1222,11 @@ public class SubscriptionManager { SubscriptionInfo result = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1277,7 +1250,11 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getAllSubInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1358,7 +1335,11 @@ public class SubscriptionManager { List<SubscriptionInfo> activeList = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1409,7 +1390,11 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1448,7 +1433,11 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName()); } @@ -1477,7 +1466,11 @@ public class SubscriptionManager { public void requestEmbeddedSubscriptionInfoListRefresh() { int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc(); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); } @@ -1506,7 +1499,11 @@ public class SubscriptionManager { @SystemApi public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); } @@ -1527,7 +1524,11 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getAllSubInfoCount(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1556,7 +1557,11 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), mContext.getFeatureId()); @@ -1577,7 +1582,11 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getActiveSubInfoCountMax(); } @@ -1634,7 +1643,11 @@ public class SubscriptionManager { } try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub == null) { Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null"); return; @@ -1668,7 +1681,11 @@ public class SubscriptionManager { } try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub == null) { Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null"); return; @@ -1771,7 +1788,11 @@ public class SubscriptionManager { int result = INVALID_SIM_SLOT_INDEX; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getSlotIndex(subscriptionId); } @@ -1805,7 +1826,11 @@ public class SubscriptionManager { int[] subId = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subId = iSub.getSubId(slotIndex); } @@ -1829,7 +1854,11 @@ public class SubscriptionManager { int result = INVALID_PHONE_INDEX; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getPhoneId(subId); } @@ -1863,7 +1892,11 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subId = iSub.getDefaultSubId(); } @@ -1886,7 +1919,11 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subId = iSub.getDefaultVoiceSubId(); } @@ -1916,7 +1953,11 @@ public class SubscriptionManager { public void setDefaultVoiceSubscriptionId(int subscriptionId) { if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.setDefaultVoiceSubId(subscriptionId); } @@ -1964,7 +2005,11 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subId = iSub.getDefaultSmsSubId(); } @@ -1990,7 +2035,11 @@ public class SubscriptionManager { public void setDefaultSmsSubId(int subscriptionId) { if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.setDefaultSmsSubId(subscriptionId); } @@ -2028,7 +2077,11 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subId = iSub.getDefaultDataSubId(); } @@ -2054,7 +2107,11 @@ public class SubscriptionManager { public void setDefaultDataSubId(int subscriptionId) { if (VDBG) logd("setDataSubscription sub id = " + subscriptionId); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.setDefaultDataSubId(subscriptionId); } @@ -2085,7 +2142,11 @@ public class SubscriptionManager { /** @hide */ public void clearSubscriptionInfo() { try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.clearSubInfo(); } @@ -2221,7 +2282,11 @@ public class SubscriptionManager { */ public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) { try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { int[] subId = iSub.getActiveSubIdList(visibleOnly); if (subId != null) return subId; @@ -2272,7 +2337,11 @@ public class SubscriptionManager { int simState = TelephonyManager.SIM_STATE_UNKNOWN; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { simState = iSub.getSimStateForSlotIndex(slotIndex); } @@ -2291,7 +2360,11 @@ public class SubscriptionManager { */ public static void setSubscriptionProperty(int subId, String propKey, String propValue) { try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.setSubscriptionProperty(subId, propKey, propValue); } @@ -2311,7 +2384,11 @@ public class SubscriptionManager { Context context) { String resultValue = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { resultValue = iSub.getSubscriptionProperty(subId, propKey, context.getOpPackageName(), context.getFeatureId()); @@ -2453,7 +2530,11 @@ public class SubscriptionManager { @UnsupportedAppUsage public boolean isActiveSubId(int subId) { try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { return iSub.isActiveSubId(subId, mContext.getOpPackageName(), mContext.getFeatureId()); @@ -2756,7 +2837,11 @@ public class SubscriptionManager { @TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) { if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub == null) return; ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { @@ -2799,7 +2884,11 @@ public class SubscriptionManager { public int getPreferredDataSubscriptionId() { int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { preferredSubId = iSub.getPreferredDataSubscriptionId(); } @@ -2830,7 +2919,11 @@ public class SubscriptionManager { List<SubscriptionInfo> subInfoList = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature); } @@ -2931,7 +3024,11 @@ public class SubscriptionManager { ParcelUuid groupUuid = null; int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug); } else { @@ -2981,7 +3078,11 @@ public class SubscriptionManager { int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug); } else { @@ -3033,7 +3134,11 @@ public class SubscriptionManager { int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug); } else { @@ -3078,7 +3183,11 @@ public class SubscriptionManager { List<SubscriptionInfo> result = null; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature); } else { @@ -3191,7 +3300,11 @@ public class SubscriptionManager { logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable); } try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { return iSub.setSubscriptionEnabled(enable, subscriptionId); } @@ -3221,7 +3334,11 @@ public class SubscriptionManager { logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled); } try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { iSub.setUiccApplicationsEnabled(enabled, subscriptionId); } @@ -3251,7 +3368,11 @@ public class SubscriptionManager { logd("canDisablePhysicalSubscription"); } try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { return iSub.canDisablePhysicalSubscription(); } @@ -3272,7 +3393,11 @@ public class SubscriptionManager { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int subscriptionId) { try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { return iSub.isSubscriptionEnabled(subscriptionId); } @@ -3295,7 +3420,11 @@ public class SubscriptionManager { int subId = INVALID_SUBSCRIPTION_ID; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { subId = iSub.getEnabledSubscriptionId(slotIndex); } @@ -3321,7 +3450,11 @@ public class SubscriptionManager { int result = 0; try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { result = helper.callMethod(iSub); } @@ -3344,7 +3477,11 @@ public class SubscriptionManager { */ public static int getActiveDataSubscriptionId() { try { - ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + ISub iSub = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); if (iSub != null) { return iSub.getActiveDataSubscriptionId(); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index df8c26da7af9..843c0656efc3 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -60,7 +60,6 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.WorkSource; import android.provider.Settings.SettingNotFoundException; @@ -107,6 +106,7 @@ import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.SmsApplication; +import com.android.telephony.Rlog; import dalvik.system.VMRuntime; @@ -785,30 +785,6 @@ public class TelephonyManager { public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause"; /** - * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast - * for an String containing the data APN type. - * - * <p class="note"> - * Retrieve with - * {@link android.content.Intent#getStringExtra(String name)}. - * - * @hide - */ - public static final String EXTRA_DATA_APN_TYPE = PhoneConstants.DATA_APN_TYPE_KEY; - - /** - * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast - * for an String containing the data APN. - * - * <p class="note"> - * Retrieve with - * {@link android.content.Intent#getStringExtra(String name)}. - * - * @hide - */ - public static final String EXTRA_DATA_APN = PhoneConstants.DATA_APN_KEY; - - /** * Broadcast intent action for letting the default dialer to know to show voicemail * notification. * @@ -5107,7 +5083,11 @@ public class TelephonyManager { @UnsupportedAppUsage private IPhoneSubInfo getSubscriberInfo() { // get it each time because that process crashes a lot - return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo")); + return IPhoneSubInfo.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPhoneSubServiceRegisterer() + .get()); } /** @@ -5320,11 +5300,19 @@ public class TelephonyManager { } private ITelephonyRegistry getTelephonyRegistry() { - return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry")); + return ITelephonyRegistry.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyRegistryServiceRegisterer() + .get()); } private IOns getIOns() { - return IOns.Stub.asInterface(ServiceManager.getService("ions")); + return IOns.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getOpportunisticNetworkServiceRegisterer() + .get()); } // @@ -5880,7 +5868,10 @@ public class TelephonyManager { * @param AID Application id. See ETSI 102.221 and 101.220. * @param p2 P2 parameter (described in ISO 7816-4). * @return an IccOpenLogicalChannelResponse object. + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) { return iccOpenLogicalChannel(getSubId(), AID, p2); } @@ -5911,7 +5902,10 @@ public class TelephonyManager { * @param p2 P2 parameter (described in ISO 7816-4). * @return an IccOpenLogicalChannelResponse object. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) { try { ITelephony telephony = getITelephony(); @@ -5939,7 +5933,10 @@ public class TelephonyManager { * iccOpenLogicalChannel. * @return true if the channel was closed successfully. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) { @@ -5966,7 +5963,10 @@ public class TelephonyManager { * @param channel is the channel id to be closed as returned by a successful * iccOpenLogicalChannel. * @return true if the channel was closed successfully. + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public boolean iccCloseLogicalChannel(int channel) { return iccCloseLogicalChannel(getSubId(), channel); } @@ -5985,7 +5985,10 @@ public class TelephonyManager { * iccOpenLogicalChannel. * @return true if the channel was closed successfully. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public boolean iccCloseLogicalChannel(int subId, int channel) { try { ITelephony telephony = getITelephony(); @@ -6021,7 +6024,10 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at the end, or null if * there is an issue connecting to the Telephony service. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi @Nullable @@ -6059,7 +6065,10 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public String iccTransmitApduLogicalChannel(int channel, int cla, int instruction, int p1, int p2, int p3, String data) { return iccTransmitApduLogicalChannel(getSubId(), channel, cla, @@ -6088,7 +6097,10 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at * the end. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int instruction, int p1, int p2, int p3, String data) { try { @@ -6124,7 +6136,10 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at * the end. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi @NonNull @@ -6160,7 +6175,10 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public String iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2, int p3, String data) { return iccTransmitApduBasicChannel(getSubId(), cla, @@ -6187,7 +6205,10 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at * the end. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public String iccTransmitApduBasicChannel(int subId, int cla, int instruction, int p1, int p2, int p3, String data) { try { @@ -6215,7 +6236,10 @@ public class TelephonyManager { * @param p3 P3 value of the APDU command. * @param filePath * @return The APDU response. + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3, String filePath) { return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath); @@ -6237,7 +6261,10 @@ public class TelephonyManager { * @param filePath * @return The APDU response. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2, int p3, String filePath) { try { @@ -6263,7 +6290,10 @@ public class TelephonyManager { * @return The APDU response from the ICC card in hexadecimal format * with the last 4 bytes being the status word. If the command fails, * returns an empty string. + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public String sendEnvelopeWithStatus(String content) { return sendEnvelopeWithStatus(getSubId(), content); } @@ -6283,7 +6313,10 @@ public class TelephonyManager { * with the last 4 bytes being the status word. If the command fails, * returns an empty string. * @hide + * @deprecated Use {@link android.se.omapi.SEService} APIs instead. */ + // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated. + @Deprecated public String sendEnvelopeWithStatus(int subId, String content) { try { ITelephony telephony = getITelephony(); diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index 96b6db75b370..a1d40e85fb10 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -16,10 +16,11 @@ package android.telephony; +import com.android.telephony.Rlog; + import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.Nullable; -import android.content.Context; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -29,7 +30,6 @@ import android.os.Message; import android.os.Messenger; import android.os.Parcelable; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.SparseArray; import com.android.internal.telephony.ITelephony; @@ -234,6 +234,9 @@ public final class TelephonyScanManager { private ITelephony getITelephony() { return ITelephony.Stub.asInterface( - ServiceManager.getService(Context.TELEPHONY_SERVICE)); + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); } } diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java index 93ccba1dd996..81a09c645070 100644 --- a/telephony/java/android/telephony/UiccAccessRule.java +++ b/telephony/java/android/telephony/UiccAccessRule.java @@ -15,6 +15,8 @@ */ package android.telephony; +import com.android.telephony.Rlog; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java index 414b9995fd58..121401277ce9 100644 --- a/telephony/java/android/telephony/VoLteServiceState.java +++ b/telephony/java/android/telephony/VoLteServiceState.java @@ -16,6 +16,8 @@ package android.telephony; +import com.android.telephony.Rlog; + import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index dbfb6a2a0f2e..fab1bf2215af 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -28,7 +28,7 @@ import android.provider.Telephony; import android.provider.Telephony.Carriers; import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetworkType; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 372bdf1c0f81..bff12b624ae8 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -31,7 +31,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.telephony.AccessNetworkConstants; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java index 11dc78a611ff..d33d3f9a5eee 100644 --- a/telephony/java/android/telephony/data/DataServiceCallback.java +++ b/telephony/java/android/telephony/data/DataServiceCallback.java @@ -22,7 +22,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.LinkProperties; import android.os.RemoteException; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.data.DataService.DataServiceProvider; import java.lang.annotation.Retention; diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java index e793979a61c9..8220b16500de 100644 --- a/telephony/java/android/telephony/data/QualifiedNetworksService.java +++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java @@ -28,7 +28,7 @@ import android.os.Message; import android.os.RemoteException; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.Annotation.ApnType; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index 16662652847d..cd3fc953f9d2 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -25,7 +25,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java index 994c49cd2315..e16fffa69f8a 100644 --- a/telephony/java/android/telephony/euicc/EuiccCardManager.java +++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java @@ -21,8 +21,8 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; import android.os.RemoteException; -import android.os.ServiceManager; import android.service.euicc.EuiccProfileInfo; +import android.telephony.TelephonyFrameworkInitializer; import android.util.Log; import com.android.internal.telephony.euicc.IAuthenticateServerCallback; @@ -148,7 +148,10 @@ public class EuiccCardManager { private IEuiccCardController getIEuiccCardController() { return IEuiccCardController.Stub.asInterface( - ServiceManager.getService("euicc_card_controller")); + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getEuiccCardControllerServiceRegisterer() + .get()); } /** diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index cb66a9650f2f..d5a48df149f1 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -30,7 +30,7 @@ import android.content.IntentSender; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.RemoteException; -import android.os.ServiceManager; +import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyManager; import android.telephony.euicc.EuiccCardManager.ResetOption; @@ -968,6 +968,10 @@ public class EuiccManager { } private static IEuiccController getIEuiccController() { - return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller")); + return IEuiccController.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getEuiccControllerService() + .get()); } } diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java index 8d2049b97138..abfee61930ed 100644 --- a/telephony/java/android/telephony/ims/ImsConferenceState.java +++ b/telephony/java/android/telephony/ims/ImsConferenceState.java @@ -24,7 +24,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.telecom.Call; import android.telecom.Connection; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.util.Log; import java.util.HashMap; diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java index dcb9c9d5ec27..136a83e2eec9 100644 --- a/telephony/java/android/telephony/ims/ImsExternalCallState.java +++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java @@ -24,7 +24,7 @@ import android.annotation.TestApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 057d22cd7eae..91514e9ebe28 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -25,14 +25,13 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.content.Context; import android.os.Binder; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; @@ -1018,7 +1017,10 @@ public class ImsMmTelManager implements RegistrationManager { private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface( - ServiceManager.getService(Context.TELEPHONY_SERVICE)); + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); if (binder == null) { throw new RuntimeException("Could not find Telephony Service."); } diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index d3fb37f005cd..c96271432ea2 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -26,8 +26,8 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; import android.telephony.AccessNetworkConstants; +import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.feature.ImsFeature; @@ -35,6 +35,8 @@ import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; +import com.android.internal.telephony.IIntegerConsumer; + import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -158,9 +160,20 @@ public class ImsRcsManager implements RegistrationManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } + + IImsRcsController imsRcsController = getIImsRcsController(); + if (imsRcsController == null) { + Log.e(TAG, "Register registration callback: IImsRcsController is null"); + throw new ImsException("Cannot find remote IMS service", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + c.setExecutor(executor); - throw new UnsupportedOperationException("registerImsRegistrationCallback is not" - + "supported."); + try { + imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder()); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } } /**{@inheritDoc}*/ @@ -171,8 +184,18 @@ public class ImsRcsManager implements RegistrationManager { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } - throw new UnsupportedOperationException("unregisterImsRegistrationCallback is not" - + "supported."); + + IImsRcsController imsRcsController = getIImsRcsController(); + if (imsRcsController == null) { + Log.e(TAG, "Unregister registration callback: IImsRcsController is null"); + throw new IllegalStateException("Cannot find remote IMS service"); + } + + try { + imsRcsController.unregisterImsRegistrationCallback(mSubId, c.getBinder()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } } /**{@inheritDoc}*/ @@ -186,8 +209,23 @@ public class ImsRcsManager implements RegistrationManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } - throw new UnsupportedOperationException("getRegistrationState is not" - + "supported."); + + IImsRcsController imsRcsController = getIImsRcsController(); + if (imsRcsController == null) { + Log.e(TAG, "Get registration state error: IImsRcsController is null"); + throw new IllegalStateException("Cannot find remote IMS service"); + } + + try { + imsRcsController.getImsRcsRegistrationState(mSubId, new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> stateCallback.accept(result)); + } + }); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } } /**{@inheritDoc}*/ @@ -202,10 +240,25 @@ public class ImsRcsManager implements RegistrationManager { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } - throw new UnsupportedOperationException("getRegistrationTransportType is not" - + "supported."); - } + IImsRcsController imsRcsController = getIImsRcsController(); + if (imsRcsController == null) { + Log.e(TAG, "Get registration transport type error: IImsRcsController is null"); + throw new IllegalStateException("Cannot find remote IMS service"); + } + + try { + imsRcsController.getImsRcsRegistrationTransportType(mSubId, + new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> transportTypeCallback.accept(result)); + } + }); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } /** * Registers an {@link AvailabilityCallback} with the system, which will provide RCS @@ -362,7 +415,10 @@ public class ImsRcsManager implements RegistrationManager { } private IImsRcsController getIImsRcsController() { - IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE); + IBinder binder = TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyImsServiceRegisterer() + .get(); return IImsRcsController.Stub.asInterface(binder); } } diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java index 6b728599c7d3..2d2e63812fad 100644 --- a/telephony/java/android/telephony/ims/ImsSsData.java +++ b/telephony/java/android/telephony/ims/ImsSsData.java @@ -22,7 +22,7 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index e4d63355625d..f0521802a167 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -25,14 +25,13 @@ import android.annotation.StringDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.WorkerThread; -import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.Binder; import android.os.RemoteException; -import android.os.ServiceManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsConfigCallback; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsConfigImplBase; @@ -415,7 +414,11 @@ public class ProvisioningManager { } private static boolean isImsAvailableOnDevice() { - IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); + IPackageManager pm = IPackageManager.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPackageManagerServiceRegisterer() + .get()); if (pm == null) { // For some reason package manger is not available.. This will fail internally anyways, // so do not throw error and allow. @@ -432,7 +435,10 @@ public class ProvisioningManager { private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface( - ServiceManager.getService(Context.TELEPHONY_SERVICE)); + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); if (binder == null) { throw new RuntimeException("Could not find Telephony Service."); } diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java index ce03c3c799bb..1e93437a5a94 100644 --- a/telephony/java/android/telephony/ims/RcsControllerCall.java +++ b/telephony/java/android/telephony/ims/RcsControllerCall.java @@ -18,7 +18,7 @@ package android.telephony.ims; import android.content.Context; import android.os.RemoteException; -import android.os.ServiceManager; +import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IRcsMessage; /** @@ -35,8 +35,11 @@ class RcsControllerCall { } <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException { - IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface(ServiceManager.getService( - Context.TELEPHONY_RCS_MESSAGE_SERVICE)); + IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyRcsMessageServiceRegisterer() + .get()); if (iRcsMessage == null) { throw new RcsMessageStoreException("Could not connect to RCS storage service"); } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 75e3f0a6393d..2e3f59a13670 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -21,12 +21,11 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; -import android.content.Context; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; +import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.util.Log; @@ -365,7 +364,10 @@ public class RcsUceAdapter { } private IImsRcsController getIImsRcsController() { - IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE); + IBinder binder = TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyImsServiceRegisterer() + .get(); return IImsRcsController.Stub.asInterface(binder); } } diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index e81bac0f6764..6f6aa44371fa 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -19,6 +19,9 @@ package android.telephony.ims.aidl; import android.net.Uri; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IRcsUceControllerCallback; +import android.telephony.ims.aidl.IImsRegistrationCallback; + +import com.android.internal.telephony.IIntegerConsumer; /** * Interface used to interact with the Telephony IMS. @@ -26,6 +29,13 @@ import android.telephony.ims.aidl.IRcsUceControllerCallback; * {@hide} */ interface IImsRcsController { + // IMS RCS registration commands + void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c); + void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c); + void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer); + void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer); + + // IMS RCS capability commands void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); boolean isCapable(int subId, int capability, int radioTech); diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index cfc803ca3639..9116a3bf3bde 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -19,7 +19,7 @@ package com.android.ims; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ProvisioningManager; import android.telephony.ims.aidl.IImsConfig; diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java index 1d6ec2d82eb7..8e86ff788a08 100644 --- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java +++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java @@ -19,7 +19,7 @@ package com.android.internal.telephony; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.content.res.XmlResourceParser; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.util.SparseIntArray; import com.android.internal.telephony.cdma.sms.UserData; diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index e75c5933f1df..832502cae37d 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -20,7 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.sysprop.TelephonyProperties; import android.telephony.PhoneNumberUtils; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.SmsCbLocation; import android.telephony.SmsCbMessage; import android.telephony.cdma.CdmaSmsCbProgramData; diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index b5af6467a0ad..cbf0f5c297e1 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -18,7 +18,7 @@ package com.android.internal.telephony.cdma.sms; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.telephony.SmsCbCmasInfo; import android.telephony.cdma.CdmaSmsCbProgramData; import android.telephony.cdma.CdmaSmsCbProgramResults; diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 0681dc11066e..417aafd765ea 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -28,7 +28,7 @@ import static com.android.internal.telephony.SmsConstants.MessageClass; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.telephony.PhoneNumberUtils; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import android.text.TextUtils; import com.android.internal.telephony.EncodeException; diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java index eed9a86cf448..0dc740194034 100644 --- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java +++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java @@ -21,7 +21,7 @@ import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.graphics.Bitmap; import android.graphics.Color; -import android.telephony.Rlog; +import com.android.telephony.Rlog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.GsmAlphabet; diff --git a/telephony/java/com/android/telephony/Rlog.java b/telephony/java/com/android/telephony/Rlog.java new file mode 100644 index 000000000000..9d6c930de8f5 --- /dev/null +++ b/telephony/java/com/android/telephony/Rlog.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.telephony; + +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; + +import com.android.internal.telephony.util.TelephonyUtils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * A copy of {@link android.telephony.Rlog} to be used within the telephony mainline module. + * + * @hide + */ +public final class Rlog { + + private static final boolean USER_BUILD = TelephonyUtils.IS_USER; + + private Rlog() { + } + + private static int log(int priority, String tag, String msg) { + return Log.logToRadioBuffer(priority, tag, msg); + } + + public static int v(String tag, String msg) { + return log(Log.VERBOSE, tag, msg); + } + + public static int v(String tag, String msg, Throwable tr) { + return log(Log.VERBOSE, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + public static int d(String tag, String msg) { + return log(Log.DEBUG, tag, msg); + } + + public static int d(String tag, String msg, Throwable tr) { + return log(Log.DEBUG, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + public static int i(String tag, String msg) { + return log(Log.INFO, tag, msg); + } + + public static int i(String tag, String msg, Throwable tr) { + return log(Log.INFO, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + public static int w(String tag, String msg) { + return log(Log.WARN, tag, msg); + } + + public static int w(String tag, String msg, Throwable tr) { + return log(Log.WARN, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + public static int w(String tag, Throwable tr) { + return log(Log.WARN, tag, Log.getStackTraceString(tr)); + } + + public static int e(String tag, String msg) { + return log(Log.ERROR, tag, msg); + } + + public static int e(String tag, String msg, Throwable tr) { + return log(Log.ERROR, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + public static int println(int priority, String tag, String msg) { + return log(priority, tag, msg); + } + + public static boolean isLoggable(String tag, int level) { + return Log.isLoggable(tag, level); + } + + /** + * Redact personally identifiable information for production users. + * @param tag used to identify the source of a log message + * @param pii the personally identifiable information we want to apply secure hash on. + * @return If tag is loggable in verbose mode or pii is null, return the original input. + * otherwise return a secure Hash of input pii + */ + public static String pii(String tag, Object pii) { + String val = String.valueOf(pii); + if (pii == null || TextUtils.isEmpty(val) || isLoggable(tag, Log.VERBOSE)) { + return val; + } + return "[" + secureHash(val.getBytes()) + "]"; + } + + /** + * Redact personally identifiable information for production users. + * @param enablePiiLogging set when caller explicitly want to enable sensitive logging. + * @param pii the personally identifiable information we want to apply secure hash on. + * @return If enablePiiLogging is set to true or pii is null, return the original input. + * otherwise return a secure Hash of input pii + */ + public static String pii(boolean enablePiiLogging, Object pii) { + String val = String.valueOf(pii); + if (pii == null || TextUtils.isEmpty(val) || enablePiiLogging) { + return val; + } + return "[" + secureHash(val.getBytes()) + "]"; + } + + /** + * Returns a secure hash (using the SHA1 algorithm) of the provided input. + * + * @return "****" if the build type is user, otherwise the hash + * @param input the bytes for which the secure hash should be computed. + */ + private static String secureHash(byte[] input) { + // Refrain from logging user personal information in user build. + if (USER_BUILD) { + return "****"; + } + + MessageDigest messageDigest; + + try { + messageDigest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + return "####"; + } + + byte[] result = messageDigest.digest(input); + return Base64.encodeToString( + result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); + } +} diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java index 653282d0d365..1361df30e9d7 100644 --- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java +++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java @@ -320,8 +320,9 @@ public class MemoryUsageTest extends InstrumentationTestCase { UserHandle.USER_CURRENT); } - mAtm.startActivityAndWait(null, null, mLaunchIntent, mimeType, - null, null, 0, mLaunchIntent.getFlags(), null, null, + mAtm.startActivityAndWait(null, + getInstrumentation().getContext().getBasePackageName(), mLaunchIntent, + mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null, UserHandle.USER_CURRENT_OR_SELF); } catch (RemoteException e) { Log.w(TAG, "Error launching app", e); diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index b4cafe41662e..656628eb39d5 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -896,39 +896,78 @@ public class PackageWatchdogTest { assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); } - /** Test that observers execute correctly for different failure reasons */ + /** Test that observers execute correctly for failures reasons that go through thresholding. */ @Test - public void testFailureReasons() { + public void testNonImmediateFailureReasons() { PackageWatchdog watchdog = createWatchdog(); TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); - TestObserver observer3 = new TestObserver(OBSERVER_NAME_3); - TestObserver observer4 = new TestObserver(OBSERVER_NAME_4); watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION); - watchdog.startObservingHealth(observer3, Arrays.asList(APP_C), SHORT_DURATION); - watchdog.startObservingHealth(observer4, Arrays.asList(APP_D), SHORT_DURATION); raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, - VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); - raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B, - VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); - raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH); - raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_D, + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); assertThat(observer1.getLastFailureReason()).isEqualTo( - PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); - assertThat(observer2.getLastFailureReason()).isEqualTo( - PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); - assertThat(observer3.getLastFailureReason()).isEqualTo( PackageWatchdog.FAILURE_REASON_APP_CRASH); - assertThat(observer4.getLastFailureReason()).isEqualTo( + assertThat(observer2.getLastFailureReason()).isEqualTo( PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); } + /** Test that observers execute correctly for failures reasons that skip thresholding. */ + @Test + public void testImmediateFailures() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); + + watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); + + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + + assertThat(observer1.mMitigatedPackages).containsExactly(APP_A, APP_B); + } + + /** + * Test that a persistent observer will mitigate failures if it wishes to observe a package. + */ + @Test + public void testPersistentObserverWatchesPackage() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1); + persistentObserver.setPersistent(true); + persistentObserver.setMayObservePackages(true); + + watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION); + + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); + assertThat(persistentObserver.mHealthCheckFailedPackages).containsExactly(APP_A); + } + + /** + * Test that a persistent observer will not mitigate failures if it does not wish to observe + * a given package. + */ + @Test + public void testPersistentObserverDoesNotWatchPackage() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1); + persistentObserver.setPersistent(true); + persistentObserver.setMayObservePackages(false); + + watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION); + + raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, + VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); + assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty(); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() @@ -964,7 +1003,12 @@ public class PackageWatchdogTest { /** Trigger package failures above the threshold. */ private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog, List<VersionedPackage> packages, int failureReason) { - for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { + long triggerFailureCount = watchdog.getTriggerFailureCount(); + if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK + || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + triggerFailureCount = 1; + } + for (int i = 0; i < triggerFailureCount; i++) { watchdog.onPackageFailure(packages, failureReason); } mTestLooper.dispatchAll(); @@ -1000,6 +1044,8 @@ public class PackageWatchdogTest { private final String mName; private int mImpact; private int mLastFailureReason; + private boolean mIsPersistent = false; + private boolean mMayObservePackages = false; final List<String> mHealthCheckFailedPackages = new ArrayList<>(); final List<String> mMitigatedPackages = new ArrayList<>(); @@ -1028,9 +1074,25 @@ public class PackageWatchdogTest { return mName; } + public boolean isPersistent() { + return mIsPersistent; + } + + public boolean mayObservePackage(String packageName) { + return mMayObservePackages; + } + public int getLastFailureReason() { return mLastFailureReason; } + + public void setPersistent(boolean persistent) { + mIsPersistent = persistent; + } + + public void setMayObservePackages(boolean mayObservePackages) { + mMayObservePackages = mayObservePackages; + } } private static class TestController extends ExplicitHealthCheckController { diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 40169b8cdcd3..9e490f765eab 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -324,6 +324,7 @@ public class StagedRollbackTest { @Test public void testNetworkPassedDoesNotRollback_Phase1() throws Exception { + // Remove available rollbacks and uninstall NetworkStack on /data/ RollbackManager rm = RollbackUtils.getRollbackManager(); String networkStack = getNetworkStackPackageName(); @@ -332,6 +333,15 @@ public class StagedRollbackTest { assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), networkStack)).isNull(); + + // Reduce health check deadline, here unlike the network failed case, we use + // a longer deadline because joining a network can take a much longer time for + // reasons external to the device than 'not joining' + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS, + Integer.toString(300000), false); + // Simulate re-installation of new NetworkStack with rollbacks enabled + installNetworkStackPackage(); } @Test @@ -343,6 +353,9 @@ public class StagedRollbackTest { @Test public void testNetworkPassedDoesNotRollback_Phase3() throws Exception { + // Sleep for > health check deadline. We expect no rollback should happen during sleeping. + // If the device reboots for rollback, this device test will fail as well as the host test. + Thread.sleep(TimeUnit.SECONDS.toMillis(310)); RollbackManager rm = RollbackUtils.getRollbackManager(); assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), getNetworkStackPackageName())).isNull(); @@ -380,11 +393,6 @@ public class StagedRollbackTest { } @Test - public void testRollbackWhitelistedApp_cleanUp() throws Exception { - RollbackUtils.getRollbackManager().expireRollbackForPackage(getModuleMetadataPackageName()); - } - - @Test public void testRollbackDataPolicy_Phase1() throws Exception { Uninstall.packages(TestApp.A, TestApp.B); Install.multi(TestApp.A1, TestApp.B1).commit(); @@ -422,6 +430,16 @@ public class StagedRollbackTest { assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1); } + @Test + public void testCleanUp() throws Exception { + // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are + // committed on a device which doesn't support checkpoint. Let's clean up all rollbacks + // so there is only one rollback to commit when testing native crashes. + RollbackManager rm = RollbackUtils.getRollbackManager(); + rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream()) + .map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage); + } + private static void runShellCommand(String cmd) { ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation() .executeShellCommand(cmd); diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 4644d8aee306..91577c202df9 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -22,8 +22,8 @@ import static org.testng.Assert.assertThrows; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,6 +53,11 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { getDevice().reboot(); } + @After + public void tearDown() throws Exception { + runPhase("testCleanUp"); + } + /** * Tests watchdog triggered staged rollbacks involving only apks. */ @@ -128,21 +133,8 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { * Tests passed network health check does not trigger watchdog staged rollbacks. */ @Test - @Ignore("b/143514090") public void testNetworkPassedDoesNotRollback() throws Exception { - // Remove available rollbacks and uninstall NetworkStack on /data/ runPhase("testNetworkPassedDoesNotRollback_Phase1"); - // Reduce health check deadline, here unlike the network failed case, we use - // a longer deadline because joining a network can take a much longer time for - // reasons external to the device than 'not joining' - getDevice().executeShellCommand("device_config put rollback " - + "watchdog_request_timeout_millis 300000"); - // Simulate re-installation of new NetworkStack with rollbacks enabled - getDevice().executeShellCommand("pm install -r --staged --enable-rollback " - + getNetworkStackPath()); - - // Sleep to allow writes to disk before reboot - Thread.sleep(5000); // Reboot device to activate staged package getDevice().reboot(); @@ -157,8 +149,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // on mobile data getDevice().waitForDeviceAvailable(); - // Sleep for > health check deadline - Thread.sleep(310000); // Verify rollback was not executed after health check deadline runPhase("testNetworkPassedDoesNotRollback_Phase3"); } @@ -180,16 +170,9 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { */ @Test public void testRollbackWhitelistedApp() throws Exception { - try { - runPhase("testRollbackWhitelistedApp_Phase1"); - getDevice().reboot(); - runPhase("testRollbackWhitelistedApp_Phase2"); - } finally { - // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are - // committed on a device which doesn't support checkpoint. Let's clean up the rollback - // so there is only one rollback to commit when testing native crashes. - runPhase("testRollbackWhitelistedApp_cleanUp"); - } + runPhase("testRollbackWhitelistedApp_Phase1"); + getDevice().reboot(); + runPhase("testRollbackWhitelistedApp_Phase2"); } @Test diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java index c16a0f446651..33d77d288e15 100644 --- a/tests/net/java/android/net/NetworkStatsTest.java +++ b/tests/net/java/android/net/NetworkStatsTest.java @@ -64,15 +64,15 @@ public class NetworkStatsTest { @Test public void testFindIndex() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 5) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) - .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12) - .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12); assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, @@ -94,21 +94,21 @@ public class NetworkStatsTest { @Test public void testFindIndexHinted() { final NetworkStats stats = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) - .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) - .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10) - .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) - .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) - .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) - .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12); // verify that we correctly find across regardless of hinting @@ -143,27 +143,27 @@ public class NetworkStatsTest { assertEquals(0, stats.size()); assertEquals(4, stats.internalSize()); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5); assertEquals(4, stats.size()); assertEquals(4, stats.internalSize()); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11); assertEquals(9, stats.size()); @@ -193,8 +193,8 @@ public class NetworkStatsTest { public void testCombineExisting() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 10); - stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10); - stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2); + stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10); + stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2); stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L, -128L, -1L, -1); @@ -215,12 +215,12 @@ public class NetworkStatsTest { @Test public void testSubtractIdenticalData() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); final NetworkStats after = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); final NetworkStats result = after.subtract(before); @@ -234,12 +234,12 @@ public class NetworkStatsTest { @Test public void testSubtractIdenticalRows() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); final NetworkStats after = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20); final NetworkStats result = after.subtract(before); @@ -253,13 +253,13 @@ public class NetworkStatsTest { @Test public void testSubtractNewRows() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); final NetworkStats after = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12) - .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12) + .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20); final NetworkStats result = after.subtract(before); @@ -275,11 +275,11 @@ public class NetworkStatsTest { @Test public void testSubtractMissingRows() throws Exception { final NetworkStats before = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0) - .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0); + .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0) + .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0); final NetworkStats after = new NetworkStats(TEST_START, 1) - .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0); + .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0); final NetworkStats result = after.subtract(before); @@ -293,40 +293,40 @@ public class NetworkStatsTest { @Test public void testTotalBytes() throws Exception { final NetworkStats iface = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L); + .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L); assertEquals(384L, iface.getTotalBytes()); final NetworkStats uidSet = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L); assertEquals(96L, uidSet.getTotalBytes()); final NetworkStats uidTag = new NetworkStats(TEST_START, 6) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L); assertEquals(64L, uidTag.getTotalBytes()); final NetworkStats uidMetered = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); assertEquals(96L, uidMetered.getTotalBytes()); final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); assertEquals(96L, uidRoaming.getTotalBytes()); } @@ -343,11 +343,11 @@ public class NetworkStatsTest { @Test public void testGroupedByIfaceAll() throws Exception { final NetworkStats uidStats = new NetworkStats(TEST_START, 3) - .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) - .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L) - .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, + .addEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L); final NetworkStats grouped = uidStats.groupedByIface(); @@ -361,19 +361,19 @@ public class NetworkStatsTest { @Test public void testGroupedByIface() throws Exception { final NetworkStats uidStats = new NetworkStats(TEST_START, 7) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L); final NetworkStats grouped = uidStats.groupedByIface(); @@ -390,19 +390,19 @@ public class NetworkStatsTest { @Test public void testAddAllValues() { final NetworkStats first = new NetworkStats(TEST_START, 5) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, + .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); final NetworkStats second = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, + .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); first.combineAllValues(second); @@ -421,19 +421,19 @@ public class NetworkStatsTest { @Test public void testGetTotal() { final NetworkStats stats = new NetworkStats(TEST_START, 7) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L); assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L); @@ -459,7 +459,7 @@ public class NetworkStatsTest { assertEquals(0, after.size()); // Test 1 item stats. - before.addValues(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L); + before.addEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L); after = before.clone(); after.removeUids(new int[0]); assertEquals(1, after.size()); @@ -469,12 +469,12 @@ public class NetworkStatsTest { assertEquals(0, after.size()); // Append remaining test items. - before.addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L); + before.addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L) + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L) + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L) + .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L) + .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L); assertEquals(7, before.size()); // Test remove with empty uid list. @@ -505,12 +505,12 @@ public class NetworkStatsTest { @Test public void testClone() throws Exception { final NetworkStats original = new NetworkStats(TEST_START, 5) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); // make clone and mutate original final NetworkStats clone = original.clone(); - original.addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L); + original.addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L); assertEquals(3, original.size()); assertEquals(2, clone.size()); @@ -523,8 +523,8 @@ public class NetworkStatsTest { public void testAddWhenEmpty() throws Exception { final NetworkStats red = new NetworkStats(TEST_START, -1); final NetworkStats blue = new NetworkStats(TEST_START, 5) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); + .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) + .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); // We're mostly checking that we don't crash red.combineAllValues(blue); @@ -537,39 +537,39 @@ public class NetworkStatsTest { final String underlyingIface = "wlan0"; final int testTag1 = 8888; NetworkStats delta = new NetworkStats(TEST_START, 17) - .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L) - .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) - .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L) - .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L) - // VPN package also uses some traffic through unprotected network. - .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L) - .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) - // Tag entries - .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L) - .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L) - // Irrelevant entries - .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L) - // Underlying Iface entries - .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L) - .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) - .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */, - 299L /* smaller than sum(tun0) */, 0L) - .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - - delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface}); + .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L) + .addEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) + .addEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L) + .addEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L) + // VPN package also uses some traffic through unprotected network. + .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L) + .addEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) + // Tag entries + .addEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L) + .addEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L) + // Irrelevant entries + .addEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L) + // Underlying Iface entries + .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L) + .addEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) + .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */, + 299L /* smaller than sum(tun0) */, 0L) + .addEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); + + delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface}); assertEquals(20, delta.size()); // tunIface and TEST_IFACE entries are not changed. @@ -634,21 +634,21 @@ public class NetworkStatsTest { final String tunIface = "tun0"; final String underlyingIface = "wlan0"; NetworkStats delta = new NetworkStats(TEST_START, 9) - // 2 different apps sent/receive data via tun0. - .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L) - .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L) - // VPN package resends data through the tunnel (with exaggerated overhead) - .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L) - // 1 app already has some traffic on the underlying interface, the other doesn't yet - .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L) - // Traffic through the underlying interface via the vpn app. - // This test should redistribute this data correctly. - .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L); + // 2 different apps sent/receive data via tun0. + .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L) + .addEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L) + // VPN package resends data through the tunnel (with exaggerated overhead) + .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L) + // 1 app already has some traffic on the underlying interface, the other doesn't yet + .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L) + // Traffic through the underlying interface via the vpn app. + // This test should redistribute this data correctly. + .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L); delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface}); assertEquals(9, delta.size()); @@ -697,9 +697,9 @@ public class NetworkStatsTest { DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); NetworkStats stats = new NetworkStats(TEST_START, 3) - .addValues(entry1) - .addValues(entry2) - .addValues(entry3); + .addEntry(entry1) + .addEntry(entry2) + .addEntry(entry3); stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL); assertEquals(3, stats.size()); @@ -724,9 +724,9 @@ public class NetworkStatsTest { DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); NetworkStats stats = new NetworkStats(TEST_START, 3) - .addValues(entry1) - .addValues(entry2) - .addValues(entry3); + .addEntry(entry1) + .addEntry(entry2) + .addEntry(entry3); stats.filter(testUid, INTERFACES_ALL, TAG_ALL); assertEquals(2, stats.size()); @@ -755,10 +755,10 @@ public class NetworkStatsTest { DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); NetworkStats stats = new NetworkStats(TEST_START, 4) - .addValues(entry1) - .addValues(entry2) - .addValues(entry3) - .addValues(entry4); + .addEntry(entry1) + .addEntry(entry2) + .addEntry(entry3) + .addEntry(entry4); stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL); assertEquals(3, stats.size()); @@ -778,8 +778,8 @@ public class NetworkStatsTest { DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); NetworkStats stats = new NetworkStats(TEST_START, 3) - .addValues(entry1) - .addValues(entry2); + .addEntry(entry1) + .addEntry(entry2); stats.filter(UID_ALL, new String[] { }, TAG_ALL); assertEquals(0, stats.size()); @@ -802,9 +802,9 @@ public class NetworkStatsTest { DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); NetworkStats stats = new NetworkStats(TEST_START, 3) - .addValues(entry1) - .addValues(entry2) - .addValues(entry3); + .addEntry(entry1) + .addEntry(entry2) + .addEntry(entry3); stats.filter(UID_ALL, INTERFACES_ALL, testTag); assertEquals(2, stats.size()); @@ -831,10 +831,10 @@ public class NetworkStatsTest { DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); NetworkStats stats = new NetworkStats(TEST_START, 4) - .addValues(entry1) - .addValues(entry2) - .addValues(entry3) - .addValues(entry4); + .addEntry(entry1) + .addEntry(entry2) + .addEntry(entry3) + .addEntry(entry4); stats.filterDebugEntries(); @@ -891,14 +891,14 @@ public class NetworkStatsTest { 0 /* operations */); final NetworkStats statsXt = new NetworkStats(TEST_START, 3) - .addValues(appEntry) - .addValues(xtRootUidEntry) - .addValues(otherEntry); + .addEntry(appEntry) + .addEntry(xtRootUidEntry) + .addEntry(otherEntry); final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3) - .addValues(appEntry) - .addValues(ebpfRootUidEntry) - .addValues(otherEntry); + .addEntry(appEntry) + .addEntry(ebpfRootUidEntry) + .addEntry(otherEntry); statsXt.apply464xlatAdjustments(stackedIface, false); statsEbpf.apply464xlatAdjustments(stackedIface, true); @@ -945,8 +945,8 @@ public class NetworkStatsTest { 0 /* operations */); NetworkStats stats = new NetworkStats(TEST_START, 2) - .addValues(firstEntry) - .addValues(secondEntry); + .addEntry(firstEntry) + .addEntry(secondEntry); // Empty map: no adjustment stats.apply464xlatAdjustments(new ArrayMap<>(), false); diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java index c0f9dc14869f..f0e5774a5dea 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java @@ -326,14 +326,14 @@ public class NetworkStatsObserversTest { // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( @@ -359,14 +359,14 @@ public class NetworkStatsObserversTest { // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( @@ -391,14 +391,14 @@ public class NetworkStatsObserversTest { // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( @@ -424,14 +424,14 @@ public class NetworkStatsObserversTest { // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, + .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, + .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 4d42a612030d..6de068e48a38 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -298,11 +298,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L)); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L)); mService.setUidForeground(UID_RED, false); mService.incrementOperationCount(UID_RED, 0xFAAD, 4); mService.setUidForeground(UID_RED, true); @@ -407,9 +407,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L)); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); mService.incrementOperationCount(UID_RED, 0xF00D, 10); forcePollAndWaitForIdle(); @@ -429,9 +429,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L)); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); forcePollAndWaitForIdle(); @@ -443,10 +443,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L)); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L)); mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10); forcePollAndWaitForIdle(); @@ -480,10 +480,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L)); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L) - .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L) + .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); mService.incrementOperationCount(UID_RED, 0xFAAD, 10); forcePollAndWaitForIdle(); @@ -501,10 +501,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L)); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L) - .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L) + .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); final Intent intent = new Intent(ACTION_UID_REMOVED); intent.putExtra(EXTRA_UID, UID_BLUE); mServiceContext.sendBroadcast(intent); @@ -536,8 +536,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); mService.incrementOperationCount(UID_RED, 0xF00D, 5); forcePollAndWaitForIdle(); @@ -552,8 +552,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); forcePollAndWaitForIdle(); @@ -564,10 +564,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) - .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) + .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L)); mService.incrementOperationCount(UID_RED, 0xFAAD, 5); forcePollAndWaitForIdle(); @@ -591,9 +591,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L)); mService.incrementOperationCount(UID_RED, 0xF00D, 1); forcePollAndWaitForIdle(); @@ -608,9 +608,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) + .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L)); forcePollAndWaitForIdle(); // first verify entire history present @@ -654,9 +654,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(entry1) - .addValues(entry2) - .addValues(entry3)); + .addEntry(entry1) + .addEntry(entry2) + .addEntry(entry3)); mService.incrementOperationCount(UID_RED, 0xF00D, 1); NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL); @@ -704,11 +704,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { .thenReturn(augmentedIfaceFilter); when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL))) .thenReturn(new NetworkStats(getElapsedRealtime(), 1) - .addValues(uidStats)); + .addEntry(uidStats)); when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)) .thenReturn(new NetworkStats(getElapsedRealtime(), 2) - .addValues(tetheredStats1) - .addValues(tetheredStats2)); + .addEntry(tetheredStats1) + .addEntry(tetheredStats2)); NetworkStats stats = mService.getDetailedUidStats(ifaceFilter); @@ -745,8 +745,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)); mService.incrementOperationCount(UID_RED, 0xF00D, 1); forcePollAndWaitForIdle(); @@ -760,10 +760,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L)); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L) + .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L)); mService.setUidForeground(UID_RED, true); mService.incrementOperationCount(UID_RED, 0xFAAD, 1); @@ -804,9 +804,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer. // We layer them on top by inspecting the iface properties. expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); mService.incrementOperationCount(UID_RED, 0xF00D, 1); @@ -843,9 +843,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it // on top by inspecting the iface properties. expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO, DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); forcePollAndWaitForIdle(); @@ -885,10 +885,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // Traffic for UID_RED. final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L); + .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L); // All tethering traffic, both hardware and software. final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, + .addEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L); expectNetworkStatsSummary(ifaceStats, tetherStatsHardware); diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp index f71be7b0b7d3..a6625ab9c17f 100644 --- a/tests/utils/testutils/Android.bp +++ b/tests/utils/testutils/Android.bp @@ -22,6 +22,7 @@ java_library { static_libs: [ "junit", "hamcrest-library", + "androidx.test.runner", ], libs: [ diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java index e2b517f875db..bce2ab5c5a7f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java +++ b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.accessibility; +package com.android.server.accessibility.test; import android.os.Handler; import android.os.Looper; @@ -31,7 +31,7 @@ import java.util.List; * at their target. */ public class MessageCapturingHandler extends Handler { - List<Pair<Message, Long>> timedMessages = new ArrayList<>(); + public List<Pair<Message, Long>> timedMessages = new ArrayList<>(); Handler.Callback mCallback; diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp index 7f0872c02613..7fa47f696b50 100644 --- a/tools/stats_log_api_gen/java_writer.cpp +++ b/tools/stats_log_api_gen/java_writer.cpp @@ -85,8 +85,9 @@ static int write_java_methods( string indent(""); if (supportQ) { // TODO(b/146235828): Use just SDK_INT check once it is incremented from Q. - fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q ||\n"); - fprintf(out, " Build.VERSION.CODENAME.equals(\"R\")) {\n"); + fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q\n"); + fprintf(out, " || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q\n"); + fprintf(out, " && Build.VERSION.PREVIEW_SDK_INT > 0)) {\n"); indent = " "; } diff --git a/wifi/Android.bp b/wifi/Android.bp index 634f6746d815..180368cbd9f7 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -67,10 +67,15 @@ java_library { optimize: { enabled: false }, + hostdex: true, // for hiddenapi check visibility: [ "//frameworks/base", // TODO(b/140299412) remove once all dependencies are fixed "//frameworks/opt/net/wifi/service:__subpackages__", ] + test_access_hidden_api_whitelist, + apex_available: [ + "com.android.wifi", + "test_com.android.wifi", + ], plugins: ["java_api_finder"], } @@ -106,8 +111,10 @@ java_defaults { name: "framework-wifi-test-defaults", sdk_version: "core_platform", // tests can use @CorePlatformApi's libs: [ + // order matters: classes in framework-wifi are resolved before framework, meaning + // @hide APIs in framework-wifi are resolved before @SystemApi stubs in framework "framework-wifi", - "framework-minus-apex", + "framework", // if sdk_version="" this gets automatically included, but here we need to add manually. "framework-res", diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 1678d5a4776b..f490766559de 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -90,6 +90,8 @@ interface IWifiManager void allowAutojoin(int netId, boolean choice); + void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin); + boolean startScan(String packageName, String featureId); List<ScanResult> getScanResults(String callingPackage, String callingFeatureId); @@ -248,4 +250,6 @@ interface IWifiManager void unregisterSuggestionConnectionStatusListener(int listenerIdentifier, String packageName); int calculateSignalLevel(int rssi); + + List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults); } diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java index c4474e2bc9cc..2bbe7d2aa4ec 100644 --- a/wifi/java/android/net/wifi/SoftApCapability.java +++ b/wifi/java/android/net/wifi/SoftApCapability.java @@ -61,11 +61,20 @@ public final class SoftApCapability implements Parcelable { */ public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 1 << 1; + + /** + * Support for WPA3 Simultaneous Authentication of Equals (WPA3-SAE). + * + * flag when {@link config_wifi_softap_sae_supported)} is true. + */ + public static final int SOFTAP_FEATURE_WPA3_SAE = 1 << 2; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = { SOFTAP_FEATURE_ACS_OFFLOAD, SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT, + SOFTAP_FEATURE_WPA3_SAE, }) public @interface HotspotFeatures {} diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index 05e245b8eabc..2b7f8af728d7 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -25,6 +25,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -55,6 +56,11 @@ import java.util.concurrent.Executor; @SystemApi public final class SoftApConfiguration implements Parcelable { + @VisibleForTesting + static final int PSK_MIN_LEN = 8; + @VisibleForTesting + static final int PSK_MAX_LEN = 63; + /** * 2GHz band. * @hide @@ -142,9 +148,10 @@ public final class SoftApConfiguration implements Parcelable { private final @Nullable MacAddress mBssid; /** - * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK). + * Pre-shared key for WPA2-PSK or WPA3-SAE-Transition or WPA3-SAE encryption which depends on + * the security type. */ - private final @Nullable String mWpa2Passphrase; + private final @Nullable String mPassphrase; /** * This is a network that does not broadcast its SSID, so an @@ -175,6 +182,12 @@ public final class SoftApConfiguration implements Parcelable { private final @SecurityType int mSecurityType; /** + * Delay in milliseconds before shutting down soft AP when + * there are no connected devices. + */ + private final int mShutdownTimeoutMillis; + + /** * Security types we support. */ /** @hide */ @@ -186,25 +199,36 @@ public final class SoftApConfiguration implements Parcelable { public static final int SECURITY_TYPE_WPA2_PSK = 1; /** @hide */ + @SystemApi + public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; + + /** @hide */ + @SystemApi + public static final int SECURITY_TYPE_WPA3_SAE = 3; + + /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "SECURITY_TYPE" }, value = { + @IntDef(prefix = { "SECURITY_TYPE_" }, value = { SECURITY_TYPE_OPEN, SECURITY_TYPE_WPA2_PSK, + SECURITY_TYPE_WPA3_SAE_TRANSITION, + SECURITY_TYPE_WPA3_SAE, }) public @interface SecurityType {} /** Private constructor for Builder and Parcelable implementation. */ private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid, - @Nullable String wpa2Passphrase, boolean hiddenSsid, @BandType int band, int channel, - @SecurityType int securityType, int maxNumberOfClients) { + @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel, + @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis) { mSsid = ssid; mBssid = bssid; - mWpa2Passphrase = wpa2Passphrase; + mPassphrase = passphrase; mHiddenSsid = hiddenSsid; mBand = band; mChannel = channel; mSecurityType = securityType; mMaxNumberOfClients = maxNumberOfClients; + mShutdownTimeoutMillis = shutdownTimeoutMillis; } @Override @@ -218,18 +242,19 @@ public final class SoftApConfiguration implements Parcelable { SoftApConfiguration other = (SoftApConfiguration) otherObj; return Objects.equals(mSsid, other.mSsid) && Objects.equals(mBssid, other.mBssid) - && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase) + && Objects.equals(mPassphrase, other.mPassphrase) && mHiddenSsid == other.mHiddenSsid && mBand == other.mBand && mChannel == other.mChannel && mSecurityType == other.mSecurityType - && mMaxNumberOfClients == other.mMaxNumberOfClients; + && mMaxNumberOfClients == other.mMaxNumberOfClients + && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis; } @Override public int hashCode() { - return Objects.hash(mSsid, mBssid, mWpa2Passphrase, mHiddenSsid, - mBand, mChannel, mSecurityType, mMaxNumberOfClients); + return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid, + mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis); } @Override @@ -237,13 +262,14 @@ public final class SoftApConfiguration implements Parcelable { StringBuilder sbuf = new StringBuilder(); sbuf.append("ssid=").append(mSsid); if (mBssid != null) sbuf.append(" \n bssid=").append(mBssid.toString()); - sbuf.append(" \n Wpa2Passphrase =").append( - TextUtils.isEmpty(mWpa2Passphrase) ? "<empty>" : "<non-empty>"); + sbuf.append(" \n Passphrase =").append( + TextUtils.isEmpty(mPassphrase) ? "<empty>" : "<non-empty>"); sbuf.append(" \n HiddenSsid =").append(mHiddenSsid); sbuf.append(" \n Band =").append(mBand); sbuf.append(" \n Channel =").append(mChannel); sbuf.append(" \n SecurityType=").append(getSecurityType()); sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients); + sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis); return sbuf.toString(); } @@ -251,12 +277,13 @@ public final class SoftApConfiguration implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mSsid); dest.writeParcelable(mBssid, flags); - dest.writeString(mWpa2Passphrase); + dest.writeString(mPassphrase); dest.writeBoolean(mHiddenSsid); dest.writeInt(mBand); dest.writeInt(mChannel); dest.writeInt(mSecurityType); dest.writeInt(mMaxNumberOfClients); + dest.writeInt(mShutdownTimeoutMillis); } @Override @@ -272,7 +299,7 @@ public final class SoftApConfiguration implements Parcelable { in.readString(), in.readParcelable(MacAddress.class.getClassLoader()), in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(), - in.readInt()); + in.readInt(), in.readInt()); } @Override @@ -300,12 +327,12 @@ public final class SoftApConfiguration implements Parcelable { } /** - * Returns String set to be passphrase for the WPA2-PSK AP. - * {@link Builder#setWpa2Passphrase(String)}. + * Returns String set to be passphrase for current AP. + * {@link #setPassphrase(String, @SecurityType int)}. */ @Nullable - public String getWpa2Passphrase() { - return mWpa2Passphrase; + public String getPassphrase() { + return mPassphrase; } /** @@ -351,6 +378,15 @@ public final class SoftApConfiguration implements Parcelable { } /** + * Returns the shutdown timeout in milliseconds. + * The Soft AP will shutdown when there are no devices associated to it for + * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}. + */ + public int getShutdownTimeoutMillis() { + return mShutdownTimeoutMillis; + } + + /** * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a * Soft AP. * @@ -360,23 +396,13 @@ public final class SoftApConfiguration implements Parcelable { public static final class Builder { private String mSsid; private MacAddress mBssid; - private String mWpa2Passphrase; + private String mPassphrase; private boolean mHiddenSsid; private int mBand; private int mChannel; private int mMaxNumberOfClients; - - private int setSecurityType() { - int securityType = SECURITY_TYPE_OPEN; - if (!TextUtils.isEmpty(mWpa2Passphrase)) { // WPA2-PSK network. - securityType = SECURITY_TYPE_WPA2_PSK; - } - return securityType; - } - - private void clearAllPassphrase() { - mWpa2Passphrase = null; - } + private int mSecurityType; + private int mShutdownTimeoutMillis; /** * Constructs a Builder with default values (see {@link Builder}). @@ -384,11 +410,13 @@ public final class SoftApConfiguration implements Parcelable { public Builder() { mSsid = null; mBssid = null; - mWpa2Passphrase = null; + mPassphrase = null; mHiddenSsid = false; mBand = BAND_2GHZ; mChannel = 0; mMaxNumberOfClients = 0; + mSecurityType = SECURITY_TYPE_OPEN; + mShutdownTimeoutMillis = 0; } /** @@ -399,11 +427,13 @@ public final class SoftApConfiguration implements Parcelable { mSsid = other.mSsid; mBssid = other.mBssid; - mWpa2Passphrase = other.mWpa2Passphrase; + mPassphrase = other.mPassphrase; mHiddenSsid = other.mHiddenSsid; mBand = other.mBand; mChannel = other.mChannel; mMaxNumberOfClients = other.mMaxNumberOfClients; + mSecurityType = other.mSecurityType; + mShutdownTimeoutMillis = other.mShutdownTimeoutMillis; } /** @@ -413,8 +443,9 @@ public final class SoftApConfiguration implements Parcelable { */ @NonNull public SoftApConfiguration build() { - return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase, - mHiddenSsid, mBand, mChannel, setSecurityType(), mMaxNumberOfClients); + return new SoftApConfiguration(mSsid, mBssid, mPassphrase, + mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients, + mShutdownTimeoutMillis); } /** @@ -462,26 +493,43 @@ public final class SoftApConfiguration implements Parcelable { } /** - * Specifies that this AP should use WPA2-PSK with the given ASCII WPA2 passphrase. - * When set to null, an open network is created. - * <p> + * Specifies that this AP should use specific security type with the given ASCII passphrase. + * + * @param securityType one of the security types from {@link @SecurityType}. + * @param passphrase The passphrase to use for sepcific {@link @SecurityType} configuration + * or null with {@link @SecurityType#SECURITY_TYPE_OPEN}. * - * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK - * configuration. * @return Builder for chaining. - * @throws IllegalArgumentException when the passphrase is the empty string + * @throws IllegalArgumentException when the passphrase length is invalid and + * {@code securityType} is not {@link @SecurityType#SECURITY_TYPE_OPEN} + * or non-null passphrase and {@code securityType} is + * {@link @SecurityType#SECURITY_TYPE_OPEN}. */ @NonNull - public Builder setWpa2Passphrase(@Nullable String passphrase) { - if (passphrase != null) { + public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) { + if (securityType == SECURITY_TYPE_OPEN) { + if (passphrase != null) { + throw new IllegalArgumentException( + "passphrase should be null when security type is open"); + } + } else { + Preconditions.checkStringNotEmpty(passphrase); final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); if (!asciiEncoder.canEncode(passphrase)) { throw new IllegalArgumentException("passphrase not ASCII encodable"); } - Preconditions.checkStringNotEmpty(passphrase); + if (securityType == SECURITY_TYPE_WPA2_PSK + || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) { + if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) { + throw new IllegalArgumentException( + "Password size must be at least " + PSK_MIN_LEN + + " and no more than " + PSK_MAX_LEN + + " for WPA2_PSK and WPA3_SAE_TRANSITION Mode"); + } + } } - clearAllPassphrase(); - mWpa2Passphrase = passphrase; + mSecurityType = securityType; + mPassphrase = passphrase; return this; } @@ -589,5 +637,30 @@ public final class SoftApConfiguration implements Parcelable { mMaxNumberOfClients = maxNumberOfClients; return this; } + + /** + * Specifies the shutdown timeout in milliseconds. + * The Soft AP will shut down when there are no devices connected to it for + * the timeout duration. + * + * Specify a value of 0 to have the framework automatically use default timeout + * setting which defined in {@link R.integer.config_wifi_framework_soft_ap_timeout_delay} + * + * <p> + * <li>If not set, defaults to 0</li> + * <li>The shut down timout will apply when + * {@link Settings.Global.SOFT_AP_TIMEOUT_ENABLED} is true</li> + * + * @param timeoutMillis milliseconds of the timeout delay. + * @return Builder for chaining. + */ + @NonNull + public Builder setShutdownTimeoutMillis(int timeoutMillis) { + if (timeoutMillis < 0) { + throw new IllegalArgumentException("Invalid timeout value"); + } + mShutdownTimeoutMillis = timeoutMillis; + return this; + } } } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index a379c75360ac..f4c5b9168cd0 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -253,9 +253,12 @@ public class WifiConfiguration implements Parcelable { /** LEAP/Network EAP (only used with LEAP) */ public static final int LEAP = 2; + /** SAE (Used only for WPA3-Personal) */ + public static final int SAE = 3; + public static final String varName = "auth_alg"; - public static final String[] strings = { "OPEN", "SHARED", "LEAP" }; + public static final String[] strings = { "OPEN", "SHARED", "LEAP", "SAE" }; } /** @@ -468,10 +471,13 @@ public class WifiConfiguration implements Parcelable { break; case SECURITY_TYPE_SAE: allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); requirePMF = true; break; case SECURITY_TYPE_EAP_SUITE_B: allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256); // Note: allowedSuiteBCiphers bitset will be set by the service once the @@ -480,6 +486,8 @@ public class WifiConfiguration implements Parcelable { break; case SECURITY_TYPE_OWE: allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); requirePMF = true; break; case SECURITY_TYPE_WAPI_PSK: diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 378c67ba4fcb..1baab12b8ab5 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -545,15 +545,22 @@ public class WifiManager { public static final String EXTRA_WIFI_AP_STATE = "wifi_state"; /** - * The look up key for an int that indicates why softAP started failed - * currently support general and no_channel - * @see #SAP_START_FAILURE_GENERAL - * @see #SAP_START_FAILURE_NO_CHANNEL - * @see #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION + * An extra containing the int error code for Soft AP start failure. + * Can be obtained from the {@link #WIFI_AP_STATE_CHANGED_ACTION} using + * {@link android.content.Intent#getIntExtra}. + * This extra will only be attached if {@link #EXTRA_WIFI_AP_STATE} is + * attached and is equal to {@link #WIFI_AP_STATE_FAILED}. + * + * The error code will be one of: + * {@link #SAP_START_FAILURE_GENERAL}, + * {@link #SAP_START_FAILURE_NO_CHANNEL}, + * {@link #SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION} * * @hide */ - public static final String EXTRA_WIFI_AP_FAILURE_REASON = "wifi_ap_error_code"; + @SystemApi + public static final String EXTRA_WIFI_AP_FAILURE_REASON = + "android.net.wifi.extra.WIFI_AP_FAILURE_REASON"; /** * The previous Wi-Fi state. * @@ -1359,6 +1366,36 @@ public class WifiManager { } /** + * Retrieve a list of {@link WifiConfiguration} for available {@link WifiNetworkSuggestion} + * matching the given list of {@link ScanResult}. + * + * An available {@link WifiNetworkSuggestion} must satisfy: + * <ul> + * <li> Matching one of the {@link ScanResult} from the given list. + * <li> and {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} set + * to true. + * </ul> + * + * @param scanResults a list of scanResult. + * @return a list of @link WifiConfiguration} for available {@link WifiNetworkSuggestion} + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD + }) + @NonNull + public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( + @NonNull List<ScanResult> scanResults) { + try { + return mService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * Returns a list of unique Hotspot 2.0 OSU (Online Sign-Up) providers associated with a given * list of ScanResult. * @@ -4103,6 +4140,23 @@ public class WifiManager { } /** + * Configure auto-join settings for a Passpoint profile. + * + * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. + * @param enableAutoJoin true to enable autojoin, false to disable autojoin. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void allowAutojoinPasspoint(@NonNull String fqdn, boolean enableAutoJoin) { + try { + mService.allowAutojoinPasspoint(fqdn, enableAutoJoin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Disable an ephemeral network. * * @param ssid in the format of WifiConfiguration's SSID. diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 5befb54ce50a..1822e84fdd57 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -17,6 +17,7 @@ package android.net.wifi.hotspot2; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; import android.net.wifi.hotspot2.pps.Policy; @@ -423,6 +424,41 @@ public final class PasspointConfiguration implements Parcelable { } /** + * The auto-join configuration specifies whether or not the Passpoint Configuration is + * considered for auto-connection. If true then yes, if false then it isn't considered as part + * of auto-connection - but can still be manually connected to. + */ + private boolean mIsAutoJoinEnabled = true; + + /** + * Configures the auto-association status of this Passpoint configuration. A value of true + * indicates that the configuration will be considered for auto-connection, a value of false + * indicates that only manual connection will work - the framework will not auto-associate to + * this Passpoint network. + * + * @param autoJoinEnabled true to be considered for framework auto-connection, false otherwise. + * @hide + */ + public void setAutoJoinEnabled(boolean autoJoinEnabled) { + mIsAutoJoinEnabled = autoJoinEnabled; + } + + /** + * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A + * value of true indicates that auto-connection can happen, a value of false indicates that it + * cannot. However, even when auto-connection is not possible manual connection by the user is + * possible. + * + * @return the auto-join configuration: true for auto-connection (or join) enabled, false + * otherwise. + * @hide + */ + @SystemApi + public boolean isAutoJoinEnabled() { + return mIsAutoJoinEnabled; + } + + /** * Constructor for creating PasspointConfiguration with default values. */ public PasspointConfiguration() {} @@ -464,6 +500,7 @@ public final class PasspointConfiguration implements Parcelable { mServiceFriendlyNames = source.mServiceFriendlyNames; mAaaServerTrustedNames = source.mAaaServerTrustedNames; mCarrierId = source.mCarrierId; + mIsAutoJoinEnabled = source.mIsAutoJoinEnabled; } @Override @@ -493,6 +530,7 @@ public final class PasspointConfiguration implements Parcelable { (HashMap<String, String>) mServiceFriendlyNames); dest.writeBundle(bundle); dest.writeInt(mCarrierId); + dest.writeBoolean(mIsAutoJoinEnabled); } @Override @@ -523,6 +561,7 @@ public final class PasspointConfiguration implements Parcelable { && mUsageLimitDataLimit == that.mUsageLimitDataLimit && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes && mCarrierId == that.mCarrierId + && mIsAutoJoinEnabled == that.mIsAutoJoinEnabled && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null : mServiceFriendlyNames.equals(that.mServiceFriendlyNames)); } @@ -533,7 +572,7 @@ public final class PasspointConfiguration implements Parcelable { mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis, mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes, mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, - mServiceFriendlyNames, mCarrierId); + mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled); } @Override @@ -587,6 +626,7 @@ public final class PasspointConfiguration implements Parcelable { builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames); } builder.append("CarrierId:" + mCarrierId); + builder.append("IsAutoJoinEnabled:" + mIsAutoJoinEnabled); return builder.toString(); } @@ -692,6 +732,7 @@ public final class PasspointConfiguration implements Parcelable { "serviceFriendlyNames"); config.setServiceFriendlyNames(friendlyNamesMap); config.mCarrierId = in.readInt(); + config.mIsAutoJoinEnabled = in.readBoolean(); return config; } diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java index d58083c92df6..3c13562d6952 100644 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ b/wifi/java/com/android/server/wifi/BaseWifiService.java @@ -182,6 +182,11 @@ public class BaseWifiService extends IWifiManager.Stub { } @Override + public void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin) { + throw new UnsupportedOperationException(); + } + + @Override public boolean startScan(String packageName, String featureId) { throw new UnsupportedOperationException(); } @@ -584,4 +589,10 @@ public class BaseWifiService extends IWifiManager.Stub { public int calculateSignalLevel(int rssi) { throw new UnsupportedOperationException(); } + + @Override + public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( + List<ScanResult> scanResults) { + throw new UnsupportedOperationException(); + } } diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index 1f601036a718..eeea7e2a6cd8 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -25,8 +25,12 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Random; + @SmallTest public class SoftApConfigurationTest { + private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789"; + private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) { Parcel parcel = Parcel.obtain(); parcel.writeParcelable(configIn, 0); @@ -37,6 +41,25 @@ public class SoftApConfigurationTest { return configOut; } + /** + * Helper method to generate random string. + * + * Note: this method has limited use as a random string generator. + * The characters used in this method do no not cover all valid inputs. + * @param length number of characters to generate for the string + * @return String generated string of random characters + */ + private String generateRandomString(int length) { + Random random = new Random(); + StringBuilder stringBuilder = new StringBuilder(length); + int index = -1; + while (stringBuilder.length() < length) { + index = random.nextInt(TEST_CHAR_SET_AS_STRING.length()); + stringBuilder.append(TEST_CHAR_SET_AS_STRING.charAt(index)); + } + return stringBuilder.toString(); + } + @Test public void testBasicSettings() { SoftApConfiguration original = new SoftApConfiguration.Builder() @@ -45,7 +68,7 @@ public class SoftApConfigurationTest { .build(); assertThat(original.getSsid()).isEqualTo("ssid"); assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66")); - assertThat(original.getWpa2Passphrase()).isNull(); + assertThat(original.getPassphrase()).isNull(); assertThat(original.getSecurityType()).isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN); assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); assertThat(original.getChannel()).isEqualTo(0); @@ -66,9 +89,9 @@ public class SoftApConfigurationTest { @Test public void testWpa2() { SoftApConfiguration original = new SoftApConfiguration.Builder() - .setWpa2Passphrase("secretsecret") + .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) .build(); - assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret"); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); assertThat(original.getSecurityType()).isEqualTo( SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); @@ -90,19 +113,73 @@ public class SoftApConfigurationTest { @Test public void testWpa2WithAllFieldCustomized() { SoftApConfiguration original = new SoftApConfiguration.Builder() - .setWpa2Passphrase("secretsecret") - .setBand(SoftApConfiguration.BAND_ANY) + .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) .setChannel(149, SoftApConfiguration.BAND_5GHZ) .setHiddenSsid(true) .setMaxNumberOfClients(10) + .setShutdownTimeoutMillis(500000) .build(); - assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret"); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); assertThat(original.getSecurityType()).isEqualTo( SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); assertThat(original.getChannel()).isEqualTo(149); assertThat(original.isHiddenSsid()).isEqualTo(true); assertThat(original.getMaxNumberOfClients()).isEqualTo(10); + assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000); + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test + public void testWpa3Sae() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); + assertThat(original.getSecurityType()).isEqualTo( + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); + assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); + assertThat(original.getChannel()).isEqualTo(149); + assertThat(original.isHiddenSsid()).isEqualTo(true); + + + SoftApConfiguration unparceled = parcelUnparcel(original); + assertThat(unparceled).isNotSameAs(original); + assertThat(unparceled).isEqualTo(original); + assertThat(unparceled.hashCode()).isEqualTo(original.hashCode()); + + SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build(); + assertThat(copy).isNotSameAs(original); + assertThat(copy).isEqualTo(original); + assertThat(copy.hashCode()).isEqualTo(original.hashCode()); + } + + @Test + public void testWpa3SaeTransition() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + assertThat(original.getSecurityType()).isEqualTo( + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION); + assertThat(original.getPassphrase()).isEqualTo("secretsecret"); + assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); + assertThat(original.getChannel()).isEqualTo(149); + assertThat(original.isHiddenSsid()).isEqualTo(true); + SoftApConfiguration unparceled = parcelUnparcel(original); assertThat(unparceled).isNotSameAs(original); @@ -114,4 +191,51 @@ public class SoftApConfigurationTest { assertThat(copy).isEqualTo(original); assertThat(copy.hashCode()).isEqualTo(original.hashCode()); } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidShortPasswordLengthForWpa2() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1), + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidLongPasswordLengthForWpa2() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1), + SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidShortPasswordLengthForWpa3SaeTransition() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1), + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidLongPasswordLengthForWpa3SaeTransition() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1), + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .setChannel(149, SoftApConfiguration.BAND_5GHZ) + .setHiddenSsid(true) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalieShutdownTimeoutMillis() { + SoftApConfiguration original = new SoftApConfiguration.Builder() + .setShutdownTimeoutMillis(-1) + .build(); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index 909cfefba941..8689a38c6b17 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -16,6 +16,10 @@ package android.net.wifi; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OWE; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_SAE; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -328,4 +332,57 @@ public class WifiConfigurationTest { assertNotNull(NetworkSelectionStatus.DISABLE_REASON_INFOS.get(i)); } } + + /** + * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the + * {@link WifiConfiguration} object correctly for SAE security type. + * @throws Exception + */ + @Test + public void testSetSecurityParamsForSae() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + + config.setSecurityParams(SECURITY_TYPE_SAE); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(config.requirePMF); + } + + /** + * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the + * {@link WifiConfiguration} object correctly for OWE security type. + * @throws Exception + */ + @Test + public void testSetSecurityParamsForOwe() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + + config.setSecurityParams(SECURITY_TYPE_OWE); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(config.requirePMF); + } + + /** + * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the + * {@link WifiConfiguration} object correctly for Suite-B security type. + * @throws Exception + */ + @Test + public void testSetSecurityParamsForSuiteB() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + + config.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256)); + assertTrue(config.allowedGroupManagementCiphers + .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256)); + assertTrue(config.requirePMF); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index f9bd31d57ffc..4b837184dc9a 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -1672,10 +1672,22 @@ public class WifiManagerTest { @Test public void testAllowAutojoin() throws Exception { mWifiManager.allowAutojoin(1, true); - verify(mWifiService).allowAutojoin(eq(1), eq(true)); + verify(mWifiService).allowAutojoin(1, true); } /** + * Test behavior of {@link WifiManager#allowAutojoinPasspoint(String, boolean)} + * @throws Exception + */ + @Test + public void testAllowAutojoinPasspoint() throws Exception { + final String fqdn = "FullyQualifiedDomainName"; + mWifiManager.allowAutojoinPasspoint(fqdn, true); + verify(mWifiService).allowAutojoinPasspoint(fqdn, true); + } + + + /** * Test behavior of {@link WifiManager#disconnect()} */ @Test @@ -2173,4 +2185,18 @@ public class WifiManagerTest { result = WifiManager.parseDppChannelList(channelList); assertEquals(result.size(), 0); } + + /** + * Test getWifiConfigsForMatchedNetworkSuggestions for given scanResults. + */ + @Test + public void testGetWifiConfigsForMatchedNetworkSuggestions() throws Exception { + List<WifiConfiguration> testResults = new ArrayList<>(); + testResults.add(new WifiConfiguration()); + + when(mWifiService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any(List.class))) + .thenReturn(testResults); + assertEquals(testResults, mWifiManager + .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>())); + } } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index f501b16d2c79..94054fdde8da 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -171,6 +171,7 @@ public class PasspointConfigurationTest { assertFalse(config.validate()); assertFalse(config.validateForR2()); + assertTrue(config.isAutoJoinEnabled()); } /** |