diff options
354 files changed, 11708 insertions, 4380 deletions
diff --git a/Android.bp b/Android.bp index bba6185739fd..477f0272efa0 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, @@ -481,11 +483,12 @@ 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", + "ike-stubs", // TODO(jiyong): add more stubs for APEXes here ], sdk_version: "core_platform", @@ -1134,6 +1137,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/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/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/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/sdkext/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING index 91947f39980a..7e7762337afc 100644 --- a/apex/sdkext/TEST_MAPPING +++ b/apex/sdkextensions/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "CtsSdkExtTestCases" + }, + { + "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..5504f4e5dd8e 100644 --- a/apex/sdkext/framework/Android.bp +++ b/apex/sdkextensions/framework/Android.bp @@ -17,55 +17,58 @@ 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", + ], } 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/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 51641f23a49e..b673df0b5c90 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 @@ -13323,6 +13326,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 +13339,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 +25786,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 +28688,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 +28706,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 +29020,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); @@ -30280,6 +30320,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"; @@ -35682,7 +35723,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; } @@ -36350,15 +36393,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(); @@ -45465,6 +45515,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); @@ -45661,6 +45716,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); @@ -45701,8 +45757,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); @@ -45711,7 +45767,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); @@ -45919,6 +45975,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>); diff --git a/api/system-current.txt b/api/system-current.txt index 41ded16bff08..41e55902cb48 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -567,6 +567,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 +678,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 +801,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 +1655,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 +1706,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"; } @@ -3578,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 { @@ -4897,6 +4915,33 @@ package android.net { 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(); @@ -5927,6 +5972,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); @@ -6003,6 +6049,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"; @@ -6291,6 +6338,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(); @@ -6957,6 +7008,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(); @@ -7065,7 +7120,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 { @@ -7081,11 +7151,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 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(); @@ -7093,14 +7165,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 @@ -7336,6 +7415,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 { @@ -7913,7 +7996,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 { @@ -9180,30 +9327,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 { @@ -10381,6 +10535,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 { diff --git a/api/test-current.txt b/api/test-current.txt index 99679428cefb..e64cbdb6eb10 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 { diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 7b96ce92e307..118a508a9071 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -126,29 +126,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", + ], + shared_libs: [ + "android.hardware.health@2.0", + "libbinder", + "libgraphicsenv", + "libhidlbase", + "libincident", + "libservices", + "libstatsmetadata", "libtimestats_proto", "libutils", ], 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/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/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/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/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/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/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/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/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/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..9223ccd54251 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. 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/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..3ef86ed9f994 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -89,6 +89,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 */ 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..16f7b398731c 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.IUpdateEngine; @@ -140,8 +141,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}. @@ -419,4 +455,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..3ea64f13fe6b 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) { 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..07cbcfbb298b 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; @@ -12459,16 +12458,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 +12476,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 +12487,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 +12498,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 +12509,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 +12521,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 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/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/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/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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 522ff9a3bcb5..bf8dc65abe28 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; } @@ -7273,7 +7268,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/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/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/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/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/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..ea70dcf28e69 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" /> 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/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/symbols.xml b/core/res/res/values/symbols.xml index 01bd510dcf27..cdbf14bf969a 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" /> 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/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/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/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/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/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/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl index 02a381669893..51fa4eeaf4d8 100644 --- a/media/java/android/media/IMediaRoute2Provider.aidl +++ b/media/java/android/media/IMediaRoute2Provider.aidl @@ -25,7 +25,7 @@ import android.media.IMediaRoute2ProviderClient; oneway interface IMediaRoute2Provider { void setClient(IMediaRoute2ProviderClient client); void requestCreateSession(String packageName, String routeId, - String controlCategory, long requestId); + String routeType, long requestId); void releaseSession(int sessionId); void selectRoute(int sessionId, String routeId); 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..13640a438e7b 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -83,12 +83,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 +150,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 +158,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 +166,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,24 +182,18 @@ 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) { + /** + * @hide + */ + public static String toUniqueId(String providerId, String routeId) { return providerId + ":" + routeId; } @@ -235,7 +228,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 +240,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 +329,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 +374,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 +403,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 +441,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 +452,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 +469,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 +480,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 +574,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..91cc44807a73 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -246,11 +246,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. @@ -301,6 +301,25 @@ public abstract class MediaRoute2ProviderService extends Service { public abstract void onTransferToRoute(int 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. */ public final void updateProviderInfo(@NonNull MediaRoute2ProviderInfo providerInfo) { @@ -357,12 +376,12 @@ 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 diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index f5cfde4b968b..dea8b045e72a 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,26 @@ 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); + requestId, route, routeType); mSessionCreationRequests.add(request); Client2 client; @@ -379,7 +353,7 @@ public class MediaRouter2 { if (client != null) { try { mMediaRouterService.requestCreateSession( - client, route, controlCategory, requestId); + client, route, routeType, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to request to create session.", ex); mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler, @@ -461,36 +435,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 +443,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 +459,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 +475,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 +506,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 +534,7 @@ public class MediaRouter2 { + "(requested route's providerId=" + requestedRoute.getProviderId() + ", actual providerId=" + sessionInfo.getProviderId() + ")"); - notifySessionCreationFailed(requestedRoute, requestedControlCategory); + notifySessionCreationFailed(requestedRoute, requestedRouteType); return; } } @@ -666,24 +610,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 +655,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 +725,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. @@ -840,12 +801,12 @@ public class MediaRouter2 { } /** - * @return the category of routes that the session includes. + * @return the type of routes that the session includes. */ @NonNull - public String getControlCategory() { + public String getRouteType() { synchronized (mControllerLock) { - return mSessionInfo.getControlCategory(); + return mSessionInfo.getRouteType(); } } @@ -935,13 +896,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; } @@ -982,13 +943,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; } @@ -1029,14 +990,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; } @@ -1084,8 +1045,12 @@ public class MediaRouter2 { } } + /** + * TODO: Change this to package private. (Hidden for debugging purposes) + * @hide + */ @NonNull - RouteSessionInfo getRouteSessionInfo() { + public RouteSessionInfo getRouteSessionInfo() { synchronized (mControllerLock) { return mSessionInfo; } @@ -1100,11 +1065,11 @@ 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) { 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 +1082,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 +1137,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/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..cb1688600fac 100644 --- a/media/java/android/media/RouteSessionInfo.java +++ b/media/java/android/media/RouteSessionInfo.java @@ -48,7 +48,7 @@ public class RouteSessionInfo implements Parcelable { final int mSessionId; final String mPackageName; - final String mControlCategory; + final String mRouteType; @Nullable final String mProviderId; final List<String> mSelectedRoutes; @@ -63,7 +63,7 @@ public class RouteSessionInfo implements Parcelable { mSessionId = builder.mSessionId; mPackageName = builder.mPackageName; - mControlCategory = builder.mControlCategory; + mRouteType = builder.mRouteType; mProviderId = builder.mProviderId; mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes); @@ -79,7 +79,7 @@ public class RouteSessionInfo implements Parcelable { mSessionId = src.readInt(); mPackageName = ensureString(src.readString()); - mControlCategory = ensureString(src.readString()); + mRouteType = ensureString(src.readString()); mProviderId = src.readString(); mSelectedRoutes = ensureList(src.createStringArrayList()); @@ -154,7 +154,7 @@ public class RouteSessionInfo implements Parcelable { */ public boolean isValid() { return !TextUtils.isEmpty(mPackageName) - && !TextUtils.isEmpty(mControlCategory) + && !TextUtils.isEmpty(mRouteType) && mSelectedRoutes.size() > 0; } @@ -175,12 +175,12 @@ public class RouteSessionInfo implements Parcelable { } /** - * 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; } /** @@ -254,7 +254,7 @@ public class RouteSessionInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mSessionId); dest.writeString(mPackageName); - dest.writeString(mControlCategory); + dest.writeString(mRouteType); dest.writeString(mProviderId); dest.writeStringList(mSelectedRoutes); dest.writeStringList(mSelectableRoutes); @@ -268,7 +268,7 @@ public class RouteSessionInfo implements Parcelable { StringBuilder result = new StringBuilder() .append("RouteSessionInfo{ ") .append("sessionId=").append(mSessionId) - .append(", controlCategory=").append(mControlCategory) + .append(", routeType=").append(mRouteType) .append(", selectedRoutes={") .append(String.join(",", mSelectedRoutes)) .append("}") @@ -291,7 +291,7 @@ public class RouteSessionInfo implements Parcelable { public static final class Builder { final String mPackageName; final int mSessionId; - final String mControlCategory; + final String mRouteType; String mProviderId; final List<String> mSelectedRoutes; final List<String> mSelectableRoutes; @@ -300,11 +300,11 @@ public class RouteSessionInfo implements Parcelable { Bundle mControlHints; public Builder(int sessionId, @NonNull String packageName, - @NonNull String controlCategory) { + @NonNull String routeType) { mSessionId = sessionId; mPackageName = Objects.requireNonNull(packageName, "packageName must not be null"); - mControlCategory = Objects.requireNonNull(controlCategory, - "controlCategory must not be null"); + mRouteType = Objects.requireNonNull(routeType, + "routeType must not be null"); mSelectedRoutes = new ArrayList<>(); mSelectableRoutes = new ArrayList<>(); @@ -315,7 +315,7 @@ public class RouteSessionInfo implements Parcelable { public Builder(RouteSessionInfo sessionInfo) { mSessionId = sessionInfo.mSessionId; mPackageName = sessionInfo.mPackageName; - mControlCategory = sessionInfo.mControlCategory; + mRouteType = sessionInfo.mRouteType; mProviderId = sessionInfo.mProviderId; mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes); @@ -327,14 +327,30 @@ 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) { mProviderId = providerId; + convertToUniqueRouteIds(providerId, mSelectedRoutes); + convertToUniqueRouteIds(providerId, mSelectableRoutes); + convertToUniqueRouteIds(providerId, mDeselectableRoutes); + convertToUniqueRouteIds(providerId, mTransferrableRoutes); return this; } + private void convertToUniqueRouteIds(@NonNull String providerId, + @NonNull List<String> routeIds) { + for (int i = 0; i < routeIds.size(); i++) { + String routeId = routeIds.get(i); + routeIds.set(i, MediaRoute2Info.toUniqueId(providerId, routeId)); + } + } + /** * Clears the selected routes. */ 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/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/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 43f9a8973e80..173551812f3a 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -21,7 +21,6 @@ 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; @@ -31,7 +30,9 @@ 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; import android.media.tv.tuner.filter.FilterEvent; +import android.media.tv.tuner.filter.Settings; import android.media.tv.tuner.frontend.FrontendCallback; import android.media.tv.tuner.frontend.FrontendInfo; import android.media.tv.tuner.frontend.FrontendStatus; @@ -123,6 +124,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); @@ -587,6 +589,127 @@ public final class Tuner implements AutoCloseable { return filter; } + /** + * 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 { + 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. + * + * @return result status of the operation. + */ + @Result + public int close() { + return nativeClose(); + } + } + + /** + * 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; diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index d24e582a8b21..e02443202085 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -227,41 +227,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/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java new file mode 100644 index 000000000000..4d02b849899d --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * 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 TunerConstants.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..930ca744a168 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java @@ -0,0 +1,44 @@ +/* + * 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.Nullable; +import android.media.tv.tuner.TunerConstants.FilterType; + +/** + * 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; + } +} 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..2c706c0ce9ce --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.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.filter; + +import android.media.tv.tuner.TunerConstants; + +/** + * 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 TunerConstants.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..f70e70a5bdb2 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.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; + +/** + * 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 TunerConstants.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..3d2c265cc00b --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/PesSettings.java @@ -0,0 +1,79 @@ +/* + * 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 PES Data. + * @hide + */ +public 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); + } + } +} 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..789ed0895750 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/Settings.java @@ -0,0 +1,37 @@ +/* + * 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 { + protected final int mType; + + protected Settings(int type) { + mType = type; + } + + /** + * Gets filter settings type. + * @return + */ + 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..1f8bcb3b4a71 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.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; + +import android.media.tv.tuner.TunerConstants; + +/** + * 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 TunerConstants.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..103698cd0d75 --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java @@ -0,0 +1,75 @@ +/* + * 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; + +/** + * Filter configuration for a TS filter. + * @hide + */ +public 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 TsFilterConfiguration.Builder newBuilder() { + return new TsFilterConfiguration.Builder(); + } + + /** + * Builder for TsFilterConfiguration. + */ + public static class Builder { + private Settings mSettings; + private int mTpid; + + /** + * Sets settings. + */ + public TsFilterConfiguration.Builder setSettings(Settings settings) { + mSettings = settings; + return this; + } + + /** + * Sets TPID. + */ + public TsFilterConfiguration.Builder setTpid(int tpid) { + mTpid = tpid; + return this; + } + + /** + * Builds a TsFilterConfiguration instance. + */ + public TsFilterConfiguration build() { + return new TsFilterConfiguration(mSettings, mTpid); + } + } +} 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/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/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/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java index 04fccc7e0f94..ec177321bba3 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,10 +58,10 @@ 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<>(); @@ -69,38 +69,38 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService 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)) { @@ -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) diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java index 6fe847bf5f3a..af69c7e8699f 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,8 @@ 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 +409,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 +430,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 +459,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 +477,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(); } @@ -522,11 +525,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 +557,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 +573,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(); } @@ -603,11 +610,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 +639,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 +656,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(); } @@ -667,11 +674,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 +708,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 +730,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 +747,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 +758,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 +770,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..c23a5b0d76e3 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.category.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/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/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/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 3c3ebe2fe228..1ee85188eac3 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -110,6 +110,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; @@ -276,6 +277,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, NotificationEntryManager notificationEntryManager, + NotificationRowContentBinder notificationRowContentBinder, NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, @@ -363,6 +365,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt notificationGutsManager, notificationLogger, notificationEntryManager, + notificationRowContentBinder, notificationInterruptionStateProvider, notificationViewHierarchyManager, keyguardViewMediator, diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index a1eccceea771..7108e65c8bce 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; @@ -146,6 +147,7 @@ public class CarStatusBarModule { NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, NotificationEntryManager notificationEntryManager, + NotificationRowContentBinder notificationRowContentBinder, NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, @@ -232,6 +234,7 @@ public class CarStatusBarModule { notificationGutsManager, notificationLogger, notificationEntryManager, + notificationRowContentBinder, notificationInterruptionStateProvider, notificationViewHierarchyManager, keyguardViewMediator, 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/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/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/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/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/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/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/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 43d0399c6d62..667e721ae37d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -584,7 +584,15 @@ public class NotificationRemoteInputManager implements Dumpable { public void bindRow(ExpandableNotificationRow row) { row.setRemoteInputController(mRemoteInputController); - row.setRemoteViewClickHandler(mOnClickHandler); + } + + /** + * Return on-click handler for notification remote views + * + * @return on-click handler + */ + public RemoteViews.OnClickHandler getRemoteViewsOnClickHandler() { + return mOnClickHandler; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java index d1f6ebf3826d..ec8dbead7de2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import android.content.Context; +import com.android.systemui.statusbar.notification.row.NotificationRowModule; + import javax.inject.Singleton; import dagger.Module; @@ -26,7 +28,7 @@ import dagger.Provides; /** * Dagger Module providing common dependencies of StatusBar. */ -@Module +@Module(includes = {NotificationRowModule.class}) public class StatusBarDependenciesModule { /** * Provides our instance of CommandQueue which is considered optional. 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/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..28e486df29b9 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 @@ -576,6 +579,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/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index 6c93618e5395..20f206b91f10 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 @@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -65,6 +65,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { Dependency.get(NotificationInterruptionStateProvider.class); private final Context mContext; + private final NotificationRowContentBinder mRowContentBinder; private final NotificationMessagingUtil mMessagingUtil; private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = this::logNotificationExpansion; @@ -76,7 +77,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; private HeadsUpManager mHeadsUpManager; - private NotificationContentInflater.InflationCallback mInflationCallback; + private NotificationRowContentBinder.InflationCallback mInflationCallback; private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; private BindRowCallback mBindRowCallback; private NotificationClicker mNotificationClicker; @@ -84,11 +85,13 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { public NotificationRowBinderImpl( Context context, + NotificationRowContentBinder rowContentBinder, boolean allowLongPress, KeyguardBypassController keyguardBypassController, StatusBarStateController statusBarStateController, NotificationLogger logger) { mContext = context; + mRowContentBinder = rowContentBinder; mMessagingUtil = new NotificationMessagingUtil(context); mAllowLongPress = allowLongPress; mKeyguardBypassController = keyguardBypassController; @@ -109,16 +112,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(NotificationRowContentBinder.InflationCallback callback) { + mInflationCallback = callback; + } + public void setNotificationClicker(NotificationClicker clicker) { mNotificationClicker = clicker; } @@ -154,19 +159,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private void bindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row, Runnable onDismissRunnable) { - row.setExpansionLogger(mExpansionLogger, entry.getSbn().getKey()); - row.setBypassController(mKeyguardBypassController); - row.setStatusBarStateController(mStatusBarStateController); - row.setGroupManager(mGroupManager); - row.setHeadsUpManager(mHeadsUpManager); - row.setOnExpandClickListener(mPresenter); - row.setInflationCallback(mInflationCallback); - if (mAllowLongPress) { - row.setLongPressListener(mGutsManager::openGuts); - } - mListContainer.bindRow(row); - getRemoteInputManager().bindRow(row); - // Get the app name. // Note that Notification.Builder#bindHeaderAppName has similar logic // but since this field is used in the guts, it must be accurate. @@ -184,15 +176,33 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { } catch (PackageManager.NameNotFoundException e) { // Do nothing } - row.setAppName(appname); + + row.initialize( + appname, + sbn.getKey(), + mExpansionLogger, + mKeyguardBypassController, + mGroupManager, + mHeadsUpManager, + mRowContentBinder, + mPresenter); + + // TODO: Either move these into ExpandableNotificationRow#initialize or out of row entirely + row.setStatusBarStateController(mStatusBarStateController); + row.setInflationCallback(mInflationCallback); + row.setAppOpsOnClickListener(mOnAppOpsClickListener); + if (mAllowLongPress) { + row.setLongPressListener(mGutsManager::openGuts); + } + mListContainer.bindRow(row); + getRemoteInputManager().bindRow(row); + row.setOnDismissRunnable(onDismissRunnable); row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (ENABLE_REMOTE_INPUT) { row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); } - row.setAppOpsOnClickListener(mOnAppOpsClickListener); - mBindRowCallback.onBindRow(entry, pmUser, sbn, row); } 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/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/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 3c247df692f4..a8a35d07b3f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -65,7 +65,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.Chronometer; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -150,7 +149,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private StatusBarStateController mStatusbarStateController; private KeyguardBypassController mBypassController; private LayoutListener mLayoutListener; - private final NotificationContentInflater mNotificationInflater; + private NotificationRowContentBinder mNotificationContentBinder; private int mIconTransformContentShift; private int mIconTransformContentShiftNoIcon; private int mMaxHeadsUpHeightBeforeN; @@ -464,7 +463,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * Inflate views based off the inflation flags set. Inflation happens asynchronously. */ public void inflateViews() { - mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams, + mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams, false /* forceInflate */, mInflationCallback); } @@ -478,7 +477,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // View should not be reinflated in the future clearInflationFlags(inflationFlag); Runnable freeViewRunnable = - () -> mNotificationInflater.unbindContent(mEntry, this, inflationFlag); + () -> mNotificationContentBinder.unbindContent(mEntry, this, inflationFlag); switch (inflationFlag) { case FLAG_CONTENT_VIEW_HEADS_UP: getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP, @@ -742,23 +741,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mIsHeadsUp || mHeadsupDisappearRunning; } - - public void setGroupManager(NotificationGroupManager groupManager) { - mGroupManager = groupManager; - mPrivateLayout.setGroupManager(groupManager); - } - public void setRemoteInputController(RemoteInputController r) { mPrivateLayout.setRemoteInputController(r); } - public void setAppName(String appName) { - mAppName = appName; - if (mMenuRow != null && mMenuRow.getMenuView() != null) { - mMenuRow.setAppName(mAppName); - } - } - public void addChildNotification(ExpandableNotificationRow row) { addChildNotification(row, -1); } @@ -852,7 +838,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mIsChildInGroup = isChildInGroup; if (mIsLowPriority) { int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED; - mNotificationInflater.bindContent(mEntry, this, flags, mBindParams, + mNotificationContentBinder.bindContent(mEntry, this, flags, mBindParams, false /* forceInflate */, mInflationCallback); } } @@ -1105,10 +1091,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mPrivateLayout.getContractedNotificationHeader(); } - public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) { - mOnExpandClickListener = onExpandClickListener; - } - public void setLongPressListener(LongPressListener longPressListener) { mLongPressListener = longPressListener; } @@ -1131,10 +1113,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - public void setHeadsUpManager(HeadsUpManager headsUpManager) { - mHeadsUpManager = headsUpManager; - } - public HeadsUpManager getHeadsUpManager() { return mHeadsUpManager; } @@ -1259,7 +1237,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView l.reInflateViews(); } mEntry.getSbn().clearPackageContext(); - mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams, + mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams, true /* forceInflate */, mInflationCallback); } @@ -1634,10 +1612,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mBindParams.usesIncreasedHeadsUpHeight = use; } - public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) { - mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler); - } - /** * Set callback for notification content inflation * @@ -1652,7 +1626,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNeedsRedaction = needsRedaction; if (needsRedaction) { setInflationFlags(FLAG_CONTENT_VIEW_PUBLIC); - mNotificationInflater.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC, + mNotificationContentBinder.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC, mBindParams, false /* forceInflate */, mInflationCallback); } else { clearInflationFlags(FLAG_CONTENT_VIEW_PUBLIC); @@ -1661,18 +1635,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - @VisibleForTesting - public NotificationContentInflater getNotificationInflater() { - return mNotificationInflater; - } - public interface ExpansionLogger { void logNotificationExpansion(String key, boolean userAction, boolean expanded); } public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); - mNotificationInflater = new NotificationContentInflater(); mMenuRow = new NotificationMenuRow(mContext); mImageResolver = new NotificationInlineImageResolver(context, new NotificationInlineImageCache()); @@ -1680,8 +1648,30 @@ public class ExpandableNotificationRow extends ActivatableNotificationView initDimens(); } - public void setBypassController(KeyguardBypassController bypassController) { + /** + * Initialize row. + */ + public void initialize( + String appName, + String notificationKey, + ExpansionLogger logger, + KeyguardBypassController bypassController, + NotificationGroupManager groupManager, + HeadsUpManager headsUpManager, + NotificationRowContentBinder rowContentBinder, + OnExpandClickListener onExpandClickListener) { + mAppName = appName; + if (mMenuRow != null && mMenuRow.getMenuView() != null) { + mMenuRow.setAppName(mAppName); + } + mLogger = logger; + mLoggingKey = notificationKey; mBypassController = bypassController; + mGroupManager = groupManager; + mPrivateLayout.setGroupManager(groupManager); + mHeadsUpManager = headsUpManager; + mNotificationContentBinder = rowContentBinder; + mOnExpandClickListener = onExpandClickListener; } public void setStatusBarStateController(StatusBarStateController statusBarStateController) { @@ -2920,11 +2910,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return 0; } - public void setExpansionLogger(ExpansionLogger logger, String key) { - mLogger = logger; - mLoggingKey = key; - } - public void onExpandedByGesture(boolean userExpanded) { int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER; if (mGroupManager.isSummaryOfGroup(mEntry.getSbn())) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java new file mode 100644 index 000000000000..c11c60fcdd04 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java @@ -0,0 +1,75 @@ +/* + * 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.row; + +import android.widget.RemoteViews; + +import androidx.annotation.Nullable; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; + +/** + * Caches {@link RemoteViews} for a notification's content views. + */ +public interface NotifRemoteViewCache { + + /** + * Whether the notification has the remote view cached + * + * @param entry notification + * @param flag inflation flag for content view + * @return true if the remote view is cached + */ + boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag); + + /** + * Get the remote view for the content flag specified. + * + * @param entry notification + * @param flag inflation flag for the content view + * @return the remote view if it is cached, null otherwise + */ + @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag); + + /** + * Cache a remote view for a given content flag on a notification. + * + * @param entry notification + * @param flag inflation flag for the content view + * @param remoteView remote view to store + */ + void putCachedView( + NotificationEntry entry, + @InflationFlag int flag, + RemoteViews remoteView); + + /** + * Remove a cached remote view for a given content flag on a notification. + * + * @param entry notification + * @param flag inflation flag for the content view + */ + void removeCachedView(NotificationEntry entry, @InflationFlag int flag); + + /** + * Clear a notification's remote view cache. + * + * @param entry notification + */ + void clearCache(NotificationEntry entry); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java new file mode 100644 index 000000000000..a19099a6ea52 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java @@ -0,0 +1,100 @@ +/* + * 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.row; + +import android.util.ArrayMap; +import android.util.SparseArray; +import android.widget.RemoteViews; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.statusbar.NotificationVisibility; +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.notification.row.NotificationRowContentBinder.InflationFlag; + +import java.util.Map; + +import javax.inject.Inject; + +/** + * Implementation of remote view cache that keeps remote views cached for all active notifications. + */ +public class NotifRemoteViewCacheImpl implements NotifRemoteViewCache { + private final Map<NotificationEntry, SparseArray<RemoteViews>> mNotifCachedContentViews = + new ArrayMap<>(); + + @Inject + NotifRemoteViewCacheImpl(NotificationEntryManager entryManager) { + entryManager.addNotificationEntryListener(mEntryListener); + } + + @Override + public boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag) { + return getCachedView(entry, flag) != null; + } + + @Override + public @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag) { + return getContentViews(entry).get(flag); + } + + @Override + public void putCachedView( + NotificationEntry entry, + @InflationFlag int flag, + RemoteViews remoteView) { + getContentViews(entry).put(flag, remoteView); + } + + @Override + public void removeCachedView(NotificationEntry entry, @InflationFlag int flag) { + getContentViews(entry).remove(flag); + } + + @Override + public void clearCache(NotificationEntry entry) { + getContentViews(entry).clear(); + } + + private @NonNull SparseArray<RemoteViews> getContentViews(NotificationEntry entry) { + SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry); + if (contentViews == null) { + throw new IllegalStateException( + String.format("Remote view cache was never created for notification %s", + entry.getKey())); + } + return contentViews; + } + + private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { + @Override + public void onPendingEntryAdded(NotificationEntry entry) { + mNotifCachedContentViews.put(entry, new SparseArray<>()); + } + + @Override + public void onEntryRemoved( + NotificationEntry entry, + @Nullable NotificationVisibility visibility, + boolean removedByUser) { + mNotifCachedContentViews.remove(entry); + } + }; +} 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..e1a6747b5398 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 @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED; import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP; @@ -26,7 +27,6 @@ import android.content.Context; import android.os.AsyncTask; import android.os.CancellationSignal; import android.service.notification.StatusBarNotification; -import android.util.ArrayMap; import android.util.Log; import android.view.View; import android.widget.RemoteViews; @@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ImageMessageConsumer; import com.android.systemui.Dependency; import com.android.systemui.statusbar.InflationTask; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.MediaNotificationProcessor; @@ -49,17 +50,30 @@ import com.android.systemui.util.Assert; import java.util.HashMap; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by * asynchronously building the content's {@link RemoteViews} and applying it to the row. */ +@Singleton +@VisibleForTesting(visibility = PACKAGE) public class NotificationContentInflater implements NotificationRowContentBinder { public static final String TAG = "NotifContentInflater"; - private RemoteViews.OnClickHandler mRemoteViewClickHandler; private boolean mInflateSynchronously = false; - private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>(); + private final NotificationRemoteInputManager mRemoteInputManager; + private final NotifRemoteViewCache mRemoteViewCache; + + @Inject + public NotificationContentInflater( + NotifRemoteViewCache remoteViewCache, + NotificationRemoteInputManager remoteInputManager) { + mRemoteViewCache = remoteViewCache; + mRemoteInputManager = remoteInputManager; + } @Override public void bindContent( @@ -76,27 +90,27 @@ public class NotificationContentInflater implements NotificationRowContentBinder return; } - StatusBarNotification sbn = row.getEntry().getSbn(); + StatusBarNotification sbn = entry.getSbn(); // To check if the notification has inline image and preload inline image if necessary. row.getImageResolver().preloadImages(sbn.getNotification()); if (forceInflate) { - mCachedContentViews.clear(); + mRemoteViewCache.clearCache(entry); } AsyncInflationTask task = new AsyncInflationTask( - sbn, mInflateSynchronously, contentToBind, - mCachedContentViews, + mRemoteViewCache, + entry, row, bindParams.isLowPriority, bindParams.isChildInGroup, bindParams.usesIncreasedHeight, bindParams.usesIncreasedHeadsUpHeight, callback, - mRemoteViewClickHandler); + mRemoteInputManager.getRemoteViewsOnClickHandler()); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); } else { @@ -123,13 +137,15 @@ public class NotificationContentInflater implements NotificationRowContentBinder result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(), packageContext, row.getHeadsUpManager(), row.getExistingSmartRepliesAndActions()); + apply( inflateSynchronously, result, reInflateFlags, - mCachedContentViews, + mRemoteViewCache, + entry, row, - mRemoteViewClickHandler, + mRemoteInputManager.getRemoteViewsOnClickHandler(), null); return result; } @@ -149,7 +165,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder int curFlag = 1; while (contentToUnbind != 0) { if ((contentToUnbind & curFlag) != 0) { - freeNotificationView(row, curFlag); + freeNotificationView(entry, row, curFlag); } contentToUnbind &= ~curFlag; curFlag = curFlag << 1; @@ -157,34 +173,25 @@ public class NotificationContentInflater implements NotificationRowContentBinder } /** - * Set click handler for notification remote views - * - * @param remoteViewClickHandler click handler for remote views - */ - public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) { - mRemoteViewClickHandler = remoteViewClickHandler; - } - - /** * Frees the content view associated with the inflation flag. Will only succeed if the * view is safe to remove. * * @param inflateFlag the flag corresponding to the content view which should be freed */ - private void freeNotificationView(ExpandableNotificationRow row, + private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row, @InflationFlag int inflateFlag) { switch (inflateFlag) { case FLAG_CONTENT_VIEW_HEADS_UP: if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) { row.getPrivateLayout().setHeadsUpChild(null); - mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP); + mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP); row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null); } break; case FLAG_CONTENT_VIEW_PUBLIC: if (row.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) { row.getPublicLayout().setContractedChild(null); - mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC); + mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC); } break; case FLAG_CONTENT_VIEW_CONTRACTED: @@ -245,11 +252,12 @@ public class NotificationContentInflater implements NotificationRowContentBinder return result; } - public static CancellationSignal apply( + private static CancellationSignal apply( boolean inflateSynchronously, InflationProgress result, @InflationFlag int reInflateFlags, - ArrayMap<Integer, RemoteViews> cachedContentViews, + NotifRemoteViewCache remoteViewCache, + NotificationEntry entry, ExpandableNotificationRow row, RemoteViews.OnClickHandler remoteViewClickHandler, @Nullable InflationCallback callback) { @@ -261,7 +269,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((reInflateFlags & flag) != 0) { boolean isNewView = !canReapplyRemoteView(result.newContentView, - cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED)); + remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -273,8 +281,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder return result.newContentView; } }; - applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews, - row, isNewView, remoteViewClickHandler, callback, privateLayout, + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, + entry, row, isNewView, remoteViewClickHandler, callback, privateLayout, privateLayout.getContractedChild(), privateLayout.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_CONTRACTED), runningInflations, applyCallback); @@ -285,7 +293,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if (result.newExpandedView != null) { boolean isNewView = !canReapplyRemoteView(result.newExpandedView, - cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED)); + remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -297,8 +305,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder return result.newExpandedView; } }; - applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, - cachedContentViews, row, isNewView, remoteViewClickHandler, + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, + entry, row, isNewView, remoteViewClickHandler, callback, privateLayout, privateLayout.getExpandedChild(), privateLayout.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations, @@ -311,7 +319,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if (result.newHeadsUpView != null) { boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView, - cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP)); + remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -323,8 +331,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder return result.newHeadsUpView; } }; - applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, - cachedContentViews, row, isNewView, remoteViewClickHandler, + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, + entry, row, isNewView, remoteViewClickHandler, callback, privateLayout, privateLayout.getHeadsUpChild(), privateLayout.getVisibleWrapper( VISIBLE_TYPE_HEADSUP), runningInflations, @@ -336,7 +344,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((reInflateFlags & flag) != 0) { boolean isNewView = !canReapplyRemoteView(result.newPublicView, - cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC)); + remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -348,15 +356,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder return result.newPublicView; } }; - applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews, - row, isNewView, remoteViewClickHandler, callback, + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, + entry, row, isNewView, remoteViewClickHandler, callback, publicLayout, publicLayout.getContractedChild(), publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED), runningInflations, applyCallback); } // Let's try to finish, maybe nobody is even inflating anything - finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row); + finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, callback, entry, + row); CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( () -> runningInflations.values().forEach(CancellationSignal::cancel)); @@ -369,7 +378,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder final InflationProgress result, final @InflationFlag int reInflateFlags, @InflationFlag int inflationId, - final ArrayMap<Integer, RemoteViews> cachedContentViews, + final NotifRemoteViewCache remoteViewCache, + final NotificationEntry entry, final ExpandableNotificationRow row, boolean isNewView, RemoteViews.OnClickHandler remoteViewClickHandler, @@ -397,7 +407,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()); @@ -422,8 +432,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder existingWrapper.onReinflated(); } runningInflations.remove(inflationId); - finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, - callback, row); + finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, + callback, entry, row); } @Override @@ -448,7 +458,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 +484,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) { @@ -488,11 +498,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder * @return true if the inflation was finished */ private static boolean finishIfDone(InflationProgress result, - @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews, + @InflationFlag int reInflateFlags, NotifRemoteViewCache remoteViewCache, HashMap<Integer, CancellationSignal> runningInflations, - @Nullable InflationCallback endListener, ExpandableNotificationRow row) { + @Nullable InflationCallback endListener, NotificationEntry entry, + ExpandableNotificationRow row) { Assert.isMainThread(); - NotificationEntry entry = row.getEntry(); NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); if (runningInflations.isEmpty()) { @@ -500,23 +510,27 @@ public class NotificationContentInflater implements NotificationRowContentBinder if (result.inflatedContentView != null) { // New view case privateLayout.setContractedChild(result.inflatedContentView); - cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView); - } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) { + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED, + result.newContentView); + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) { // Reinflation case. Only update if it's still cached (i.e. view has not been // freed while inflating). - cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView); + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED, + result.newContentView); } } if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) { if (result.inflatedExpandedView != null) { privateLayout.setExpandedChild(result.inflatedExpandedView); - cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView); + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED, + result.newExpandedView); } else if (result.newExpandedView == null) { privateLayout.setExpandedChild(null); - cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, null); - } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED) != null) { - cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView); + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED); + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) { + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED, + result.newExpandedView); } if (result.newExpandedView != null) { privateLayout.setExpandedInflatedSmartReplies( @@ -530,12 +544,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) { if (result.inflatedHeadsUpView != null) { privateLayout.setHeadsUpChild(result.inflatedHeadsUpView); - cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView); + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP, + result.newHeadsUpView); } else if (result.newHeadsUpView == null) { privateLayout.setHeadsUpChild(null); - cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, null); - } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP) != null) { - cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView); + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP); + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) { + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP, + result.newHeadsUpView); } if (result.newHeadsUpView != null) { privateLayout.setHeadsUpInflatedSmartReplies( @@ -548,16 +564,18 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { if (result.inflatedPublicView != null) { publicLayout.setContractedChild(result.inflatedPublicView); - cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView); - } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC) != null) { - cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView); + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC, + result.newPublicView); + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) { + remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC, + result.newPublicView); } } entry.headsUpStatusBarText = result.headsUpStatusBarText; entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic; if (endListener != null) { - endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags); + endListener.onAsyncInflationFinished(entry, reInflateFlags); } return true; } @@ -615,7 +633,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress> implements InflationCallback, InflationTask { - private final StatusBarNotification mSbn; + private final NotificationEntry mEntry; private final Context mContext; private final boolean mInflateSynchronously; private final boolean mIsLowPriority; @@ -624,17 +642,17 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final InflationCallback mCallback; private final boolean mUsesIncreasedHeadsUpHeight; private @InflationFlag int mReInflateFlags; - private final ArrayMap<Integer, RemoteViews> mCachedContentViews; + private final NotifRemoteViewCache mRemoteViewCache; private ExpandableNotificationRow mRow; private Exception mError; private RemoteViews.OnClickHandler mRemoteViewClickHandler; private CancellationSignal mCancellationSignal; private AsyncInflationTask( - StatusBarNotification notification, boolean inflateSynchronously, @InflationFlag int reInflateFlags, - ArrayMap<Integer, RemoteViews> cachedContentViews, + NotifRemoteViewCache cache, + NotificationEntry entry, ExpandableNotificationRow row, boolean isLowPriority, boolean isChildInGroup, @@ -642,11 +660,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder boolean usesIncreasedHeadsUpHeight, InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler) { + mEntry = entry; mRow = row; - mSbn = notification; mInflateSynchronously = inflateSynchronously; mReInflateFlags = reInflateFlags; - mCachedContentViews = cachedContentViews; + mRemoteViewCache = cache; mContext = mRow.getContext(); mIsLowPriority = isLowPriority; mIsChildInGroup = isChildInGroup; @@ -654,7 +672,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight; mRemoteViewClickHandler = remoteViewClickHandler; mCallback = callback; - NotificationEntry entry = row.getEntry(); entry.setInflationTask(this); } @@ -667,12 +684,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder @Override protected InflationProgress doInBackground(Void... params) { try { + final StatusBarNotification sbn = mEntry.getSbn(); final Notification.Builder recoveredBuilder = Notification.Builder.recoverBuilder(mContext, - mSbn.getNotification()); + sbn.getNotification()); - Context packageContext = mSbn.getPackageContext(mContext); - Notification notification = mSbn.getNotification(); + Context packageContext = sbn.getPackageContext(mContext); + Notification notification = sbn.getNotification(); if (notification.isMediaNotification()) { MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext, packageContext); @@ -681,7 +699,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); - return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(), + return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, mRow.getContext(), packageContext, mRow.getHeadsUpManager(), mRow.getExistingSmartRepliesAndActions()); } catch (Exception e) { @@ -694,20 +712,20 @@ public class NotificationContentInflater implements NotificationRowContentBinder protected void onPostExecute(InflationProgress result) { if (mError == null) { mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags, - mCachedContentViews, mRow, mRemoteViewClickHandler, this); + mRemoteViewCache, mEntry, mRow, mRemoteViewClickHandler, this); } else { handleError(mError); } } private void handleError(Exception e) { - mRow.getEntry().onInflationTaskFinished(); - StatusBarNotification sbn = mRow.getEntry().getSbn(); + mEntry.onInflationTaskFinished(); + StatusBarNotification sbn = mEntry.getSbn(); final String ident = sbn.getPackageName() + "/0x" + 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,17 +747,17 @@ public class NotificationContentInflater implements NotificationRowContentBinder } @Override - public void handleInflationException(StatusBarNotification notification, Exception e) { + public void handleInflationException(NotificationEntry entry, Exception e) { handleError(e); } @Override public void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags) { - mRow.getEntry().onInflationTaskFinished(); + mEntry.onInflationTaskFinished(); mRow.onNotificationUpdated(); if (mCallback != null) { - mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags); + mCallback.onAsyncInflationFinished(mEntry, inflatedFlags); } // Notify the resolver that the inflation task has finished, 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/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java new file mode 100644 index 000000000000..df8653cf2406 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -0,0 +1,44 @@ +/* + * 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.row; + +import javax.inject.Singleton; + +import dagger.Binds; +import dagger.Module; + +/** + * Dagger Module containing notification row and view inflation implementations. + */ +@Module +public abstract class NotificationRowModule { + /** + * Provides notification row content binder instance. + */ + @Binds + @Singleton + public abstract NotificationRowContentBinder provideNotificationRowContentBinder( + NotificationContentInflater contentBinderImpl); + + /** + * Provides notification remote view cache instance. + */ + @Binds + @Singleton + public abstract NotifRemoteViewCache provideNotifRemoteViewCache( + NotifRemoteViewCacheImpl cacheImpl); +} 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/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java index fe0739f9088c..896b6e570da2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java @@ -32,7 +32,6 @@ import com.android.systemui.statusbar.InflationTask; 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.notification.row.NotificationContentInflater.AsyncInflationTask; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup; import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; @@ -428,7 +427,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * The notification is still pending inflation but we've decided that we no longer need * the content view (e.g. suppression might have changed and we decided we need to transfer * back). However, there is no way to abort just this inflation if other inflation requests - * have started (see {@link AsyncInflationTask#supersedeTask(InflationTask)}). So instead + * have started (see {@link InflationTask#supersedeTask(InflationTask)}). So instead * we just flag it as aborted and free when it's inflated. */ boolean mAbortOnInflation; 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/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 277a7613aafb..30825ed65eb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -205,6 +205,7 @@ import com.android.systemui.statusbar.notification.collection.init.NewNotifPipel import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; @@ -413,6 +414,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final NotificationGutsManager mGutsManager; private final NotificationLogger mNotificationLogger; private final NotificationEntryManager mEntryManager; + private final NotificationRowContentBinder mRowContentBinder; private NotificationListController mNotificationListController; private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final NotificationViewHierarchyManager mViewHierarchyManager; @@ -634,6 +636,7 @@ public class StatusBar extends SystemUI implements DemoMode, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, NotificationEntryManager notificationEntryManager, + NotificationRowContentBinder notificationRowContentBinder, NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, @@ -715,6 +718,7 @@ public class StatusBar extends SystemUI implements DemoMode, mGutsManager = notificationGutsManager; mNotificationLogger = notificationLogger; mEntryManager = notificationEntryManager; + mRowContentBinder = notificationRowContentBinder; mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mViewHierarchyManager = notificationViewHierarchyManager; mKeyguardViewMediator = keyguardViewMediator; @@ -1240,6 +1244,7 @@ public class StatusBar extends SystemUI implements DemoMode, final NotificationRowBinderImpl rowBinder = new NotificationRowBinderImpl( mContext, + mRowContentBinder, mAllowNotificationLongPress, mKeyguardBypassController, mStatusBarStateController, @@ -1272,7 +1277,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 +1291,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/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java index 153ca22933a9..df741079de29 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java @@ -69,6 +69,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -125,6 +126,7 @@ public class StatusBarModule { NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, NotificationEntryManager notificationEntryManager, + NotificationRowContentBinder notificationRowContentBinder, NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, @@ -207,6 +209,7 @@ public class StatusBarModule { notificationGutsManager, notificationLogger, notificationEntryManager, + notificationRowContentBinder, notificationInterruptionStateProvider, notificationViewHierarchyManager, keyguardViewMediator, 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/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..0c22aaeaafb9 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 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..3fdbd3edfcaa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -47,6 +47,9 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; +import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; @@ -72,6 +75,7 @@ public class NotificationTestHelper { public static final UserHandle USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser()); private static final String GROUP_KEY = "gruKey"; + private static final String APP_NAME = "appName"; private final Context mContext; private int mId; @@ -303,9 +307,6 @@ public class NotificationTestHelper { null /* root */, false /* attachToRoot */); ExpandableNotificationRow row = mRow; - row.setGroupManager(mGroupManager); - row.setHeadsUpManager(mHeadsUpManager); - row.setAboveShelfChangedListener(aboveShelf -> {}); final NotificationChannel channel = new NotificationChannel( @@ -329,6 +330,23 @@ public class NotificationTestHelper { entry.setRow(row); entry.createIcons(mContext, entry.getSbn()); row.setEntry(entry); + + NotificationContentInflater contentBinder = new NotificationContentInflater( + mock(NotifRemoteViewCache.class), + mock(NotificationRemoteInputManager.class)); + contentBinder.setInflateSynchronously(true); + + row.initialize( + APP_NAME, + entry.getKey(), + mock(ExpansionLogger.class), + mock(KeyguardBypassController.class), + mGroupManager, + mHeadsUpManager, + contentBinder, + mock(OnExpandClickListener.class)); + row.setAboveShelfChangedListener(aboveShelf -> { }); + row.setInflationFlags(extraInflationFlags); inflateAndWait(row); @@ -341,11 +359,10 @@ public class NotificationTestHelper { private static void inflateAndWait(ExpandableNotificationRow row) throws Exception { CountDownLatch countDownLatch = new CountDownLatch(1); - row.getNotificationInflater().setInflateSynchronously(true); NotificationContentInflater.InflationCallback callback = new NotificationContentInflater.InflationCallback() { @Override - public void handleInflationException(StatusBarNotification notification, + 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..1c294531ea68 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; @@ -86,7 +87,10 @@ import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -143,6 +147,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 +224,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, @@ -230,19 +237,27 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mNotifLog, mock(NotificationSectionsFeatureManager.class), mock(PeopleNotificationIdentifier.class)), - mEnvironment + mEnvironment, + mFeatureFlags ); mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager); mEntryManager.addNotificationEntryListener(mEntryListener); mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor); + NotificationRowContentBinder contentBinder = new NotificationContentInflater( + mock(NotifRemoteViewCache.class), + mRemoteInputManager); + NotificationRowBinderImpl notificationRowBinder = - new NotificationRowBinderImpl(mContext, true, /* allowLongPress */ + new NotificationRowBinderImpl(mContext, + contentBinder, + true, /* allowLongPress */ mock(KeyguardBypassController.class), 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/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/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/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java new file mode 100644 index 000000000000..d7214f3b9228 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java @@ -0,0 +1,112 @@ +/* + * 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.row; + +import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED; +import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; +import android.widget.RemoteViews; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +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.notification.collection.NotificationEntryBuilder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class NotifRemoteViewCacheImplTest extends SysuiTestCase { + + private NotifRemoteViewCacheImpl mNotifRemoteViewCache; + private NotificationEntry mEntry; + private NotificationEntryListener mEntryListener; + @Mock private RemoteViews mRemoteViews; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mEntry = new NotificationEntryBuilder().build(); + + NotificationEntryManager entryManager = mock(NotificationEntryManager.class); + mNotifRemoteViewCache = new NotifRemoteViewCacheImpl(entryManager); + ArgumentCaptor<NotificationEntryListener> entryListenerCaptor = + ArgumentCaptor.forClass(NotificationEntryListener.class); + verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture()); + mEntryListener = entryListenerCaptor.getValue(); + } + + @Test + public void testPutCachedView() { + // GIVEN an initialized cache for an entry. + mEntryListener.onPendingEntryAdded(mEntry); + + // WHEN a notification's cached remote views is put in. + mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews); + + // THEN the remote view is cached. + assertTrue(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED)); + assertEquals( + "Cached remote view is not the one we put in.", + mRemoteViews, + mNotifRemoteViewCache.getCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED)); + } + + @Test + public void testRemoveCachedView() { + // GIVEN a cache with a cached view. + mEntryListener.onPendingEntryAdded(mEntry); + mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews); + + // WHEN we remove the cached view. + mNotifRemoteViewCache.removeCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED); + + // THEN the remote view is not cached. + assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED)); + } + + @Test + public void testClearCache() { + // GIVEN a non-empty cache. + mEntryListener.onPendingEntryAdded(mEntry); + mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews); + mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED, mRemoteViews); + + // WHEN we clear the cache. + mNotifRemoteViewCache.clearCache(mEntry); + + // THEN the cache is empty. + assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED)); + assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED)); + } +} 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..cb9da6a40cb9 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 @@ -22,30 +22,35 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; 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.app.Notification; 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; import android.view.View; import android.view.ViewGroup; import android.widget.RemoteViews; +import android.widget.TextView; import androidx.test.filters.SmallTest; import androidx.test.filters.Suppress; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.InflationTask; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; @@ -58,6 +63,8 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.HashMap; import java.util.concurrent.CountDownLatch; @@ -74,8 +81,11 @@ public class NotificationContentInflaterTest extends SysuiTestCase { private Notification.Builder mBuilder; private ExpandableNotificationRow mRow; + @Mock private NotifRemoteViewCache mCache; + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); mBuilder = new Notification.Builder(mContext).setSmallIcon( R.drawable.ic_person) .setContentTitle("Title") @@ -84,7 +94,9 @@ public class NotificationContentInflaterTest extends SysuiTestCase { ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow( mBuilder.build()); mRow = spy(row); - mNotificationInflater = new NotificationContentInflater(); + mNotificationInflater = new NotificationContentInflater( + mCache, + mock(NotificationRemoteInputManager.class)); } @Test @@ -175,11 +187,13 @@ public class NotificationContentInflaterTest extends SysuiTestCase { result, FLAG_CONTENT_VIEW_EXPANDED, 0, - new ArrayMap() /* cachedContentViews */, mRow, + mock(NotifRemoteViewCache.class), + mRow.getEntry(), + mRow, 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"); @@ -245,6 +259,71 @@ public class NotificationContentInflaterTest extends SysuiTestCase { NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView)); } + @Test + public void testUsesSameViewWhenCachedPossibleToReuse() throws Exception { + // GIVEN a cached view. + RemoteViews contractedRemoteView = mBuilder.createContentView(); + when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED)) + .thenReturn(true); + when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED)) + .thenReturn(contractedRemoteView); + + // GIVEN existing bound view with same layout id. + View view = contractedRemoteView.apply(mContext, null /* parent */); + mRow.getPrivateLayout().setContractedChild(view); + + // WHEN inflater inflates + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow); + + // THEN the view should be re-used + assertEquals("Binder inflated a new view even though the old one was cached and usable.", + view, mRow.getPrivateLayout().getContractedChild()); + } + + @Test + public void testInflatesNewViewWhenCachedNotPossibleToReuse() throws Exception { + // GIVEN a cached remote view. + RemoteViews contractedRemoteView = mBuilder.createHeadsUpContentView(); + when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED)) + .thenReturn(true); + when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED)) + .thenReturn(contractedRemoteView); + + // GIVEN existing bound view with different layout id. + View view = new TextView(mContext); + mRow.getPrivateLayout().setContractedChild(view); + + // WHEN inflater inflates + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow); + + // THEN the view should be a new view + assertNotEquals("Binder (somehow) used the same view when inflating.", + view, mRow.getPrivateLayout().getContractedChild()); + } + + @Test + public void testInflationCachesCreatedRemoteView() throws Exception { + // WHEN inflater inflates + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow); + + // THEN inflater informs cache of the new remote view + verify(mCache).putCachedView( + eq(mRow.getEntry()), + eq(FLAG_CONTENT_VIEW_CONTRACTED), + any()); + } + + @Test + public void testUnbindRemovesCachedRemoteView() { + // WHEN inflated unbinds content + mNotificationInflater.unbindContent(mRow.getEntry(), mRow, FLAG_CONTENT_VIEW_HEADS_UP); + + // THEN inflated informs cache to remove remote view + verify(mCache).removeCachedView( + eq(mRow.getEntry()), + eq(FLAG_CONTENT_VIEW_HEADS_UP)); + } + private static void inflateAndWait(NotificationContentInflater inflater, @InflationFlag int contentToInflate, ExpandableNotificationRow row) @@ -261,7 +340,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/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 77a6a2602d8e..39f037cfa70d 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; @@ -166,7 +167,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mock(NotificationSectionsFeatureManager.class), mock(PeopleNotificationIdentifier.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/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 7e485f45e5e6..3da87ea2fb56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -122,6 +122,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; @@ -247,6 +248,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private DismissCallbackRegistry mDismissCallbackRegistry; @Mock private ScreenPinningRequest mScreenPinningRequest; @Mock private NotificationEntryManager mEntryManager; + @Mock private NotificationContentInflater mNotificationContentInflater; @Mock private LockscreenLockIconController mLockscreenLockIconController; @Mock private StatusBarNotificationActivityStarter.Builder mStatusBarNotificationActivityStarterBuilder; @@ -356,6 +358,7 @@ public class StatusBarTest extends SysuiTestCase { mNotificationGutsManager, notificationLogger, mEntryManager, + mNotificationContentInflater, mNotificationInterruptionStateProvider, mNotificationViewHierarchyManager, mKeyguardViewMediator, diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index 0be853a81fb2..797b713d8614 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: [ 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/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/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index 7e685fbe97f1..ce6657334611 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. */ @@ -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..4b860d0bd91e 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; @@ -75,6 +75,7 @@ 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 +88,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 +103,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 +143,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 +178,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 +214,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 +228,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<>(); @@ -421,7 +423,6 @@ public class Tethering { } } - void interfaceRemoved(String iface) { if (VDBG) Log.d(TAG, "interfaceRemoved " + iface); synchronized (mPublicSync) { @@ -1022,8 +1023,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 +1283,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 +1313,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; @@ -1390,12 +1391,13 @@ public class Tethering { // TODO: remove this invocation of NetworkUtils.makeStrings(). dnsServers = NetworkUtils.makeStrings(dnses); } + 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 +1700,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 +1711,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 +1722,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 +1886,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 +2067,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/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/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index 46c49e5a5e11..39efe731ce8a 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -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/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/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index bd8a3618733f..4515be8f4a9f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3029,25 +3029,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 +3414,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,16 +4910,33 @@ 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; public final int factorySerialNumber; NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel, int factorySerialNumber) { this.name = name; this.messenger = messenger; - this.asyncChannel = asyncChannel; + this.mAsyncChannel = asyncChannel; this.factorySerialNumber = factorySerialNumber; } + + void requestNetwork(NetworkRequest request, int score, int servingSerialNumber) { + mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, + servingSerialNumber, request); + } + + void cancelRequest(NetworkRequest request) { + mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request); + } + + void connect(Context context, Handler handler) { + mAsyncChannel.connect(context, handler, messenger); + } + + void completeConnection() { + mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + } } private void ensureNetworkRequestHasType(NetworkRequest request) { @@ -5325,7 +5325,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) { 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); } @Override @@ -5961,8 +5961,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/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index c5f1923b0b98..0fc5340846f2 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -3192,6 +3192,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()) { 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/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/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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index a1e1f29016c2..76f54cdf91be 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1555,20 +1555,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]; 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/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java index f90fab4666d6..31d4816e5d66 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; @@ -152,7 +153,7 @@ public class IntegrityFileManager { throws IOException, RuleParseException { synchronized (RULES_LOCK) { // Try to identify indexes from the index file. - List<List<Integer>> ruleReadingIndexes = + List<RuleIndexRange> ruleReadingIndexes = mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata); // Read the rules based on the index information. 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/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/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 115155ce24b5..9ca302edd204 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); + String routeType, long requestId); public abstract void releaseSession(int 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(int sessionId, String routeId); + public abstract void deselectRoute(int sessionId, String routeId); + public abstract void transferToRoute(int 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..5cc2b16e073a 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,10 +76,10 @@ 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(); } @@ -95,46 +94,46 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void selectRoute(int sessionId, MediaRoute2Info route) { + public void selectRoute(int 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(int 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(int 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(); } } @@ -346,11 +345,11 @@ 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); } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 562a7200fb38..b48243279d9e 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -28,6 +28,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 +175,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); @@ -267,16 +268,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 +435,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 +448,7 @@ class MediaRouter2ServiceImpl { clientRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::requestCreateSessionOnHandler, clientRecord.mUserRecord.mHandler, - clientRecord, route, controlCategory, requestId)); + clientRecord, route, routeType, requestId)); } } @@ -502,13 +503,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)); @@ -607,7 +609,7 @@ class MediaRouter2ServiceImpl { if (clientRecord != null && managerRecord.mTrusted) { //TODO: select category properly requestCreateSessionLocked(clientRecord.mClient, route, - route.getSupportedCategories().get(0), uniqueRequestId); + route.getRouteTypes().get(0), uniqueRequestId); } } } @@ -725,7 +727,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 +737,7 @@ class MediaRouter2ServiceImpl { mUserRecord = userRecord; mPackageName = packageName; mSelectRouteSequenceNumbers = new ArrayList<>(); - mControlCategories = Collections.emptyList(); + mDiscoveryRequest = RouteDiscoveryRequest.EMPTY; mClient = client; mUid = uid; mPid = pid; @@ -961,7 +963,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 +973,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 +1002,8 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route); + provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), + route.getOriginalId()); } private void deselectRouteOnHandler(@NonNull Client2Record clientRecord, @@ -1016,7 +1019,8 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route); + provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), + route.getOriginalId()); } private void transferToRouteOnHandler(@NonNull Client2Record clientRecord, @@ -1032,7 +1036,8 @@ class MediaRouter2ServiceImpl { if (provider == null) { return; } - provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route); + provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId), + route.getOriginalId()); } private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord, @@ -1141,12 +1146,12 @@ class MediaRouter2ServiceImpl { } String originalRouteId = matchingRequest.mRoute.getId(); - String originalCategory = matchingRequest.mControlCategory; + String originalCategory = matchingRequest.mRouteType; Client2Record client2Record = matchingRequest.mClientRecord; if (!sessionInfo.getSelectedRoutes().contains(originalRouteId) || !TextUtils.equals(originalCategory, - sessionInfo.getControlCategory())) { + sessionInfo.getRouteType())) { Slog.w(TAG, "Created session doesn't match the original request." + " originalRouteId=" + originalRouteId + ", originalCategory=" + originalCategory + ", requestId=" + requestId @@ -1239,21 +1244,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 +1406,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 +1426,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..daf603012391 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -91,7 +91,7 @@ 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 } @@ -102,33 +102,33 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void selectRoute(int sessionId, MediaRoute2Info route) { + public void selectRoute(int sessionId, String routeId) { //TODO: implement method } @Override - public void deselectRoute(int sessionId, MediaRoute2Info route) { + public void deselectRoute(int sessionId, String routeId) { //TODO: implement method } @Override - public void transferToRoute(int sessionId, MediaRoute2Info route) { + public void transferToRoute(int 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(CATEGORY_LIVE_AUDIO) + .addRouteType(CATEGORY_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(CATEGORY_LIVE_AUDIO) + .addRouteType(CATEGORY_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(CATEGORY_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/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..e281712f743a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6107,11 +6107,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); diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index e8564fc80ac3..fd871bdc1ba2 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2076,54 +2076,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; - } - + for (int i = mStoppingActivities.size() - 1; i >= 0; --i) { + final ActivityRecord s = mStoppingActivities.get(i); final boolean animating = s.isAnimating(TRANSITION); - if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + s.nowVisible + " animating=" + animating + " finishing=" + s.finishing); final ActivityStack stack = s.getActivityStack(); @@ -2145,7 +2102,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } readyToStopActivities.add(s); - mStoppingActivities.remove(activityNdx); + mStoppingActivities.remove(i); } } @@ -2161,6 +2118,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/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/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..7b40f609aaf1 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -210,7 +210,7 @@ class InsetsSourceProvider { new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top)); } - boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) { + 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..720493f4a466 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); @@ -296,6 +300,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/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..96bc8e963b38 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -792,8 +792,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/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..2a08f5c2de12 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); @@ -14584,8 +14602,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/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/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/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/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/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/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/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/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/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/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 693603f36c68..4d5713259a0f 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -29,7 +29,6 @@ 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; @@ -4205,8 +4204,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/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java index ce32dce834db..534546921ebf 100644 --- a/telephony/java/android/telephony/CellIdentity.java +++ b/telephony/java/android/telephony/CellIdentity.java @@ -191,6 +191,7 @@ public abstract class CellIdentity implements Parcelable { * * @hide */ + @SystemApi public abstract @NonNull CellIdentity sanitizeLocationInfo(); @Override 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/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index f31fafe36508..d28d750c6011 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -155,7 +155,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/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java index 202da6817cb5..b10649c7208f 100644 --- a/telephony/java/android/telephony/NetworkScan.java +++ b/telephony/java/android/telephony/NetworkScan.java @@ -17,9 +17,7 @@ package android.telephony; import android.annotation.IntDef; -import android.content.Context; import android.os.RemoteException; -import android.os.ServiceManager; import com.android.internal.telephony.ITelephony; @@ -148,6 +146,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/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 5b12aed3e51b..66feb7bac25d 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -2044,4 +2044,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/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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 22eed6e026af..321753bc1776 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -53,7 +53,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 +264,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 +273,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 +299,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 +312,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 +328,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 +354,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 +382,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 +432,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 +463,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 +525,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 +533,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 +541,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 +551,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 +561,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 +642,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 +681,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 +690,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 +698,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 +706,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 +714,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 +730,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 +739,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 +748,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 +757,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 +772,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 +962,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 +983,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 +1011,7 @@ public class SubscriptionManager { mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); if (telephonyRegistryManager != null) { telephonyRegistryManager.addOnSubscriptionsChangedListener(listener, - listener.mExecutor); + executor); } } @@ -1185,7 +1144,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 +1182,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 +1220,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 +1248,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 +1333,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 +1388,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 +1431,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 +1464,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 +1497,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 +1522,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 +1555,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 +1580,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 +1641,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 +1679,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 +1786,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 +1824,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 +1852,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 +1890,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 +1917,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 +1951,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 +2003,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 +2033,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 +2075,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 +2105,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 +2140,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 +2280,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 +2335,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 +2358,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 +2382,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 +2528,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 +2835,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 +2882,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 +2917,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 +3022,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 +3076,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 +3132,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 +3181,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 +3298,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 +3332,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 +3366,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 +3391,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 +3418,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 +3448,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 +3475,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..607450b76917 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()); } // diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index 96b6db75b370..8e6c170f255b 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -19,7 +19,6 @@ package android.telephony; 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 +28,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 +232,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/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/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..4f0f089dfa79 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; @@ -362,7 +362,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/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/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/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/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..09d5386dcfc5 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -106,8 +106,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..71942f0f80f4 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); 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..e870481ccf26 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. * @@ -4103,6 +4110,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..d91efbccc2de 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(); } 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..5ac50a0d1c97 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 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()); } /** |