diff options
368 files changed, 12525 insertions, 4268 deletions
diff --git a/Android.bp b/Android.bp index 9655daf649c7..1c9eac9f7d39 100644 --- a/Android.bp +++ b/Android.bp @@ -405,6 +405,7 @@ filegroup { ":framework-statsd-sources", ":framework-tethering-srcs", ":framework-wifi-updatable-sources", + ":ike-srcs", ":updatable-media-srcs", ], visibility: ["//visibility:private"], @@ -413,6 +414,7 @@ filegroup { java_library { name: "framework-updatable-stubs-module_libs_api", static_libs: [ + "android.net.ipsec.ike.stubs.module_lib", "framework-appsearch.stubs.module_lib", "framework-graphics.stubs.module_lib", "framework-media.stubs.module_lib", @@ -432,6 +434,7 @@ java_library { name: "framework-all", installable: false, static_libs: [ + "android.net.ipsec.ike.impl", "framework-minus-apex", "framework-appsearch.impl", "framework-graphics.impl", diff --git a/StubLibraries.bp b/StubLibraries.bp index 86364af20812..3f2e89889912 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -306,6 +306,7 @@ java_library_static { name: "android_stubs_current", srcs: [ ":api-stubs-docs-non-updatable" ], static_libs: [ + "android.net.ipsec.ike.stubs", "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs", @@ -328,6 +329,7 @@ java_library_static { name: "android_system_stubs_current", srcs: [ ":system-api-stubs-docs-non-updatable" ], static_libs: [ + "android.net.ipsec.ike.stubs.system", "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs.system", @@ -366,6 +368,7 @@ java_library_static { static_libs: [ // Modules do not have test APIs, but we want to include their SystemApis, like we include // the SystemApi of framework-non-updatable-sources. + "android.net.ipsec.ike.stubs.system", "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs.system", diff --git a/TEST_MAPPING b/TEST_MAPPING index 2b12da291acb..6c265bc1a338 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -51,6 +51,17 @@ "exclude-annotation": "org.junit.Ignore" } ] + }, + { + "name": "FrameworksInProcessTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] } ], "postsubmit-managedprofile-stress": [ diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java index d3938f4c0926..afd8e2948c41 100644 --- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java +++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java @@ -77,7 +77,7 @@ public class ResourcesManagerPerfTest { } private void getResourcesForPath(String path) { - ResourcesManager.getInstance().getResources(null, path, null, null, null, + ResourcesManager.getInstance().getResources(null, path, null, null, null, null, Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(), null, null); } diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java index f4c0a172710b..45c723bea9db 100644 --- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java +++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java @@ -95,8 +95,9 @@ public class ResourcesThemePerfTest { ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; Resources destResources = resourcesManager.getResources(null, ai.sourceDir, - ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY, - c, mContext.getResources().getCompatibilityInfo(), null, null); + ai.splitSourceDirs, ai.resourceDirs, ai.overlayPaths, ai.sharedLibraryFiles, + Display.DEFAULT_DISPLAY, c, mContext.getResources().getCompatibilityInfo(), + null, null); Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets()); Resources.Theme destTheme = destResources.newTheme(); diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index a3b80139ced8..905000ac7c3d 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -208,8 +208,8 @@ package android.app.appsearch { ctor public GetByUriRequest.Builder(); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>); - method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>); method @NonNull public android.app.appsearch.GetByUriRequest build(); method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String); } @@ -243,8 +243,8 @@ package android.app.appsearch { public static final class RemoveByUriRequest.Builder { ctor public RemoveByUriRequest.Builder(); - method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>); method @NonNull public android.app.appsearch.RemoveByUriRequest build(); method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String); } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java index 0fcf0613dd38..656608d82ad4 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java @@ -122,14 +122,14 @@ public final class GetByUriRequest { /** Adds one or more URIs to the request. */ @NonNull - public Builder addUri(@NonNull String... uris) { + public Builder addUris(@NonNull String... uris) { Preconditions.checkNotNull(uris); - return addUri(Arrays.asList(uris)); + return addUris(Arrays.asList(uris)); } /** Adds one or more URIs to the request. */ @NonNull - public Builder addUri(@NonNull Collection<String> uris) { + public Builder addUris(@NonNull Collection<String> uris) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(uris); mUris.addAll(uris); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java index 2104198d371c..198eee85be53 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java @@ -73,14 +73,14 @@ public final class RemoveByUriRequest { /** Adds one or more URIs to the request. */ @NonNull - public Builder addUri(@NonNull String... uris) { + public Builder addUris(@NonNull String... uris) { Preconditions.checkNotNull(uris); - return addUri(Arrays.asList(uris)); + return addUris(Arrays.asList(uris)); } /** Adds one or more URIs to the request. */ @NonNull - public Builder addUri(@NonNull Collection<String> uris) { + public Builder addUris(@NonNull Collection<String> uris) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(uris); mUris.addAll(uris); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java index 0328d5453ab8..4869aa38b5fd 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -163,7 +163,7 @@ public class SetSchemaResponse { /** Adds deletedTypes to the list of deleted schema types. */ @NonNull - public Builder addDeletedType(@NonNull Collection<String> deletedTypes) { + public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) { Preconditions.checkState(!mBuilt, "Builder has already been used"); mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes)); return this; @@ -171,7 +171,7 @@ public class SetSchemaResponse { /** Adds incompatibleTypes to the list of incompatible schema types. */ @NonNull - public Builder addIncompatibleType(@NonNull Collection<String> incompatibleTypes) { + public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) { Preconditions.checkState(!mBuilt, "Builder has already been used"); mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes)); return this; @@ -179,7 +179,7 @@ public class SetSchemaResponse { /** Adds migratedTypes to the list of migrated schema types. */ @NonNull - public Builder addMigratedType(@NonNull Collection<String> migratedTypes) { + public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) { Preconditions.checkState(!mBuilt, "Builder has already been used"); mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes)); return this; diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java index 1b4d28401ea0..14dd472c9b9c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java @@ -147,35 +147,41 @@ public final class BundleUtil { if (bundle == null) { return 0; } - int[] hashCodes = new int[bundle.size()]; - int i = 0; + int[] hashCodes = new int[bundle.size() + 1]; + int hashCodeIdx = 0; // Bundle inherit its hashCode() from Object.java, which only relative to their memory // address. Bundle doesn't have an order, so we should iterate all keys and combine // their value's hashcode into an array. And use the hashcode of the array to be // the hashcode of the bundle. - for (String key : bundle.keySet()) { - Object value = bundle.get(key); + // Because bundle.keySet() doesn't guarantee any particular order, we need to sort the keys + // in case the iteration order varies from run to run. + String[] keys = bundle.keySet().toArray(new String[0]); + Arrays.sort(keys); + // Hash the keys so we can detect key-only differences + hashCodes[hashCodeIdx++] = Arrays.hashCode(keys); + for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) { + Object value = bundle.get(keys[keyIdx]); if (value instanceof Bundle) { - hashCodes[i++] = deepHashCode((Bundle) value); + hashCodes[hashCodeIdx++] = deepHashCode((Bundle) value); } else if (value instanceof int[]) { - hashCodes[i++] = Arrays.hashCode((int[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((int[]) value); } else if (value instanceof byte[]) { - hashCodes[i++] = Arrays.hashCode((byte[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((byte[]) value); } else if (value instanceof char[]) { - hashCodes[i++] = Arrays.hashCode((char[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((char[]) value); } else if (value instanceof long[]) { - hashCodes[i++] = Arrays.hashCode((long[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((long[]) value); } else if (value instanceof float[]) { - hashCodes[i++] = Arrays.hashCode((float[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((float[]) value); } else if (value instanceof short[]) { - hashCodes[i++] = Arrays.hashCode((short[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((short[]) value); } else if (value instanceof double[]) { - hashCodes[i++] = Arrays.hashCode((double[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((double[]) value); } else if (value instanceof boolean[]) { - hashCodes[i++] = Arrays.hashCode((boolean[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((boolean[]) value); } else if (value instanceof String[]) { // Optimization to avoid Object[] handler creating an inner array for common cases - hashCodes[i++] = Arrays.hashCode((String[]) value); + hashCodes[hashCodeIdx++] = Arrays.hashCode((String[]) value); } else if (value instanceof Object[]) { Object[] array = (Object[]) value; int[] innerHashCodes = new int[array.length]; @@ -186,7 +192,7 @@ public final class BundleUtil { innerHashCodes[j] = array[j].hashCode(); } } - hashCodes[i++] = Arrays.hashCode(innerHashCodes); + hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes); } else if (value instanceof ArrayList) { ArrayList<?> list = (ArrayList<?>) value; int[] innerHashCodes = new int[list.size()]; @@ -198,7 +204,7 @@ public final class BundleUtil { innerHashCodes[j] = item.hashCode(); } } - hashCodes[i++] = Arrays.hashCode(innerHashCodes); + hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes); } else if (value instanceof SparseArray) { SparseArray<?> array = (SparseArray<?>) value; int[] innerHashCodes = new int[array.size() * 2]; @@ -211,9 +217,9 @@ public final class BundleUtil { innerHashCodes[j * 2 + 1] = item.hashCode(); } } - hashCodes[i++] = Arrays.hashCode(innerHashCodes); + hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes); } else { - hashCodes[i++] = value.hashCode(); + hashCodes[hashCodeIdx++] = value.hashCode(); } } return Arrays.hashCode(hashCodes); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 3bbc945c8b87..271129bee7c5 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -38,6 +38,7 @@ import android.os.ParcelableException; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import com.android.internal.util.Preconditions; @@ -49,6 +50,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** TODO(b/142567528): add comments when implement this class */ public class AppSearchManagerService extends SystemService { @@ -56,6 +58,9 @@ public class AppSearchManagerService extends SystemService { private PackageManagerInternal mPackageManagerInternal; private ImplInstanceManager mImplInstanceManager; + // Cache of unlocked user ids so we don't have to query UserManager service each time. + private final Set<Integer> mUnlockedUserIds = new ArraySet<>(); + public AppSearchManagerService(Context context) { super(context); } @@ -67,6 +72,11 @@ public class AppSearchManagerService extends SystemService { mImplInstanceManager = ImplInstanceManager.getInstance(getContext()); } + @Override + public void onUserUnlocked(@NonNull TargetUser user) { + mUnlockedUserIds.add(user.getUserIdentifier()); + } + private class Stub extends IAppSearchManager.Stub { @Override public void setSchema( @@ -86,6 +96,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size()); for (int i = 0; i < schemaBundles.size(); i++) { @@ -133,6 +144,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); @@ -165,6 +177,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); @@ -207,6 +220,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); @@ -253,6 +267,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); @@ -287,6 +302,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); @@ -318,6 +334,7 @@ public class AppSearchManagerService extends SystemService { // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally // opened it try { + verifyUserUnlocked(callingUserId); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); @@ -337,6 +354,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.invalidateNextPageToken(nextPageToken); @@ -364,6 +382,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis); @@ -392,6 +411,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); @@ -431,6 +451,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); @@ -453,6 +474,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.persistToDisk(); @@ -470,6 +492,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyUserUnlocked(callingUserId); mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -479,6 +502,13 @@ public class AppSearchManagerService extends SystemService { } } + private void verifyUserUnlocked(int callingUserId) { + if (!mUnlockedUserIds.contains(callingUserId)) { + throw new IllegalStateException( + "User " + callingUserId + " is locked or not running."); + } + } + private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) { Preconditions.checkNotNull(callingPackage); if (mPackageManagerInternal.getPackageUid( diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java index 97b1a8cd6d50..5ea2a02b5b40 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java @@ -73,8 +73,8 @@ public final class ImplInstanceManager { /** * Gets an instance of AppSearchImpl for the given user. * - * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will - * be created. + * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and + * one will be created. * * @param context The context * @param userId The multi-user userId of the device user calling AppSearch diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 12699b7815fb..6ba557224582 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -Ibe06fb9c574c8718191f833bb042fa10c300e4e2 +I2bf8bd9db1b71b7da4ab50dd7480e4529678413a diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java index 20fb90986f41..44d5180c3a36 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java @@ -59,7 +59,7 @@ public class AppSearchTestUtils { session.getByUri( new GetByUriRequest.Builder() .setNamespace(namespace) - .addUri(uris) + .addUris(uris) .build())); assertThat(result.getSuccesses()).hasSize(uris.length); assertThat(result.getFailures()).isEmpty(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index bfc153f5f2f7..82ee5d8c1c7c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1990,8 +1990,11 @@ public class JobSchedulerService extends com.android.server.SystemService } final boolean shouldForceBatchJob; - // Restricted jobs must always be batched - if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { + if (job.shouldTreatAsExpeditedJob()) { + // Never batch expedited jobs, even for RESTRICTED apps. + shouldForceBatchJob = false; + } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { + // Restricted jobs must always be batched shouldForceBatchJob = true; } else if (job.getNumFailures() > 0) { shouldForceBatchJob = false; diff --git a/api/Android.bp b/api/Android.bp index 69dce979748e..d5c6bf6d024e 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -28,6 +28,7 @@ genrule { genrule { name: "frameworks-base-api-current.txt", srcs: [ + ":android.net.ipsec.ike{.public.api.txt}", ":art.module.public.api{.public.api.txt}", ":conscrypt.module.public.api{.public.api.txt}", ":framework-appsearch{.public.api.txt}", @@ -64,6 +65,7 @@ genrule { genrule { name: "frameworks-base-api-current.srcjar", srcs: [ + ":android.net.ipsec.ike{.public.stubs.source}", ":api-stubs-docs-non-updatable", ":art.module.public.api{.public.stubs.source}", ":conscrypt.module.public.api{.public.stubs.source}", @@ -88,6 +90,7 @@ genrule { genrule { name: "frameworks-base-api-removed.txt", srcs: [ + ":android.net.ipsec.ike{.public.removed-api.txt}", ":art.module.public.api{.public.removed-api.txt}", ":conscrypt.module.public.api{.public.removed-api.txt}", ":framework-appsearch{.public.removed-api.txt}", @@ -123,6 +126,7 @@ genrule { genrule { name: "frameworks-base-api-system-current.txt", srcs: [ + ":android.net.ipsec.ike{.system.api.txt}", ":framework-appsearch{.system.api.txt}", ":framework-graphics{.system.api.txt}", ":framework-media{.system.api.txt}", @@ -156,6 +160,7 @@ genrule { genrule { name: "frameworks-base-api-system-removed.txt", srcs: [ + ":android.net.ipsec.ike{.system.removed-api.txt}", ":framework-appsearch{.system.removed-api.txt}", ":framework-graphics{.system.removed-api.txt}", ":framework-media{.system.removed-api.txt}", @@ -189,6 +194,7 @@ genrule { genrule { name: "frameworks-base-api-module-lib-current.txt", srcs: [ + ":android.net.ipsec.ike{.module-lib.api.txt}", ":framework-appsearch{.module-lib.api.txt}", ":framework-graphics{.module-lib.api.txt}", ":framework-media{.module-lib.api.txt}", @@ -221,6 +227,7 @@ genrule { genrule { name: "frameworks-base-api-module-lib-removed.txt", srcs: [ + ":android.net.ipsec.ike{.module-lib.removed-api.txt}", ":framework-appsearch{.module-lib.removed-api.txt}", ":framework-graphics{.module-lib.removed-api.txt}", ":framework-media{.module-lib.removed-api.txt}", diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index e21a6b288fb3..50f400122fe1 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -132,9 +132,6 @@ cc_test { "tests/XmlParserTests.cpp", "tests/ZipFileTests.cpp", ], - required: [ - "idmap2", - ], static_libs: ["libgmock"], target: { android: { @@ -163,9 +160,19 @@ cc_test { shared_libs: [ "libz", ], + data: [ + ":libz", + ":idmap2", + ], }, }, - data: ["tests/data/**/*.apk"], + data: [ + "tests/data/**/*.apk", + ], + compile_multilib: "first", + test_options: { + unit_test: true, + }, } cc_binary { diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml deleted file mode 100644 index 5147f4e6cb4c..000000000000 --- a/cmds/idmap2/AndroidTest.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for idmap2_tests"> - <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> - <option name="cleanup" value="true" /> - <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" /> - </target_preparer> - <option name="test-suite-tag" value="idmap2_tests" /> - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/local/tmp" /> - <option name="module-name" value="idmap2_tests" /> - </test> -</configuration> diff --git a/core/api/current.txt b/core/api/current.txt index 838dd55e34d5..f82e00035e65 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -435,6 +435,7 @@ package android { field public static final int clickable = 16842981; // 0x10100e5 field public static final int clipChildren = 16842986; // 0x10100ea field public static final int clipOrientation = 16843274; // 0x101020a + field public static final int clipToOutline = 16844328; // 0x1010628 field public static final int clipToPadding = 16842987; // 0x10100eb field public static final int closeIcon = 16843905; // 0x1010481 field @Deprecated public static final int codes = 16843330; // 0x1010242 @@ -2936,12 +2937,12 @@ package android.accessibilityservice { field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14 field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28 field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13 - field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19 field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15 + field public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43; // 0x2b field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17 field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29 field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16 @@ -5554,15 +5555,19 @@ package android.app { field public static final int DEFAULT_LIGHTS = 4; // 0x4 field public static final int DEFAULT_SOUND = 1; // 0x1 field public static final int DEFAULT_VIBRATE = 2; // 0x2 + field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent"; field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents"; field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; field public static final String EXTRA_BIG_TEXT = "android.bigText"; + field public static final String EXTRA_CALL_PERSON = "android.callPerson"; field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID"; field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID"; field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown"; field public static final String EXTRA_COLORIZED = "android.colorized"; field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions"; field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle"; + field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent"; + field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent"; field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic"; field public static final String EXTRA_INFO_TEXT = "android.infoText"; field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation"; @@ -5594,6 +5599,8 @@ package android.app { field public static final String EXTRA_TEXT_LINES = "android.textLines"; field public static final String EXTRA_TITLE = "android.title"; field public static final String EXTRA_TITLE_BIG = "android.title.big"; + field public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon"; + field public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText"; field public static final int FLAG_AUTO_CANCEL = 16; // 0x10 field public static final int FLAG_BUBBLE = 4096; // 0x1000 field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40 @@ -5842,6 +5849,14 @@ package android.app { method @NonNull public android.app.Notification.Builder setWhen(long); } + public static class Notification.CallStyle extends android.app.Notification.Style { + method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent); + method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent); + method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent); + method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon); + method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence); + } + public static final class Notification.CarExtender implements android.app.Notification.Extender { ctor public Notification.CarExtender(); ctor public Notification.CarExtender(android.app.Notification); @@ -30594,6 +30609,7 @@ package android.os { field public static String DIRECTORY_NOTIFICATIONS; field public static String DIRECTORY_PICTURES; field public static String DIRECTORY_PODCASTS; + field @NonNull public static String DIRECTORY_RECORDINGS; field public static String DIRECTORY_RINGTONES; field public static String DIRECTORY_SCREENSHOTS; field public static final String MEDIA_BAD_REMOVAL = "bad_removal"; @@ -40338,6 +40354,7 @@ package android.telephony { field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; + field public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = "store_sim_pin_for_unattended_reboot_bool"; field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool"; @@ -42063,7 +42080,7 @@ package android.telephony { method @Deprecated public int getPhoneCount(); method public int getPhoneType(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription(); - method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState(); method @Nullable public android.telephony.SignalStrength getSignalStrength(); method public int getSimCarrierId(); method @Nullable public CharSequence getSimCarrierIdName(); @@ -42922,6 +42939,16 @@ package android.telephony.ims { field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2 } + public final class ImsRegistrationAttributes implements android.os.Parcelable { + method public int describeContents(); + method public int getAttributeFlags(); + method @NonNull public java.util.Set<java.lang.String> getFeatureTags(); + method public int getTransportType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1 + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR; + } + public class RcsUceAdapter { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; } @@ -42931,7 +42958,6 @@ package android.telephony.ims { method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); - field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1 field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0 field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2 field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1 @@ -42940,9 +42966,9 @@ package android.telephony.ims { public static class RegistrationManager.RegistrationCallback { ctor public RegistrationManager.RegistrationCallback(); method @Deprecated public void onRegistered(int); - method public void onRegistered(int, int); + method public void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes); method @Deprecated public void onRegistering(int); - method public void onRegistering(int, int); + method public void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes); method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo); method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo); } @@ -50687,6 +50713,7 @@ package android.view.autofill { method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback); field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; + field public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET = "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET"; field public static final String EXTRA_CLIENT_STATE = "android.view.autofill.extra.CLIENT_STATE"; } @@ -54634,6 +54661,8 @@ package android.widget { method public void setTextViewText(@IdRes int, CharSequence); method public void setTextViewTextSize(@IdRes int, int, float); method public void setUri(@IdRes int, String, android.net.Uri); + method public void setViewOutlinePreferredRadius(@IdRes int, float, int); + method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int); method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int); method public void setViewVisibility(@IdRes int, int); method public void showNext(@IdRes int); @@ -54658,6 +54687,12 @@ package android.widget { @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface RemoteViews.RemoteView { } + public static final class RemoteViews.RemoteViewOutlineProvider extends android.view.ViewOutlineProvider { + ctor public RemoteViews.RemoteViewOutlineProvider(float); + method public void getOutline(@NonNull android.view.View, @NonNull android.graphics.Outline); + method public float getRadius(); + } + public abstract class RemoteViewsService extends android.app.Service { ctor public RemoteViewsService(); method public android.os.IBinder onBind(android.content.Intent); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index bf70803fbc74..4e256254c04a 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -162,6 +162,7 @@ package android.net { } public class ConnectivityManager { + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5bb3e0517032..74ccbb14f816 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -11869,6 +11869,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String); method public boolean needsOtaServiceProvisioning(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled(); + method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); @@ -11993,6 +11994,9 @@ package android.telephony { field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2 field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3 field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1 + field public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; // 0x2 + field public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; // 0x1 + field public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; // 0x0 field public static final int RADIO_POWER_OFF = 0; // 0x0 field public static final int RADIO_POWER_ON = 1; // 0x1 field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2 @@ -12974,6 +12978,16 @@ package android.telephony.ims { field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service"; } + public final class ImsRegistrationAttributes implements android.os.Parcelable { + method public int getRegistrationTechnology(); + } + + public static final class ImsRegistrationAttributes.Builder { + ctor public ImsRegistrationAttributes.Builder(int); + method @NonNull public android.telephony.ims.ImsRegistrationAttributes build(); + method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFeatureTags(@NonNull java.util.Set<java.lang.String>); + } + public class ImsService extends android.app.Service { ctor public ImsService(); method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int); @@ -13757,7 +13771,9 @@ package android.telephony.ims.stub { ctor public ImsRegistrationImplBase(); method public final void onDeregistered(android.telephony.ims.ImsReasonInfo); method public final void onRegistered(int); + method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes); method public final void onRegistering(int); + method public final void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes); method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]); method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo); method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String); diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java index 4b2d74160006..768ec3851991 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java @@ -20,12 +20,12 @@ package android.accessibilityservice; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP; -import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP; @@ -97,10 +97,10 @@ public final class AccessibilityGestureEvent implements Parcelable { GESTURE_UNKNOWN, GESTURE_TOUCH_EXPLORATION, GESTURE_2_FINGER_SINGLE_TAP, - GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, GESTURE_2_FINGER_DOUBLE_TAP, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, GESTURE_2_FINGER_TRIPLE_TAP, + GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, GESTURE_3_FINGER_SINGLE_TAP, GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD, GESTURE_3_FINGER_DOUBLE_TAP, @@ -232,8 +232,8 @@ public final class AccessibilityGestureEvent implements Parcelable { case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH"; case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION"; case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP"; - case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD: - return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD"; + case GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD: + return "GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD"; case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP"; case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD: return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD"; diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 1fa7fa2b744f..dab4a5dc316c 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -445,8 +445,8 @@ public abstract class AccessibilityService extends Service { /** The user has performed a three-finger double tap and hold gesture on the touch screen. */ public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; - /** The user has performed a two-finger single-tap and hold gesture on the touch screen. */ - public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; + /** The user has performed a two-finger triple-tap and hold gesture on the touch screen. */ + public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43; /** The user has performed a three-finger single-tap and hold gesture on the touch screen. */ public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0774ac134d1b..bb6a774cbee2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2289,11 +2289,12 @@ public final class ActivityThread extends ClientTransactionHandler { * Creates the top level resources for the given package. Will return an existing * Resources if one has already been created. */ - Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, - String[] libDirs, LoadedApk pkgInfo, Configuration overrideConfig) { - return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs, - null, overrideConfig, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), - null); + Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] legacyOverlayDirs, + String[] overlayPaths, String[] libDirs, LoadedApk pkgInfo, + Configuration overrideConfig) { + return mResourcesManager.getResources(null, resDir, splitResDirs, legacyOverlayDirs, + overlayPaths, libDirs, null, overrideConfig, pkgInfo.getCompatibilityInfo(), + pkgInfo.getClassLoader(), null); } @UnsupportedAppUsage @@ -2462,12 +2463,15 @@ public final class ActivityThread extends ClientTransactionHandler { private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk, ApplicationInfo appInfo) { Resources packageResources = loadedApk.mResources; - String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()); - String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs); + boolean resourceDirsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.resourceDirs), + ArrayUtils.defeatNullable(loadedApk.getOverlayDirs())); + boolean overlayPathsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.overlayPaths), + ArrayUtils.defeatNullable(loadedApk.getOverlayPaths())); return (packageResources == null || packageResources.getAssets().isUpToDate()) - && overlayDirs.length == resourceDirs.length - && ArrayUtils.containsAll(overlayDirs, resourceDirs); + && resourceDirsUpToDate && overlayPathsUpToDate; } @UnsupportedAppUsage diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 8ac91396a6b0..062cab457ebe 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1728,7 +1728,7 @@ public class ApplicationPackageManager extends PackageManager { final Resources r = mContext.mMainThread.getTopLevelResources( sameUid ? app.sourceDir : app.publicSourceDir, sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, - app.resourceDirs, app.sharedLibraryFiles, + app.resourceDirs, app.overlayPaths, app.sharedLibraryFiles, mContext.mPackageInfo, configuration); if (r != null) { return r; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4ddeb8fbfbef..9a20e0fefd33 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2345,6 +2345,7 @@ class ContextImpl extends Context { pi.getResDir(), splitResDirs, pi.getOverlayDirs(), + pi.getOverlayPaths(), pi.getApplicationInfo().sharedLibraryFiles, overrideDisplayId, overrideConfig, @@ -2442,6 +2443,7 @@ class ContextImpl extends Context { mPackageInfo.getResDir(), paths, mPackageInfo.getOverlayDirs(), + mPackageInfo.getOverlayPaths(), mPackageInfo.getApplicationInfo().sharedLibraryFiles, mForceDisplayOverrideInResources ? getDisplayId() : null, null, @@ -2558,7 +2560,8 @@ class ContextImpl extends Context { Resources createWindowContextResources() { final String resDir = mPackageInfo.getResDir(); final String[] splitResDirs = mPackageInfo.getSplitResDirs(); - final String[] overlayDirs = mPackageInfo.getOverlayDirs(); + final String[] legacyOverlayDirs = mPackageInfo.getOverlayDirs(); + final String[] overlayPaths = mPackageInfo.getOverlayPaths(); final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles; final int displayId = getDisplayId(); final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) @@ -2567,7 +2570,7 @@ class ContextImpl extends Context { final List<ResourcesLoader> loaders = mResources.getLoaders(); return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs, - overlayDirs, libDirs, displayId, null /* overrideConfig */, + legacyOverlayDirs, overlayPaths, libDirs, displayId, null /* overrideConfig */, compatInfo, mClassLoader, loaders); } @@ -2855,6 +2858,7 @@ class ContextImpl extends Context { packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), + packageInfo.getOverlayPaths(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java index 8b6570f1241f..ac1fa1ec6837 100644 --- a/core/java/android/app/GameManager.java +++ b/core/java/android/app/GameManager.java @@ -16,7 +16,9 @@ package android.app; +import android.Manifest; import android.annotation.IntDef; +import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.UserHandleAware; import android.content.Context; @@ -73,8 +75,8 @@ public final class GameManager { /** * Returns the game mode for the given package. */ - // TODO(b/178111358): Add @RequiresPermission. @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public @GameMode int getGameMode(String packageName) { try { return mService.getGameMode(packageName, mContext.getUserId()); @@ -86,8 +88,8 @@ public final class GameManager { /** * Sets the game mode for the given package. */ - // TODO(b/178111358): Add @RequiresPermission. @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public void setGameMode(String packageName, @GameMode int gameMode) { try { mService.setGameMode(packageName, gameMode, mContext.getUserId()); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index c01b5a32b98b..be426aa7ed2b 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -113,7 +113,8 @@ public final class LoadedApk { private String mAppDir; @UnsupportedAppUsage private String mResDir; - private String[] mOverlayDirs; + private String[] mLegacyOverlayDirs; + private String[] mOverlayPaths; @UnsupportedAppUsage private String mDataDir; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -222,7 +223,8 @@ public final class LoadedApk { mSplitAppDirs = null; mSplitResDirs = null; mSplitClassLoaderNames = null; - mOverlayDirs = null; + mLegacyOverlayDirs = null; + mOverlayPaths = null; mDataDir = null; mDataDirFile = null; mDeviceProtectedDataDirFile = null; @@ -364,8 +366,8 @@ public final class LoadedApk { } mResources = ResourcesManager.getInstance().getResources(null, mResDir, - splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, - null, null, getCompatibilityInfo(), + splitPaths, mLegacyOverlayDirs, mOverlayPaths, + mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(), getClassLoader(), mApplication == null ? null : mApplication.getResources().getLoaders()); } @@ -379,7 +381,8 @@ public final class LoadedApk { mApplicationInfo = aInfo; mAppDir = aInfo.sourceDir; mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; - mOverlayDirs = aInfo.resourceDirs; + mLegacyOverlayDirs = aInfo.resourceDirs; + mOverlayPaths = aInfo.overlayPaths; mDataDir = aInfo.dataDir; mLibDir = aInfo.nativeLibraryDir; mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir); @@ -1213,9 +1216,19 @@ public final class LoadedApk { return mSplitResDirs; } + /** + * Corresponds to {@link ApplicationInfo#resourceDirs}. + */ @UnsupportedAppUsage public String[] getOverlayDirs() { - return mOverlayDirs; + return mLegacyOverlayDirs; + } + + /** + * Corresponds to {@link ApplicationInfo#overlayPaths}. + */ + public String[] getOverlayPaths() { + return mOverlayPaths; } public String getDataDir() { @@ -1252,8 +1265,8 @@ public final class LoadedApk { } mResources = ResourcesManager.getInstance().getResources(null, mResDir, - splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, - null, null, getCompatibilityInfo(), + splitPaths, mLegacyOverlayDirs, mOverlayPaths, + mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(), getClassLoader(), null); } return mResources; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 49f508d83f91..c242fd466c41 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -22,7 +22,10 @@ import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP; import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast; +import static java.util.Objects.requireNonNull; + import android.annotation.ColorInt; +import android.annotation.ColorRes; import android.annotation.DimenRes; import android.annotation.Dimension; import android.annotation.DrawableRes; @@ -33,6 +36,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.StringRes; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; @@ -403,6 +407,7 @@ public class Notification implements Parcelable STANDARD_LAYOUTS.add(R.layout.notification_template_material_conversation); STANDARD_LAYOUTS.add(R.layout.notification_template_material_media); STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_call); STANDARD_LAYOUTS.add(R.layout.notification_template_header); } @@ -649,7 +654,7 @@ public class Notification implements Parcelable private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList( BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class, DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class, - MessagingStyle.class); + MessagingStyle.class, CallStyle.class); /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT, @@ -1318,6 +1323,53 @@ public class Notification implements Parcelable public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation"; /** + * {@link #extras} key: the type of call represented by the + * {@link android.app.Notification.CallStyle} notification. This extra is an int. + * @hide + */ + public static final String EXTRA_CALL_TYPE = "android.callType"; + + /** + * {@link #extras} key: the person to be displayed as calling for the + * {@link android.app.Notification.CallStyle} notification. This extra is a {@link Person}. + */ + public static final String EXTRA_CALL_PERSON = "android.callPerson"; + + /** + * {@link #extras} key: the icon to be displayed as a verification status of the caller on a + * {@link android.app.Notification.CallStyle} notification. This extra is an {@link Icon}. + */ + public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon"; + + /** + * {@link #extras} key: the text to be displayed as a verification status of the caller on a + * {@link android.app.Notification.CallStyle} notification. This extra is a + * {@link CharSequence}. + */ + public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText"; + + /** + * {@link #extras} key: the intent to be sent when the users answers a + * {@link android.app.Notification.CallStyle} notification. This extra is a + * {@link PendingIntent}. + */ + public static final String EXTRA_ANSWER_INTENT = "android.answerIntent"; + + /** + * {@link #extras} key: the intent to be sent when the users declines a + * {@link android.app.Notification.CallStyle} notification. This extra is a + * {@link PendingIntent}. + */ + public static final String EXTRA_DECLINE_INTENT = "android.declineIntent"; + + /** + * {@link #extras} key: the intent to be sent when the users hangs up a + * {@link android.app.Notification.CallStyle} notification. This extra is a + * {@link PendingIntent}. + */ + public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent"; + + /** * {@link #extras} key: whether the notification should be colorized as * supplied to {@link Builder#setColorized(boolean)}. */ @@ -5876,11 +5928,11 @@ public class Notification implements Parcelable return summary; } - private RemoteViews generateActionButton(Action action, boolean emphazisedMode, + private RemoteViews generateActionButton(Action action, boolean emphasizedMode, StandardTemplateParams p) { final boolean tombstone = (action.actionIntent == null); RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(), - emphazisedMode ? getEmphasizedActionLayoutResource() + emphasizedMode ? getEmphasizedActionLayoutResource() : tombstone ? getActionTombstoneLayoutResource() : getActionLayoutResource()); if (!tombstone) { @@ -5890,43 +5942,42 @@ public class Notification implements Parcelable if (action.mRemoteInputs != null) { button.setRemoteInputs(R.id.action0, action.mRemoteInputs); } - if (emphazisedMode) { + if (emphasizedMode) { // change the background bgColor CharSequence title = action.title; - ColorStateList[] outResultColor = null; + ColorStateList[] outResultColor = new ColorStateList[1]; int background = resolveBackgroundColor(p); if (isLegacy()) { title = ContrastColorUtil.clearColorSpans(title); } else { - outResultColor = new ColorStateList[1]; title = ensureColorSpanContrast(title, background, outResultColor); } button.setTextViewText(R.id.action0, processTextSpans(title)); - setTextViewColorPrimary(button, R.id.action0, p); - int rippleColor; - boolean hasColorOverride = outResultColor != null && outResultColor[0] != null; + int textColor = getPrimaryTextColor(p); + boolean hasColorOverride = outResultColor[0] != null; if (hasColorOverride) { // There's a span spanning the full text, let's take it and use it as the // background color background = outResultColor[0].getDefaultColor(); - int textColor = ContrastColorUtil.resolvePrimaryColor(mContext, + textColor = ContrastColorUtil.resolvePrimaryColor(mContext, background, mInNightMode); - button.setTextColor(R.id.action0, textColor); - rippleColor = textColor; } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p) && mTintActionButtons && !mInNightMode) { - rippleColor = resolveContrastColor(p); - button.setTextColor(R.id.action0, rippleColor); - } else { - rippleColor = getPrimaryTextColor(p); + textColor = resolveContrastColor(p); } + button.setTextColor(R.id.action0, textColor); // We only want about 20% alpha for the ripple - rippleColor = (rippleColor & 0x00ffffff) | 0x33000000; + final int rippleColor = (textColor & 0x00ffffff) | 0x33000000; button.setColorStateList(R.id.action0, "setRippleColor", ColorStateList.valueOf(rippleColor)); button.setColorStateList(R.id.action0, "setButtonBackground", ColorStateList.valueOf(background)); button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride); + if (p.mAllowActionIcons) { + button.setImageViewIcon(R.id.action0, action.getIcon()); + boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY); + button.setBoolean(R.id.action0, "setWrapModePriority", priority); + } } else { button.setTextViewText(R.id.action0, processTextSpans( processLegacyText(action.title))); @@ -5936,8 +5987,12 @@ public class Notification implements Parcelable button.setTextColor(R.id.action0, resolveContrastColor(p)); } } - button.setIntTag(R.id.action0, R.id.notification_action_index_tag, - mActions.indexOf(action)); + // CallStyle notifications add action buttons which don't actually exist in mActions, + // so we have to omit the index in that case. + int actionIndex = mActions.indexOf(action); + if (actionIndex != -1) { + button.setIntTag(R.id.action0, R.id.notification_action_index_tag, actionIndex); + } return button; } @@ -6371,6 +6426,10 @@ public class Notification implements Parcelable return R.layout.notification_template_material_conversation; } + private int getCallLayoutResource() { + return R.layout.notification_template_material_call; + } + private int getActionLayoutResource() { return R.layout.notification_material_action; } @@ -8039,6 +8098,10 @@ public class Notification implements Parcelable : mBuilder.getMessagingLayoutResource(), p, bindResult); + if (isConversationLayout) { + mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p); + mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p); + } addExtras(mBuilder.mN.extras); if (!isConversationLayout) { @@ -8925,6 +8988,441 @@ public class Notification implements Parcelable } } + + + /** + * Helper class for generating large-format notifications that include a large image attachment. + * + * Here's how you'd set the <code>CallStyle</code> on a notification: + * <pre class="prettyprint"> + * Notification notif = new Notification.Builder(mContext) + * .setSmallIcon(R.drawable.new_post) + * .setStyle(Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent)) + * .build(); + * </pre> + */ + public static class CallStyle extends Style { + private static final int CALL_TYPE_INCOMING = 1; + private static final int CALL_TYPE_ONGOING = 2; + private static final int CALL_TYPE_SCREENING = 3; + + /** + * This is a key used privately on the action.extras to give spacing priority + * to the required call actions + */ + private static final String KEY_ACTION_PRIORITY = "key_action_priority"; + + private int mCallType; + private Person mPerson; + private PendingIntent mAnswerIntent; + private PendingIntent mDeclineIntent; + private PendingIntent mHangUpIntent; + private Icon mVerificationIcon; + private CharSequence mVerificationText; + + CallStyle() { + } + + /** + * Create a CallStyle for an incoming call. + * This notification will have a decline and an answer action, will allow a single + * custom {@link Builder#addAction(Action) action}, and will have a default + * {@link Builder#setContentText(CharSequence) content text} for an incoming call. + * + * @param person The person displayed as the caller. + * The person also needs to have a non-empty name associated with it. + * @param declineIntent The intent to be sent when the user taps the decline action + * @param answerIntent The intent to be sent when the user taps the answer action + */ + @NonNull + public static CallStyle forIncomingCall(@NonNull Person person, + @NonNull PendingIntent declineIntent, @NonNull PendingIntent answerIntent) { + return new CallStyle(CALL_TYPE_INCOMING, person, + null /* hangUpIntent */, + requireNonNull(declineIntent, "declineIntent is required"), + requireNonNull(answerIntent, "answerIntent is required") + ); + } + + /** + * Create a CallStyle for an ongoing call. + * This notification will have a hang up action, will allow up to two + * custom {@link Builder#addAction(Action) actions}, and will have a default + * {@link Builder#setContentText(CharSequence) content text} for an ongoing call. + * + * @param person The person displayed as being on the other end of the call. + * The person also needs to have a non-empty name associated with it. + * @param hangUpIntent The intent to be sent when the user taps the hang up action + */ + @NonNull + public static CallStyle forOngoingCall(@NonNull Person person, + @NonNull PendingIntent hangUpIntent) { + return new CallStyle(CALL_TYPE_ONGOING, person, + requireNonNull(hangUpIntent, "hangUpIntent is required"), + null /* declineIntent */, + null /* answerIntent */ + ); + } + + /** + * Create a CallStyle for a call that is being screened. + * This notification will have a hang up and an answer action, will allow a single + * custom {@link Builder#addAction(Action) action}, and will have a default + * {@link Builder#setContentText(CharSequence) content text} for a call that is being + * screened. + * + * @param person The person displayed as the caller. + * The person also needs to have a non-empty name associated with it. + * @param hangUpIntent The intent to be sent when the user taps the hang up action + * @param answerIntent The intent to be sent when the user taps the answer action + */ + @NonNull + public static CallStyle forScreeningCall(@NonNull Person person, + @NonNull PendingIntent hangUpIntent, @NonNull PendingIntent answerIntent) { + return new CallStyle(CALL_TYPE_SCREENING, person, + requireNonNull(hangUpIntent, "hangUpIntent is required"), + null /* declineIntent */, + requireNonNull(answerIntent, "answerIntent is required") + ); + } + + /** + * @param person The person displayed for the incoming call. + * The user also needs to have a non-empty name associated with it. + * @param hangUpIntent The intent to be sent when the user taps the hang up action + * @param declineIntent The intent to be sent when the user taps the decline action + * @param answerIntent The intent to be sent when the user taps the answer action + */ + private CallStyle(int callType, @NonNull Person person, + @Nullable PendingIntent hangUpIntent, @Nullable PendingIntent declineIntent, + @Nullable PendingIntent answerIntent) { + if (person == null || TextUtils.isEmpty(person.getName())) { + throw new IllegalArgumentException("person must have a non-empty a name"); + } + mCallType = callType; + mPerson = person; + mAnswerIntent = answerIntent; + mDeclineIntent = declineIntent; + mHangUpIntent = hangUpIntent; + } + + /** + * Optional icon to be displayed with {@link #setVerificationText(CharSequence) text} + * as a verification status of the caller. + */ + @NonNull + public CallStyle setVerificationIcon(@Nullable Icon verificationIcon) { + mVerificationIcon = verificationIcon; + return this; + } + + /** + * Optional text to be displayed with an {@link #setVerificationIcon(Icon) icon} + * as a verification status of the caller. + */ + @NonNull + public CallStyle setVerificationText(@Nullable CharSequence verificationText) { + mVerificationText = safeCharSequence(verificationText); + return this; + } + + /** + * @hide + */ + public boolean displayCustomViewInline() { + // This is a lie; True is returned to make sure that the custom view is not used + // instead of the template, but it will not actually be included. + return true; + } + + /** + * @hide + */ + @Override + public void purgeResources() { + super.purgeResources(); + if (mVerificationIcon != null) { + mVerificationIcon.convertToAshmem(); + } + } + + /** + * @hide + */ + @Override + public void reduceImageSizes(Context context) { + super.reduceImageSizes(context); + if (mVerificationIcon != null) { + int rightIconSize = context.getResources().getDimensionPixelSize( + ActivityManager.isLowRamDeviceStatic() + ? R.dimen.notification_right_icon_size_low_ram + : R.dimen.notification_right_icon_size); + mVerificationIcon.scaleDownIfNecessary(rightIconSize, rightIconSize); + } + } + + /** + * @hide + */ + @Override + public RemoteViews makeContentView(boolean increasedHeight) { + return makeCallLayout(); + } + + /** + * @hide + */ + @Override + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { + return makeCallLayout(); + } + + /** + * @hide + */ + public RemoteViews makeBigContentView() { + return makeCallLayout(); + } + + @NonNull + private Action makeNegativeAction() { + if (mDeclineIntent == null) { + return makeAction(R.drawable.ic_call_decline, + R.string.call_notification_hang_up_action, + R.color.call_notification_decline_color, mHangUpIntent); + } else { + return makeAction(R.drawable.ic_call_decline, + R.string.call_notification_decline_action, + R.color.call_notification_decline_color, mDeclineIntent); + } + } + + @Nullable + private Action makeAnswerAction() { + return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer, + R.string.call_notification_answer_action, + R.color.call_notification_answer_color, mAnswerIntent); + } + + @NonNull + private Action makeAction(@DrawableRes int icon, @StringRes int title, + @ColorRes int colorRes, PendingIntent intent) { + Action action = new Action.Builder(Icon.createWithResource("", icon), + new SpannableStringBuilder().append(mBuilder.mContext.getString(title), + new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)), + SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE), + intent).build(); + action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true); + return action; + } + + private ArrayList<Action> makeActionsList() { + final Action negativeAction = makeNegativeAction(); + final Action answerAction = makeAnswerAction(); + + ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS); + final Action lastAction; + if (answerAction == null) { + // If there's no answer action, put the hang up / decline action at the end + lastAction = negativeAction; + } else { + // Otherwise put the answer action at the end, and put the decline action at start. + actions.add(negativeAction); + lastAction = answerAction; + } + // For consistency with the standard actions bar, contextual actions are ignored. + for (Action action : Builder.filterOutContextualActions(mBuilder.mActions)) { + if (actions.size() >= MAX_ACTION_BUTTONS - 1) { + break; + } + actions.add(action); + } + actions.add(lastAction); + return actions; + } + + private RemoteViews makeCallLayout() { + Bundle extras = mBuilder.mN.extras; + CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT)); + if (text == null) { + text = getDefaultText(); + } + + // Bind standard template + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .allowActionIcons(true) + .hideLargeIcon(true) + .text(text) + .summaryText(mBuilder.processLegacyText(mVerificationText)); + // TODO(b/179178086): hide the snooze button + RemoteViews contentView = mBuilder.applyStandardTemplate( + mBuilder.getCallLayoutResource(), p, null /* result */); + + // Bind actions. + mBuilder.resetStandardTemplateWithActions(contentView); + bindCallActions(contentView, p); + + // Bind some extra conversation-specific header fields. + mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p); + mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p); + contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE); + bindCallerVerification(contentView, p); + + // Bind some custom CallLayout properties + contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", + mBuilder.isColorized(p) + ? mBuilder.getPrimaryTextColor(p) + : mBuilder.resolveContrastColor(p)); + contentView.setInt(R.id.status_bar_latest_event_content, + "setNotificationBackgroundColor", mBuilder.resolveBackgroundColor(p)); + contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon", + mBuilder.mN.mLargeIcon); + contentView.setBundle(R.id.status_bar_latest_event_content, "setData", + mBuilder.mN.extras); + + return contentView; + } + + private void bindCallActions(RemoteViews view, StandardTemplateParams p) { + view.setViewVisibility(R.id.actions_container, View.VISIBLE); + view.setViewVisibility(R.id.actions, View.VISIBLE); + view.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target, + RemoteViews.MARGIN_BOTTOM, 0); + + // Clear view padding to allow buttons to start on the left edge. + // This must be done before 'setEmphasizedMode' which sets top/bottom margins. + view.setViewPadding(R.id.actions, 0, 0, 0, 0); + // Add an optional indent that will make buttons start at the correct column when + // there is enough space to do so (and fall back to the left edge if not). + view.setInt(R.id.actions, "setCollapsibleIndentDimen", + R.dimen.call_notification_collapsible_indent); + + // Emphasize so that buttons have borders or colored backgrounds + boolean emphasizedMode = true; + view.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode); + // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the + // required actions (Answer, Decline, and Hang Up). + view.setBoolean(R.id.actions, "setPrioritizedWrapMode", true); + + // Create the buttons for the generated actions list. + int i = 0; + for (Action action : makeActionsList()) { + final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p); + if (i > 0) { + // Clear start margin from non-first buttons to reduce the gap between buttons. + // (8dp remaining gap is from all buttons' standard 4dp inset). + button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0); + } + view.addView(R.id.actions, button); + ++i; + } + } + + private void bindCallerVerification(RemoteViews contentView, StandardTemplateParams p) { + if (mVerificationIcon != null) { + contentView.setImageViewIcon(R.id.verification_icon, mVerificationIcon); + contentView.setDrawableTint(R.id.verification_icon, false /* targetBackground */, + mBuilder.getSecondaryTextColor(p), PorterDuff.Mode.SRC_ATOP); + contentView.setViewVisibility(R.id.verification_icon, View.VISIBLE); + } else { + contentView.setViewVisibility(R.id.verification_icon, View.GONE); + } + if (!TextUtils.isEmpty(mVerificationText)) { + contentView.setTextViewText(R.id.verification_text, mVerificationText); + mBuilder.setTextViewColorSecondary(contentView, R.id.verification_text, p); + contentView.setViewVisibility(R.id.verification_text, View.VISIBLE); + } else { + contentView.setViewVisibility(R.id.verification_text, View.GONE); + } + } + + @Nullable + private String getDefaultText() { + switch (mCallType) { + case CALL_TYPE_INCOMING: + return mBuilder.mContext.getString(R.string.call_notification_incoming_text); + case CALL_TYPE_ONGOING: + return mBuilder.mContext.getString(R.string.call_notification_ongoing_text); + case CALL_TYPE_SCREENING: + return mBuilder.mContext.getString(R.string.call_notification_screening_text); + } + return null; + } + + /** + * @hide + */ + public void addExtras(Bundle extras) { + super.addExtras(extras); + extras.putInt(EXTRA_CALL_TYPE, mCallType); + extras.putParcelable(EXTRA_CALL_PERSON, mPerson); + if (mVerificationIcon != null) { + extras.putParcelable(EXTRA_VERIFICATION_ICON, mVerificationIcon); + } + if (mVerificationText != null) { + extras.putCharSequence(EXTRA_VERIFICATION_TEXT, mVerificationText); + } + if (mAnswerIntent != null) { + extras.putParcelable(EXTRA_ANSWER_INTENT, mAnswerIntent); + } + if (mDeclineIntent != null) { + extras.putParcelable(EXTRA_DECLINE_INTENT, mDeclineIntent); + } + if (mHangUpIntent != null) { + extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent); + } + fixTitleAndTextExtras(extras); + } + + private void fixTitleAndTextExtras(Bundle extras) { + CharSequence sender = mPerson != null ? mPerson.getName() : null; + if (sender != null) { + extras.putCharSequence(EXTRA_TITLE, sender); + } + if (extras.getCharSequence(EXTRA_TEXT) == null) { + extras.putCharSequence(EXTRA_TEXT, getDefaultText()); + } + } + + /** + * @hide + */ + @Override + protected void restoreFromExtras(Bundle extras) { + super.restoreFromExtras(extras); + mCallType = extras.getInt(EXTRA_CALL_TYPE); + mPerson = extras.getParcelable(EXTRA_CALL_PERSON); + mVerificationIcon = extras.getParcelable(EXTRA_VERIFICATION_ICON); + mVerificationText = extras.getCharSequence(EXTRA_VERIFICATION_TEXT); + mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT); + mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT); + mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT); + } + + /** + * @hide + */ + @Override + public boolean hasSummaryInHeader() { + return false; + } + + /** + * @hide + */ + @Override + public boolean areNotificationsVisiblyDifferent(Style other) { + if (other == null || getClass() != other.getClass()) { + return true; + } + CallStyle otherS = (CallStyle) other; + return !Objects.equals(mCallType, otherS.mCallType) + || !Objects.equals(mPerson, otherS.mPerson) + || !Objects.equals(mVerificationText, otherS.mVerificationText); + } + } + /** * Notification style for custom views that are decorated by the system * @@ -11376,6 +11874,7 @@ public class Notification implements Parcelable boolean mHideActions; boolean mHideProgress; boolean mPromotePicture; + boolean mAllowActionIcons; CharSequence title; CharSequence text; CharSequence headerTextSecondary; @@ -11392,6 +11891,7 @@ public class Notification implements Parcelable mHideActions = false; mHideProgress = false; mPromotePicture = false; + mAllowActionIcons = false; title = null; text = null; summaryText = null; @@ -11431,6 +11931,11 @@ public class Notification implements Parcelable return this; } + final StandardTemplateParams allowActionIcons(boolean allowActionIcons) { + this.mAllowActionIcons = allowActionIcons; + return this; + } + final StandardTemplateParams promotePicture(boolean promotePicture) { this.mPromotePicture = promotePicture; return this; diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 772833cc6d2d..ac8d3a261ac6 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -39,6 +39,7 @@ import android.os.IBinder; import android.os.Process; import android.os.Trace; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; @@ -60,6 +61,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.WeakHashMap; @@ -174,8 +176,8 @@ public class ResourcesManager { * based on. * * @see #activityResources - * @see #getResources(IBinder, String, String[], String[], String[], Integer, Configuration, - * CompatibilityInfo, ClassLoader, List) + * @see #getResources(IBinder, String, String[], String[], String[], String[], Integer, + * Configuration, CompatibilityInfo, ClassLoader, List) */ public final Configuration overrideConfig = new Configuration(); @@ -482,8 +484,8 @@ public class ResourcesManager { } } - if (key.mOverlayDirs != null) { - for (final String idmapPath : key.mOverlayDirs) { + if (key.mOverlayPaths != null) { + for (final String idmapPath : key.mOverlayPaths) { apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/)); } } @@ -783,14 +785,16 @@ public class ResourcesManager { /** * Creates base resources for a binder token. Calls to - * {@link #getResources(IBinder, String, String[], String[], String[], Integer, Configuration, - * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override - * configurations merged with the one specified here. + * + * {@link #getResources(IBinder, String, String[], String[], String[], String[], Integer, + * Configuration, CompatibilityInfo, ClassLoader, List)} with the same binder token will have + * their override configurations merged with the one specified here. * * @param token Represents an {@link Activity} or {@link WindowContext}. * @param resDir The base resource path. Can be null (only framework resources will be loaded). * @param splitResDirs An array of split resource paths. Can be null. - * @param overlayDirs An array of overlay paths. Can be null. + * @param legacyOverlayDirs An array of overlay APK paths. Can be null. + * @param overlayPaths An array of overlay APK and non-APK paths. Can be null. * @param libDirs An array of resource library paths. Can be null. * @param displayId The ID of the display for which to create the resources. * @param overrideConfig The configuration to apply on top of the base configuration. Can be @@ -804,7 +808,8 @@ public class ResourcesManager { public @Nullable Resources createBaseTokenResources(@NonNull IBinder token, @Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] legacyOverlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, int displayId, @Nullable Configuration overrideConfig, @@ -817,7 +822,7 @@ public class ResourcesManager { final ResourcesKey key = new ResourcesKey( resDir, splitResDirs, - overlayDirs, + combinedOverlayPaths(legacyOverlayDirs, overlayPaths), libDirs, displayId, overrideConfig, @@ -1043,7 +1048,8 @@ public class ResourcesManager { * @param activityToken Represents an Activity. If null, global resources are assumed. * @param resDir The base resource path. Can be null (only framework resources will be loaded). * @param splitResDirs An array of split resource paths. Can be null. - * @param overlayDirs An array of overlay paths. Can be null. + * @param legacyOverlayDirs An array of overlay APK paths. Can be null. + * @param overlayPaths An array of overlay APK and non-APK paths. Can be null. * @param libDirs An array of resource library paths. Can be null. * @param overrideDisplayId The ID of the display for which the returned Resources should be * based. This will cause display-based configuration properties to override those of the base @@ -1063,7 +1069,8 @@ public class ResourcesManager { @Nullable IBinder activityToken, @Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] legacyOverlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, @Nullable Integer overrideDisplayId, @Nullable Configuration overrideConfig, @@ -1075,7 +1082,7 @@ public class ResourcesManager { final ResourcesKey key = new ResourcesKey( resDir, splitResDirs, - overlayDirs, + combinedOverlayPaths(legacyOverlayDirs, overlayPaths), libDirs, overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY, overrideConfig, @@ -1250,7 +1257,7 @@ public class ResourcesManager { // Create the new ResourcesKey with the rebased override config. final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, - oldKey.mSplitResDirs, oldKey.mOverlayDirs, oldKey.mLibDirs, + oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs, displayId, rebasedOverrideConfig, oldKey.mCompatInfo, oldKey.mLoaders); if (DEBUG) { @@ -1393,7 +1400,7 @@ public class ResourcesManager { updatedResourceKeys.put(impl, new ResourcesKey( key.mResDir, key.mSplitResDirs, - key.mOverlayDirs, + key.mOverlayPaths, newLibAssets, key.mDisplayId, key.mOverrideConfiguration, @@ -1423,7 +1430,8 @@ public class ResourcesManager { // ApplicationInfo is mutable, so clone the arrays to prevent outside modification String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs); - String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs); + String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs, + appInfo.overlayPaths); final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>(); final int implCount = mResourceImpls.size(); @@ -1458,6 +1466,39 @@ public class ResourcesManager { } } + /** + * Creates an array with the contents of {@param overlayPaths} and the unique elements of + * {@param resourceDirs}. + * + * {@link ApplicationInfo#resourceDirs} only contains paths of overlays APKs. + * {@link ApplicationInfo#overlayPaths} was created to contain paths of overlay of varying file + * formats. It also contains the contents of {@code resourceDirs} because the order of loaded + * overlays matter. In case {@code resourceDirs} contains overlay APK paths that are not present + * in overlayPaths (perhaps an app inserted an additional overlay path into a + * {@code resourceDirs}), this method is used to combine the contents of {@code resourceDirs} + * that do not exist in {@code overlayPaths}} and {@code overlayPaths}}. + */ + @Nullable + private static String[] combinedOverlayPaths(@Nullable String[] resourceDirs, + @Nullable String[] overlayPaths) { + if (resourceDirs == null) { + return ArrayUtils.cloneOrNull(overlayPaths); + } else if(overlayPaths == null) { + return ArrayUtils.cloneOrNull(resourceDirs); + } else { + final ArrayList<String> paths = new ArrayList<>(); + for (final String path : overlayPaths) { + paths.add(path); + } + for (final String path : resourceDirs) { + if (!paths.contains(path)) { + paths.add(path); + } + } + return paths.toArray(new String[0]); + } + } + private void redirectResourcesToNewImplLocked( @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) { // Bail early if there is no work to do. @@ -1559,7 +1600,7 @@ public class ResourcesManager { final ResourcesKey newKey = new ResourcesKey( oldKey.mResDir, oldKey.mSplitResDirs, - oldKey.mOverlayDirs, + oldKey.mOverlayPaths, oldKey.mLibDirs, oldKey.mDisplayId, oldKey.mOverrideConfiguration, diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 82255c87c971..ff41d1c84e91 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -13328,6 +13328,7 @@ public class DevicePolicyManager { * @return true if the app can grant device sensors-related permissions, false otherwise. */ public boolean canAdminGrantSensorsPermissions() { + throwIfParentInstance("canAdminGrantSensorsPermissions"); return canAdminGrantSensorsPermissionsForUser(myUserId()); } diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java index 1e0653d67ace..920b9fe6a34f 100644 --- a/core/java/android/app/smartspace/SmartspaceTargetEvent.java +++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java @@ -138,6 +138,15 @@ public final class SmartspaceTargetEvent implements Parcelable { dest.writeInt(mEventType); } + @Override + public String toString() { + return "SmartspaceTargetEvent{" + + "mSmartspaceTarget=" + mSmartspaceTarget + + ", mSmartspaceActionId='" + mSmartspaceActionId + '\'' + + ", mEventType=" + mEventType + + '}'; + } + /** * @hide */ diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 2c1e951b6128..30ea5c476191 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -30,7 +30,7 @@ import java.util.Map; interface IUsageStatsManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime, - String callingPackage); + String callingPackage, int userId); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime, String callingPackage); diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index f74d16ee9238..31781ec79203 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.annotation.UserHandleAware; import android.app.Activity; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; @@ -437,11 +438,12 @@ public final class UsageStatsManager { * @see #INTERVAL_YEARLY * @see #INTERVAL_BEST */ + @UserHandleAware public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) { try { @SuppressWarnings("unchecked") ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime, - endTime, mContext.getOpPackageName()); + endTime, mContext.getOpPackageName(), mContext.getUserId()); if (slice != null) { return slice.getList(); } diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java index 960a08755cb8..9007d9d8bbcc 100644 --- a/core/java/android/companion/Association.java +++ b/core/java/android/companion/Association.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import com.android.internal.util.DataClass; +import java.util.Date; import java.util.Objects; /** @@ -39,12 +40,19 @@ public final class Association implements Parcelable { private final @NonNull String mPackageName; private final @Nullable String mDeviceProfile; private final boolean mNotifyOnDeviceNearby; + private final long mTimeApprovedMs; /** @hide */ public int getUserId() { return mUserId; } + private String timeApprovedMsToString() { + return new Date(mTimeApprovedMs).toString(); + } + + + // Code below generated by codegen v1.0.22. @@ -71,7 +79,8 @@ public final class Association implements Parcelable { @NonNull String deviceMacAddress, @NonNull String packageName, @Nullable String deviceProfile, - boolean notifyOnDeviceNearby) { + boolean notifyOnDeviceNearby, + long timeApprovedMs) { this.mUserId = userId; com.android.internal.util.AnnotationValidations.validate( UserIdInt.class, null, mUserId); @@ -83,6 +92,7 @@ public final class Association implements Parcelable { NonNull.class, null, mPackageName); this.mDeviceProfile = deviceProfile; this.mNotifyOnDeviceNearby = notifyOnDeviceNearby; + this.mTimeApprovedMs = timeApprovedMs; // onConstructed(); // You can define this method to get a callback } @@ -107,6 +117,11 @@ public final class Association implements Parcelable { return mNotifyOnDeviceNearby; } + @DataClass.Generated.Member + public long getTimeApprovedMs() { + return mTimeApprovedMs; + } + @Override @DataClass.Generated.Member public String toString() { @@ -118,7 +133,8 @@ public final class Association implements Parcelable { "deviceMacAddress = " + mDeviceMacAddress + ", " + "packageName = " + mPackageName + ", " + "deviceProfile = " + mDeviceProfile + ", " + - "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + + "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + ", " + + "timeApprovedMs = " + timeApprovedMsToString() + " }"; } @@ -139,7 +155,8 @@ public final class Association implements Parcelable { && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress) && Objects.equals(mPackageName, that.mPackageName) && Objects.equals(mDeviceProfile, that.mDeviceProfile) - && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby; + && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby + && mTimeApprovedMs == that.mTimeApprovedMs; } @Override @@ -154,6 +171,7 @@ public final class Association implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mPackageName); _hash = 31 * _hash + Objects.hashCode(mDeviceProfile); _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby); + _hash = 31 * _hash + Long.hashCode(mTimeApprovedMs); return _hash; } @@ -171,6 +189,7 @@ public final class Association implements Parcelable { dest.writeString(mDeviceMacAddress); dest.writeString(mPackageName); if (mDeviceProfile != null) dest.writeString(mDeviceProfile); + dest.writeLong(mTimeApprovedMs); } @Override @@ -190,6 +209,7 @@ public final class Association implements Parcelable { String deviceMacAddress = in.readString(); String packageName = in.readString(); String deviceProfile = (flg & 0x8) == 0 ? null : in.readString(); + long timeApprovedMs = in.readLong(); this.mUserId = userId; com.android.internal.util.AnnotationValidations.validate( @@ -202,6 +222,7 @@ public final class Association implements Parcelable { NonNull.class, null, mPackageName); this.mDeviceProfile = deviceProfile; this.mNotifyOnDeviceNearby = notifyOnDeviceNearby; + this.mTimeApprovedMs = timeApprovedMs; // onConstructed(); // You can define this method to get a callback } @@ -221,10 +242,10 @@ public final class Association implements Parcelable { }; @DataClass.Generated( - time = 1610482674799L, + time = 1612832377589L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/companion/Association.java", - inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\nprivate final long mTimeApprovedMs\npublic int getUserId()\nprivate java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl index 527d8df94ea0..95d3515abb80 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -49,4 +49,6 @@ interface ICompanionDeviceManager { void registerDevicePresenceListenerService(in String packageName, in String deviceAddress); void unregisterDevicePresenceListenerService(in String packageName, in String deviceAddress); + + boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId); } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 6ec11693d69b..01ff4326a800 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -923,6 +923,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String[] resourceDirs; /** + * Contains the contents of {@link #resourceDirs} and along with paths for overlays that may or + * may not be APK packages. + * + * {@hide} + */ + public String[] overlayPaths; + + /** * String retrieved from the seinfo tag found in selinux policy. This value can be set through * the mac_permissions.xml policy construct. This value is used for setting an SELinux security * context on the process as well as its data directory. @@ -1472,6 +1480,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { if (resourceDirs != null) { pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs)); } + if (overlayPaths != null) { + pw.println(prefix + "overlayPaths=" + Arrays.toString(overlayPaths)); + } if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) { pw.println(prefix + "seinfo=" + seInfo); pw.println(prefix + "seinfoUser=" + seInfoUser); @@ -1568,6 +1579,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir); } } + if (overlayPaths != null) { + for (String dir : overlayPaths) { + proto.write(ApplicationInfoProto.OVERLAY_PATHS, dir); + } + } proto.write(ApplicationInfoProto.DATA_DIR, dataDir); proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName); if (!ArrayUtils.isEmpty(splitClassLoaderNames)) { @@ -1717,6 +1733,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { primaryCpuAbi = orig.primaryCpuAbi; secondaryCpuAbi = orig.secondaryCpuAbi; resourceDirs = orig.resourceDirs; + overlayPaths = orig.overlayPaths; seInfo = orig.seInfo; seInfoUser = orig.seInfoUser; sharedLibraryFiles = orig.sharedLibraryFiles; @@ -1803,6 +1820,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString8(primaryCpuAbi); dest.writeString8(secondaryCpuAbi); dest.writeString8Array(resourceDirs); + dest.writeString8Array(overlayPaths); dest.writeString8(seInfo); dest.writeString8(seInfoUser); dest.writeString8Array(sharedLibraryFiles); @@ -1886,6 +1904,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { primaryCpuAbi = source.readString8(); secondaryCpuAbi = source.readString8(); resourceDirs = source.createString8Array(); + overlayPaths = source.createString8Array(); seInfo = source.readString8(); seInfoUser = source.readString8(); sharedLibraryFiles = source.createString8Array(); @@ -2282,7 +2301,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ public String[] getAllApkPaths() { - final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs }; + final String[][] inputLists = { + splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths + }; final List<String> output = new ArrayList<>(10); if (sourceDir != null) { output.add(sourceDir); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e6c0f6a4c2fa..0819d1743ad6 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.split.SplitAssetLoader; import android.content.res.ApkAssets; import android.content.res.AssetManager; @@ -7969,7 +7970,11 @@ public class PackageParser { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.getAllOverlayPaths(); + final OverlayPaths overlayPaths = state.getAllOverlayPaths(); + if (overlayPaths != null) { + ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]); + ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]); + } ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } @@ -8600,6 +8605,7 @@ public class PackageParser { null, null, androidAppInfo.resourceDirs, + androidAppInfo.overlayPaths, androidAppInfo.sharedLibraryFiles, null, null, diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 5cc74c0a1c8e..e115597865b3 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -31,6 +31,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.component.ParsedMainComponent; import android.os.BaseBundle; import android.os.Debug; @@ -53,7 +54,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.Arrays; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; @@ -85,9 +85,10 @@ public class PackageUserState { public ArraySet<String> disabledComponents; public ArraySet<String> enabledComponents; - private String[] overlayPaths; - private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths - private String[] cachedOverlayPaths; + private OverlayPaths overlayPaths; + // Maps library name to overlay paths. + private ArrayMap<String, OverlayPaths> sharedLibraryOverlayPaths; + private OverlayPaths cachedOverlayPaths; @Nullable private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap; @@ -121,8 +122,7 @@ public class PackageUserState { uninstallReason = o.uninstallReason; disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents); enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); - overlayPaths = - o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length); + overlayPaths = o.overlayPaths; if (o.sharedLibraryOverlayPaths != null) { sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths); } @@ -132,25 +132,55 @@ public class PackageUserState { } } - public String[] getOverlayPaths() { + @Nullable + public OverlayPaths getOverlayPaths() { return overlayPaths; } - public void setOverlayPaths(String[] paths) { - overlayPaths = paths; - cachedOverlayPaths = null; + @Nullable + public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() { + return sharedLibraryOverlayPaths; } - public Map<String, String[]> getSharedLibraryOverlayPaths() { - return sharedLibraryOverlayPaths; + /** + * Sets the path of overlays currently enabled for this package and user combination. + * @return true if the path contents differ than what they were previously + */ + @Nullable + public boolean setOverlayPaths(@Nullable OverlayPaths paths) { + if (Objects.equals(paths, overlayPaths)) { + return false; + } + if ((overlayPaths == null && paths.isEmpty()) + || (paths == null && overlayPaths.isEmpty())) { + return false; + } + overlayPaths = paths; + cachedOverlayPaths = null; + return true; } - public void setSharedLibraryOverlayPaths(String library, String[] paths) { + /** + * Sets the path of overlays currently enabled for a library that this package uses. + * + * @return true if the path contents for the library differ than what they were previously + */ + public boolean setSharedLibraryOverlayPaths(@NonNull String library, + @Nullable OverlayPaths paths) { if (sharedLibraryOverlayPaths == null) { sharedLibraryOverlayPaths = new ArrayMap<>(); } - sharedLibraryOverlayPaths.put(library, paths); + final OverlayPaths currentPaths = sharedLibraryOverlayPaths.get(library); + if (Objects.equals(paths, currentPaths)) { + return false; + } cachedOverlayPaths = null; + if (paths == null || paths.isEmpty()) { + return sharedLibraryOverlayPaths.remove(library) != null; + } else { + sharedLibraryOverlayPaths.put(library, paths); + return true; + } } /** @@ -332,35 +362,21 @@ public class PackageUserState { return isComponentEnabled; } - public String[] getAllOverlayPaths() { + public OverlayPaths getAllOverlayPaths() { if (overlayPaths == null && sharedLibraryOverlayPaths == null) { return null; } - if (cachedOverlayPaths != null) { return cachedOverlayPaths; } - - final LinkedHashSet<String> paths = new LinkedHashSet<>(); - if (overlayPaths != null) { - final int N = overlayPaths.length; - for (int i = 0; i < N; i++) { - paths.add(overlayPaths[i]); - } - } - + final OverlayPaths.Builder newPaths = new OverlayPaths.Builder(); + newPaths.addAll(overlayPaths); if (sharedLibraryOverlayPaths != null) { - for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) { - if (libOverlayPaths != null) { - final int N = libOverlayPaths.length; - for (int i = 0; i < N; i++) { - paths.add(libOverlayPaths[i]); - } - } + for (final OverlayPaths libOverlayPaths : sharedLibraryOverlayPaths.values()) { + newPaths.addAll(libOverlayPaths); } } - - cachedOverlayPaths = paths.toArray(new String[0]); + cachedOverlayPaths = newPaths.build(); return cachedOverlayPaths; } diff --git a/core/java/android/content/pm/overlay/OverlayPaths.java b/core/java/android/content/pm/overlay/OverlayPaths.java new file mode 100644 index 000000000000..a4db733af013 --- /dev/null +++ b/core/java/android/content/pm/overlay/OverlayPaths.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm.overlay; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** @hide */ +@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false, + genEqualsHashCode = true, genToString = true) +public class OverlayPaths { + /** + * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}. + * Only contains paths to APKs of overlays that can have their idmap resolved from their base + * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap + * path. + */ + @NonNull + private final List<String> mResourceDirs = new ArrayList<>(); + + /** + * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}. + * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays + * that are not APKs. + */ + @NonNull + private final List<String> mOverlayPaths = new ArrayList<>(); + + public static class Builder { + final OverlayPaths mPaths = new OverlayPaths(); + + /** + * Adds a non-APK path to the contents of {@link OverlayPaths#getOverlayPaths()}. + */ + public Builder addNonApkPath(@NonNull String idmapPath) { + mPaths.mOverlayPaths.add(idmapPath); + return this; + } + + /** + * Adds a overlay APK path to the contents of {@link OverlayPaths#getResourceDirs()} and + * {@link OverlayPaths#getOverlayPaths()}. + */ + public Builder addApkPath(@NonNull String overlayPath) { + addUniquePath(mPaths.mResourceDirs, overlayPath); + addUniquePath(mPaths.mOverlayPaths, overlayPath); + return this; + } + + public Builder addAll(@Nullable OverlayPaths other) { + if (other != null) { + for (final String path : other.getResourceDirs()) { + addUniquePath(mPaths.mResourceDirs, path); + } + for (final String path : other.getOverlayPaths()) { + addUniquePath(mPaths.mOverlayPaths, path); + } + } + return this; + } + + public OverlayPaths build() { + return mPaths; + } + + private static void addUniquePath(@NonNull List<String> paths, @NonNull String path) { + if (!paths.contains(path)) { + paths.add(path); + } + } + } + + /** + * Returns whether {@link #getOverlayPaths()} and {@link #getOverlayPaths} are empty. + */ + public boolean isEmpty() { + return mResourceDirs.isEmpty() && mOverlayPaths.isEmpty(); + } + + private OverlayPaths() { + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}. + * Only contains paths to APKs of overlays that can have their idmap resolved from their base + * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap + * path. + */ + @DataClass.Generated.Member + public @NonNull List<String> getResourceDirs() { + return mResourceDirs; + } + + /** + * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}. + * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays + * that are not APKs. + */ + @DataClass.Generated.Member + public @NonNull List<String> getOverlayPaths() { + return mOverlayPaths; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "OverlayPaths { " + + "resourceDirs = " + mResourceDirs + ", " + + "overlayPaths = " + mOverlayPaths + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(OverlayPaths other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + OverlayPaths that = (OverlayPaths) o; + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mResourceDirs, that.mResourceDirs) + && Objects.equals(mOverlayPaths, that.mOverlayPaths); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mResourceDirs); + _hash = 31 * _hash + Objects.hashCode(mOverlayPaths); + return _hash; + } + + @DataClass.Generated( + time = 1612307813586L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java", + inputSignatures = "private final @android.annotation.NonNull java.util.List<java.lang.String> mResourceDirs\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mOverlayPaths\npublic boolean isEmpty()\nclass OverlayPaths extends java.lang.Object implements []\nfinal android.content.pm.overlay.OverlayPaths mPaths\npublic android.content.pm.overlay.OverlayPaths.Builder addNonApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addAll(android.content.pm.overlay.OverlayPaths)\npublic android.content.pm.overlay.OverlayPaths build()\nprivate static void addUniquePath(java.util.List<java.lang.String>,java.lang.String)\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index b7365b3eaf61..fb0d90490567 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -41,6 +41,7 @@ import android.content.pm.SELinuxUtil; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.pm.SigningInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.component.ComponentParseUtils; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedAttribution; @@ -412,7 +413,11 @@ public class PackageInfoWithoutStateUtils { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.getAllOverlayPaths(); + final OverlayPaths overlayPaths = state.getAllOverlayPaths(); + if (overlayPaths != null) { + ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]); + ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]); + } return ai; } diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java index 05769ddc5397..99b56a82173e 100644 --- a/core/java/android/content/res/ResourcesKey.java +++ b/core/java/android/content/res/ResourcesKey.java @@ -38,7 +38,7 @@ public final class ResourcesKey { public final String[] mSplitResDirs; @Nullable - public final String[] mOverlayDirs; + public final String[] mOverlayPaths; @Nullable public final String[] mLibDirs; @@ -67,7 +67,7 @@ public final class ResourcesKey { public ResourcesKey(@Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, int overrideDisplayId, @Nullable Configuration overrideConfig, @@ -75,7 +75,7 @@ public final class ResourcesKey { @Nullable ResourcesLoader[] loader) { mResDir = resDir; mSplitResDirs = splitResDirs; - mOverlayDirs = overlayDirs; + mOverlayPaths = overlayPaths; mLibDirs = libDirs; mLoaders = (loader != null && loader.length == 0) ? null : loader; mDisplayId = overrideDisplayId; @@ -86,7 +86,7 @@ public final class ResourcesKey { int hash = 17; hash = 31 * hash + Objects.hashCode(mResDir); hash = 31 * hash + Arrays.hashCode(mSplitResDirs); - hash = 31 * hash + Arrays.hashCode(mOverlayDirs); + hash = 31 * hash + Arrays.hashCode(mOverlayPaths); hash = 31 * hash + Arrays.hashCode(mLibDirs); hash = 31 * hash + Objects.hashCode(mDisplayId); hash = 31 * hash + Objects.hashCode(mOverrideConfiguration); @@ -98,12 +98,12 @@ public final class ResourcesKey { @UnsupportedAppUsage public ResourcesKey(@Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, int displayId, @Nullable Configuration overrideConfig, @Nullable CompatibilityInfo compatInfo) { - this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo, + this(resDir, splitResDirs, overlayPaths, libDirs, displayId, overrideConfig, compatInfo, null); } @@ -115,7 +115,7 @@ public final class ResourcesKey { if (mResDir != null && mResDir.startsWith(path)) { return true; } else { - return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayDirs, path) + return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayPaths, path) || anyStartsWith(mLibDirs, path); } } @@ -154,7 +154,7 @@ public final class ResourcesKey { if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) { return false; } - if (!Arrays.equals(mOverlayDirs, peer.mOverlayDirs)) { + if (!Arrays.equals(mOverlayPaths, peer.mOverlayPaths)) { return false; } if (!Arrays.equals(mLibDirs, peer.mLibDirs)) { @@ -186,8 +186,8 @@ public final class ResourcesKey { } builder.append("]"); builder.append(" mOverlayDirs=["); - if (mOverlayDirs != null) { - builder.append(TextUtils.join(",", mOverlayDirs)); + if (mOverlayPaths != null) { + builder.append(TextUtils.join(",", mOverlayPaths)); } builder.append("]"); builder.append(" mLibDirs=["); diff --git a/tools/hiddenapi/Android.bp b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl index e0eb06cbea7f..4dc41f149328 100644 --- a/tools/hiddenapi/Android.bp +++ b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.hardware.face; -python_binary_host { - name: "merge_csv", - main: "merge_csv.py", - srcs: ["merge_csv.py"], - version: { - py2: { - enabled: false, - }, - py3: { - enabled: true, - embedded_launcher: true - }, - }, -} +/** + * @hide + */ +parcelable FaceAuthenticationFrame; diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java index 3a0e09b70b50..092359c99173 100644 --- a/core/java/android/hardware/face/FaceDataFrame.java +++ b/core/java/android/hardware/face/FaceDataFrame.java @@ -63,6 +63,22 @@ public final class FaceDataFrame implements Parcelable { } /** + * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}. + * + * @param acquiredInfo An integer corresponding to a known acquired message. + * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless + * {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}. + */ + public FaceDataFrame(int acquiredInfo, int vendorCode) { + mAcquiredInfo = acquiredInfo; + mVendorCode = vendorCode; + mPan = 0f; + mTilt = 0f; + mDistance = 0f; + mIsCancellable = false; + } + + /** * @return An integer corresponding to a known acquired message. * * @see android.hardware.biometrics.BiometricFaceConstants diff --git a/core/java/android/hardware/face/FaceEnrollFrame.aidl b/core/java/android/hardware/face/FaceEnrollFrame.aidl new file mode 100644 index 000000000000..b8546812beeb --- /dev/null +++ b/core/java/android/hardware/face/FaceEnrollFrame.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.face; + +/** + * @hide + */ +parcelable FaceEnrollFrame; diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java index 03dba551aae2..de717fbe46b4 100644 --- a/core/java/android/hardware/face/FaceEnrollStage.java +++ b/core/java/android/hardware/face/FaceEnrollStage.java @@ -28,6 +28,7 @@ import java.lang.annotation.RetentionPolicy; */ @Retention(RetentionPolicy.SOURCE) @IntDef({ + FaceEnrollStage.UNKNOWN, FaceEnrollStage.FIRST_FRAME_RECEIVED, FaceEnrollStage.WAITING_FOR_CENTERING, FaceEnrollStage.HOLD_STILL_IN_CENTER, @@ -37,6 +38,11 @@ import java.lang.annotation.RetentionPolicy; }) public @interface FaceEnrollStage { /** + * The current enrollment stage is not known. + */ + int UNKNOWN = -1; + + /** * Enrollment has just begun. No action is needed from the user yet. */ int FIRST_FRAME_RECEIVED = 0; diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 588bc01d7d42..f3da6a9d4a03 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -71,17 +71,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan private static final int MSG_FACE_DETECTED = 109; private static final int MSG_CHALLENGE_INTERRUPTED = 110; private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111; + private static final int MSG_AUTHENTICATION_FRAME = 112; + private static final int MSG_ENROLLMENT_FRAME = 113; private final IFaceService mService; private final Context mContext; private IBinder mToken = new Binder(); - private AuthenticationCallback mAuthenticationCallback; - private FaceDetectionCallback mFaceDetectionCallback; - private EnrollmentCallback mEnrollmentCallback; - private RemovalCallback mRemovalCallback; - private SetFeatureCallback mSetFeatureCallback; - private GetFeatureCallback mGetFeatureCallback; - private GenerateChallengeCallback mGenerateChallengeCallback; + @Nullable private AuthenticationCallback mAuthenticationCallback; + @Nullable private FaceDetectionCallback mFaceDetectionCallback; + @Nullable private EnrollmentCallback mEnrollmentCallback; + @Nullable private RemovalCallback mRemovalCallback; + @Nullable private SetFeatureCallback mSetFeatureCallback; + @Nullable private GetFeatureCallback mGetFeatureCallback; + @Nullable private GenerateChallengeCallback mGenerateChallengeCallback; private CryptoObject mCryptoObject; private Face mRemovalFace; private Handler mHandler; @@ -154,6 +156,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public void onChallengeInterruptFinished(int sensorId) { mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget(); } + + @Override + public void onAuthenticationFrame(FaceAuthenticationFrame frame) { + mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget(); + } + + @Override + public void onEnrollmentFrame(FaceEnrollFrame frame) { + mHandler.obtainMessage(MSG_ENROLLMENT_FRAME, frame).sendToTarget(); + } }; /** @@ -1248,6 +1260,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case MSG_CHALLENGE_INTERRUPT_FINISHED: sendChallengeInterruptFinished((int) msg.obj /* sensorId */); break; + case MSG_AUTHENTICATION_FRAME: + sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */); + break; + case MSG_ENROLLMENT_FRAME: + sendEnrollmentFrame((FaceEnrollFrame) msg.obj /* frame */); + break; default: Slog.w(TAG, "Unknown message: " + msg.what); } @@ -1349,15 +1367,52 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan private void sendAcquiredResult(int acquireInfo, int vendorCode) { if (mAuthenticationCallback != null) { + final FaceAuthenticationFrame frame = new FaceAuthenticationFrame( + new FaceDataFrame(acquireInfo, vendorCode)); + sendAuthenticationFrame(frame); + } else if (mEnrollmentCallback != null) { + final FaceEnrollFrame frame = new FaceEnrollFrame( + null /* cell */, + FaceEnrollStage.UNKNOWN, + new FaceDataFrame(acquireInfo, vendorCode)); + sendEnrollmentFrame(frame); + } + } + + private void sendAuthenticationFrame(@Nullable FaceAuthenticationFrame frame) { + if (frame == null) { + Slog.w(TAG, "Received null authentication frame"); + } else if (mAuthenticationCallback != null) { + // TODO(b/178414967): Send additional frame data to callback + final int acquireInfo = frame.getData().getAcquiredInfo(); + final int vendorCode = frame.getData().getVendorCode(); + final int helpCode = getHelpCode(acquireInfo, vendorCode); + final String helpMessage = getAcquiredString(mContext, acquireInfo, vendorCode); mAuthenticationCallback.onAuthenticationAcquired(acquireInfo); + + // Ensure that only non-null help messages are sent. + if (helpMessage != null) { + mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage); + } } - final String msg = getAcquiredString(mContext, acquireInfo, vendorCode); - final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR - ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo; - if (mEnrollmentCallback != null) { - mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg); - } else if (mAuthenticationCallback != null && msg != null) { - mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg); + } + + private void sendEnrollmentFrame(@Nullable FaceEnrollFrame frame) { + if (frame == null) { + Slog.w(TAG, "Received null enrollment frame"); + } else if (mEnrollmentCallback != null) { + // TODO(b/178414967): Send additional frame data to callback + final int acquireInfo = frame.getData().getAcquiredInfo(); + final int vendorCode = frame.getData().getVendorCode(); + final int helpCode = getHelpCode(acquireInfo, vendorCode); + final String helpMessage = getAcquiredString(mContext, acquireInfo, vendorCode); + mEnrollmentCallback.onEnrollmentHelp(helpCode, helpMessage); } } + + private static int getHelpCode(int acquireInfo, int vendorCode) { + return acquireInfo == FACE_ACQUIRED_VENDOR + ? vendorCode + FACE_ACQUIRED_VENDOR_BASE + : acquireInfo; + } } diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl index bd4d3a0b7017..2ef1430a2f99 100644 --- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl +++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl @@ -16,6 +16,8 @@ package android.hardware.face; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticationFrame; +import android.hardware.face.FaceEnrollFrame; /** * Communication channel from the FaceService back to FaceAuthenticationManager. @@ -34,4 +36,6 @@ oneway interface IFaceServiceReceiver { void onChallengeGenerated(int sensorId, long challenge); void onChallengeInterrupted(int sensorId); void onChallengeInterruptFinished(int sensorId); + void onAuthenticationFrame(in FaceAuthenticationFrame frame); + void onEnrollmentFrame(in FaceEnrollFrame frame); } diff --git a/core/java/android/net/VpnTransportInfo.java b/core/java/android/net/VpnTransportInfo.java new file mode 100644 index 000000000000..082fa58f8ac2 --- /dev/null +++ b/core/java/android/net/VpnTransportInfo.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +import com.android.internal.util.MessageUtils; + +import java.util.Objects; + +/** @hide */ +public final class VpnTransportInfo implements TransportInfo, Parcelable { + private static final SparseArray<String> sTypeToString = + MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"}); + + /** Type of this VPN. */ + @VpnManager.VpnType public final int type; + + public VpnTransportInfo(@VpnManager.VpnType int type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VpnTransportInfo)) return false; + + VpnTransportInfo that = (VpnTransportInfo) o; + return this.type == that.type; + } + + @Override + public int hashCode() { + return Objects.hash(type); + } + + @Override + public String toString() { + final String typeString = sTypeToString.get(type, "VPN_TYPE_???"); + return String.format("VpnTransportInfo{%s}", typeString); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(type); + } + + public static final @NonNull Creator<VpnTransportInfo> CREATOR = + new Creator<VpnTransportInfo>() { + public VpnTransportInfo createFromParcel(Parcel in) { + return new VpnTransportInfo(in.readInt()); + } + public VpnTransportInfo[] newArray(int size) { + return new VpnTransportInfo[size]; + } + }; +} diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index fa090f59a8b9..1a38338c26aa 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -28,8 +28,10 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; import java.io.IOException; +import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -67,8 +69,7 @@ import java.util.concurrent.Executor; public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); - @VisibleForTesting - public static final Map< + private static final Map< VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); @@ -88,6 +89,18 @@ public class VcnManager { mService = requireNonNull(service, "missing service"); } + /** + * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes. + * + * @hide + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + @NonNull + public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + getAllPolicyListeners() { + return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS); + } + // TODO: Make setVcnConfig(), clearVcnConfig() Public API /** * Sets the VCN configuration for a given subscription group. diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index af8e8de85d9f..305815ff9e51 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -33,20 +33,33 @@ public final class BatteryUsageStats implements Parcelable { private final int mDischargePercentage; private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers; private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers; + private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers; private BatteryUsageStats(@NonNull Builder builder) { mConsumedPower = builder.mConsumedPower; mDischargePercentage = builder.mDischargePercentage; + int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size(); mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount); for (int i = 0; i < uidBatteryConsumerCount; i++) { - mUidBatteryConsumers.add(builder.mUidBatteryConsumerBuilders.valueAt(i).build()); + UidBatteryConsumer.Builder uidBatteryConsumerBuilder = + builder.mUidBatteryConsumerBuilders.valueAt(i); + if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) { + mUidBatteryConsumers.add(uidBatteryConsumerBuilder.build()); + } } + int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size(); mSystemBatteryConsumers = new ArrayList<>(systemBatteryConsumerCount); for (int i = 0; i < systemBatteryConsumerCount; i++) { mSystemBatteryConsumers.add(builder.mSystemBatteryConsumerBuilders.valueAt(i).build()); } + + int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size(); + mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount); + for (int i = 0; i < userBatteryConsumerCount; i++) { + mUserBatteryConsumers.add(builder.mUserBatteryConsumerBuilders.valueAt(i).build()); + } } /** @@ -75,6 +88,11 @@ public final class BatteryUsageStats implements Parcelable { return mSystemBatteryConsumers; } + @NonNull + public List<UserBatteryConsumer> getUserBatteryConsumers() { + return mUserBatteryConsumers; + } + @Override public int describeContents() { return 0; @@ -85,6 +103,8 @@ public final class BatteryUsageStats implements Parcelable { source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader()); mSystemBatteryConsumers = new ArrayList<>(); source.readParcelableList(mSystemBatteryConsumers, getClass().getClassLoader()); + mUserBatteryConsumers = new ArrayList<>(); + source.readParcelableList(mUserBatteryConsumers, getClass().getClassLoader()); mConsumedPower = source.readDouble(); mDischargePercentage = source.readInt(); } @@ -93,6 +113,7 @@ public final class BatteryUsageStats implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelableList(mUidBatteryConsumers, flags); dest.writeParcelableList(mSystemBatteryConsumers, flags); + dest.writeParcelableList(mUserBatteryConsumers, flags); dest.writeDouble(mConsumedPower); dest.writeInt(mDischargePercentage); } @@ -120,6 +141,8 @@ public final class BatteryUsageStats implements Parcelable { new SparseArray<>(); private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders = new SparseArray<>(); + private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders = + new SparseArray<>(); public Builder(int customPowerComponentCount, int customTimeComponentCount) { mCustomPowerComponentCount = customPowerComponentCount; @@ -137,7 +160,6 @@ public final class BatteryUsageStats implements Parcelable { /** * Sets the battery discharge amount since BatteryStats reset as percentage of the full * charge. - * */ @SuppressLint("PercentageInt") // See b/174188159 @NonNull @@ -173,8 +195,8 @@ public final class BatteryUsageStats implements Parcelable { } /** - * Creates or returns a exiting UidBatteryConsumer, which represents battery attribution - * data for an individual UID. + * Creates or returns a exiting SystemBatteryConsumer, which represents battery attribution + * data for a specific drain type. */ @NonNull public SystemBatteryConsumer.Builder getOrCreateSystemBatteryConsumerBuilder( @@ -188,6 +210,21 @@ public final class BatteryUsageStats implements Parcelable { return builder; } + /** + * Creates or returns a exiting UserBatteryConsumer, which represents battery attribution + * data for an individual {@link UserHandle}. + */ + @NonNull + public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) { + UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId); + if (builder == null) { + builder = new UserBatteryConsumer.Builder(mCustomPowerComponentCount, + mCustomTimeComponentCount, userId); + mUserBatteryConsumerBuilders.put(userId, builder); + } + return builder; + } + @NonNull public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() { return mUidBatteryConsumerBuilders; diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 48e7389e5ff2..5b5fe1d15a82 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; +import android.util.IntArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -53,9 +54,13 @@ public final class BatteryUsageStatsQuery implements Parcelable { public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1; private final int mFlags; + @NonNull + private final int[] mUserIds; private BatteryUsageStatsQuery(@NonNull Builder builder) { mFlags = builder.mFlags; + mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray() + : new int[]{UserHandle.USER_ALL}; } @BatteryUsageStatsFlags @@ -63,13 +68,28 @@ public final class BatteryUsageStatsQuery implements Parcelable { return mFlags; } + /** + * Returns an array of users for which the attribution is requested. It may + * contain {@link UserHandle#USER_ALL} to indicate that the attribution + * should be performed for all users. Battery consumed by users <b>not</b> included + * in this array will be returned in the aggregated form as {@link UserBatteryConsumer}'s. + */ + @NonNull + public int[] getUserIds() { + return mUserIds; + } + private BatteryUsageStatsQuery(Parcel in) { mFlags = in.readInt(); + mUserIds = new int[in.readInt()]; + in.readIntArray(mUserIds); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mFlags); + dest.writeInt(mUserIds.length); + dest.writeIntArray(mUserIds); } @Override @@ -96,6 +116,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ public static final class Builder { private int mFlags; + private IntArray mUserIds; /** * Builds a read-only BatteryUsageStatsQuery object. @@ -105,6 +126,18 @@ public final class BatteryUsageStatsQuery implements Parcelable { } /** + * Add a user whose battery stats should be included in the battery usage stats. + * {@link UserHandle#USER_ALL} will be used by default if no users are added explicitly. + */ + public Builder addUser(@NonNull UserHandle userHandle) { + if (mUserIds == null) { + mUserIds = new IntArray(1); + } + mUserIds.add(userHandle.getIdentifier()); + return this; + } + + /** * Sets flags to modify the behavior of {@link BatteryStatsManager#getBatteryUsageStats}. */ public Builder setFlags(@BatteryUsageStatsFlags int flags) { diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 9584bc74d6f2..6a76da2cc13d 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -2557,6 +2557,14 @@ public final class Debug public static native long getZramFreeKb(); /** + * Return total memory size in kilobytes for exported DMA-BUFs or -1 if + * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read. + * + * @hide + */ + public static native long getDmabufTotalExportedKb(); + + /** * Return memory size in kilobytes allocated for ION heaps or -1 if * /sys/kernel/ion/total_heaps_kb could not be read. * @@ -2565,6 +2573,14 @@ public final class Debug public static native long getIonHeapsSizeKb(); /** + * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if + * /sys/kernel/dma_heap/total_pools_kb could not be read. + * + * @hide + */ + public static native long getDmabufHeapPoolsSizeKb(); + + /** * Return memory size in kilobytes allocated for ION pools or -1 if * /sys/kernel/ion/total_pools_kb could not be read. * @@ -2573,13 +2589,13 @@ public final class Debug public static native long getIonPoolsSizeKb(); /** - * Return ION memory mapped by processes in kB. + * Return DMA-BUF memory mapped by processes in kB. * Notes: * * Warning: Might impact performance as it reads /proc/<pid>/maps files for each process. * * @hide */ - public static native long getIonMappedSizeKb(); + public static native long getDmabufMappedSizeKb(); /** * Return memory size in kilobytes used by GPU. diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 518e29d51091..62951246a43b 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -18,6 +18,7 @@ package android.os; import android.Manifest; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.AppGlobals; @@ -836,6 +837,21 @@ public class Environment { public static String DIRECTORY_AUDIOBOOKS = "Audiobooks"; /** + * Standard directory in which to place any audio files which are + * recordings. + */ + @NonNull + // The better way is that expose a static method getRecordingDirectories. + // But since it's an existing API surface and developers already + // used to DIRECTORY_* constants, we should keep using this pattern + // for consistency. We use SuppressLint here to avoid exposing a final + // field. A final field will prevent us from ever changing the value of + // DIRECTORY_RECORDINGS. Not that it's likely that we will ever need to + // change it, but it's better to have such option. + @SuppressLint({"MutableBareField", "AllUpper"}) + public static String DIRECTORY_RECORDINGS = "Recordings"; + + /** * List of standard storage directories. * <p> * Each of its values have its own constant: @@ -851,6 +867,7 @@ public class Environment { * <li>{@link #DIRECTORY_DCIM} * <li>{@link #DIRECTORY_DOCUMENTS} * <li>{@link #DIRECTORY_AUDIOBOOKS} + * <li>{@link #DIRECTORY_RECORDINGS} * </ul> * @hide */ @@ -866,6 +883,7 @@ public class Environment { DIRECTORY_DCIM, DIRECTORY_DOCUMENTS, DIRECTORY_AUDIOBOOKS, + DIRECTORY_RECORDINGS, }; /** @@ -891,6 +909,7 @@ public class Environment { /** {@hide} */ public static final int HAS_DCIM = 1 << 8; /** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9; /** {@hide} */ public static final int HAS_AUDIOBOOKS = 1 << 10; + /** {@hide} */ public static final int HAS_RECORDINGS = 1 << 11; /** {@hide} */ public static final int HAS_ANDROID = 1 << 16; /** {@hide} */ public static final int HAS_OTHER = 1 << 17; @@ -921,6 +940,7 @@ public class Environment { else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM; else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS; else if (DIRECTORY_AUDIOBOOKS.equals(name)) res |= HAS_AUDIOBOOKS; + else if (DIRECTORY_RECORDINGS.equals(name)) res |= HAS_RECORDINGS; else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID; else res |= HAS_OTHER; } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 39e3e146f45b..8068c872c4bb 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -1442,7 +1442,7 @@ public class Process { * * @hide */ - public static boolean hasFileLocks(int pid) throws IOException { + public static boolean hasFileLocks(int pid) throws Exception { BufferedReader br = null; try { @@ -1454,8 +1454,13 @@ public class Process { for (int i = 0; i < 5 && st.hasMoreTokens(); i++) { String str = st.nextToken(); - if (i == 4 && Integer.parseInt(str) == pid) { - return true; + try { + if (i == 4 && Integer.parseInt(str) == pid) { + return true; + } + } catch (NumberFormatException nfe) { + throw new Exception("Exception parsing /proc/locks at \" " + + line + " \", token #" + i); } } } diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index 3ffaa9e4b1f0..d1ca6a560c3f 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -96,6 +96,7 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela private final int mUid; private String mPackageWithHighestDrain; private boolean mSystemComponent; + private boolean mExcludeFromBatteryUsageStats; public Builder(int customPowerComponentCount, int customTimeComponentCount, BatteryStats.Uid batteryStatsUid) { @@ -113,14 +114,6 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela } /** - * Creates a read-only object out of the Builder values. - */ - @NonNull - public UidBatteryConsumer build() { - return new UidBatteryConsumer(this); - } - - /** * Sets the name of the package owned by this UID that consumed the highest amount * of power since BatteryStats reset. */ @@ -131,12 +124,27 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela } /** - * Marks the UidBatteryConsumer as part of the system. For example, - * the UidBatteryConsumer with the UID {@link Process#BLUETOOTH_UID} is considered - * as a system component. + * Marks the UidBatteryConsumer for exclusion from the result set. */ - public void setSystemComponent(boolean systemComponent) { - mSystemComponent = systemComponent; + public Builder excludeFromBatteryUsageStats() { + mExcludeFromBatteryUsageStats = true; + return this; + } + + /** + * Returns true if this UidBatteryConsumer must be excluded from the + * BatteryUsageStats. + */ + public boolean isExcludedFromBatteryUsageStats() { + return mExcludeFromBatteryUsageStats; + } + + /** + * Creates a read-only object out of the Builder values. + */ + @NonNull + public UidBatteryConsumer build() { + return new UidBatteryConsumer(this); } } } diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java new file mode 100644 index 000000000000..94e567f368b0 --- /dev/null +++ b/core/java/android/os/UserBatteryConsumer.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.os; + +import android.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * Contains power consumption data attributed to a {@link UserHandle}. + * + * {@hide} + */ +public class UserBatteryConsumer extends BatteryConsumer implements Parcelable { + private final int mUserId; + + public int getUserId() { + return mUserId; + } + + private UserBatteryConsumer(@NonNull UserBatteryConsumer.Builder builder) { + super(builder.mPowerComponentsBuilder.build()); + mUserId = builder.mUserId; + } + + private UserBatteryConsumer(Parcel in) { + super(new PowerComponents(in)); + mUserId = in.readInt(); + } + + /** + * Writes the contents into a Parcel. + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mUserId); + } + + public static final Creator<UserBatteryConsumer> CREATOR = + new Creator<UserBatteryConsumer>() { + @Override + public UserBatteryConsumer createFromParcel(Parcel in) { + return new UserBatteryConsumer(in); + } + + @Override + public UserBatteryConsumer[] newArray(int size) { + return new UserBatteryConsumer[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + /** + * Builder for UserBatteryConsumer. + */ + public static final class Builder extends BaseBuilder<Builder> { + private final int mUserId; + private List<UidBatteryConsumer.Builder> mUidBatteryConsumers; + + Builder(int customPowerComponentCount, int customTimeComponentCount, int userId) { + super(customPowerComponentCount, customTimeComponentCount); + mUserId = userId; + } + + /** + * Add a UidBatteryConsumer to this UserBatteryConsumer. + * <p> + * Calculated power and duration components of the added UID battery consumers + * are aggregated at the time the UserBatteryConsumer is built by the {@link #build()} + * method. + * </p> + */ + public void addUidBatteryConsumer(UidBatteryConsumer.Builder uidBatteryConsumerBuilder) { + if (mUidBatteryConsumers == null) { + mUidBatteryConsumers = new ArrayList<>(); + } + mUidBatteryConsumers.add(uidBatteryConsumerBuilder); + } + + /** + * Creates a read-only object out of the Builder values. + */ + @NonNull + public UserBatteryConsumer build() { + if (mUidBatteryConsumers != null) { + for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) { + UidBatteryConsumer.Builder uidBatteryConsumer = mUidBatteryConsumers.get(i); + mPowerComponentsBuilder.addPowerAndDuration( + uidBatteryConsumer.mPowerComponentsBuilder); + } + } + return new UserBatteryConsumer(this); + } + } +} diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java index faa90d93719f..86b20c424869 100644 --- a/core/java/android/provider/FontsContract.java +++ b/core/java/android/provider/FontsContract.java @@ -350,6 +350,9 @@ public class FontsContract { return cachedTypeface; } + Log.w(TAG, "Platform version of downloadable fonts is deprecated. Please use" + + " androidx version instead."); + synchronized (sLock) { // It is possible that Font is loaded during the thread sleep time // re-check the cache to avoid re-loading the font diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a29fcb17b083..ef81ed768e56 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -44,6 +44,7 @@ import android.content.Context; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; @@ -89,8 +90,11 @@ import com.android.internal.util.Preconditions; import com.android.internal.widget.ILockSettings; import java.io.IOException; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; @@ -99,7 +103,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; - /** * The Settings provider contains global system-level device preferences. */ @@ -2490,7 +2493,7 @@ public final class Settings { public static final String EXTRA_NUMBER_OF_CERTIFICATES = "android.settings.extra.number_of_certificates"; - private static final String JID_RESOURCE_PREFIX = "android"; + private static final String SYSTEM_PACKAGE_NAME = "android"; public static final String AUTHORITY = "settings"; @@ -2659,22 +2662,30 @@ public final class Settings { private final String mCallListCommand; private final String mCallSetAllCommand; + private final ArraySet<String> mReadableFields; + private final ArraySet<String> mAllFields; + @GuardedBy("this") private GenerationTracker mGenerationTracker; - public NameValueCache(Uri uri, String getCommand, String setCommand, - ContentProviderHolder providerHolder) { - this(uri, getCommand, setCommand, null, null, providerHolder); + <T extends NameValueTable> NameValueCache(Uri uri, String getCommand, + String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) { + this(uri, getCommand, setCommand, null, null, providerHolder, + callerClass); } - NameValueCache(Uri uri, String getCommand, String setCommand, String listCommand, - String setAllCommand, ContentProviderHolder providerHolder) { + private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand, + String setCommand, String listCommand, String setAllCommand, + ContentProviderHolder providerHolder, Class<T> callerClass) { mUri = uri; mCallGetCommand = getCommand; mCallSetCommand = setCommand; mCallListCommand = listCommand; mCallSetAllCommand = setAllCommand; mProviderHolder = providerHolder; + mReadableFields = new ArraySet<>(); + mAllFields = new ArraySet<>(); + getPublicSettingsForClass(callerClass, mAllFields, mReadableFields); } public boolean putStringForUser(ContentResolver cr, String name, String value, @@ -2726,6 +2737,19 @@ public final class Settings { @UnsupportedAppUsage public String getStringForUser(ContentResolver cr, String name, final int userHandle) { + // Check if the target settings key is readable. Reject if the caller is not system and + // is trying to access a settings key defined in the Settings.Secure, Settings.System or + // Settings.Global and is not annotated as @Readable. + // Notice that a key string that is not defined in any of the Settings.* classes will + // still be regarded as readable. + // TODO(b/175024829): provide a register method. + if (!Settings.isInSystemServer() && !isSystemOrPrivilegedApp() + && mAllFields.contains(name) && !mReadableFields.contains(name)) { + throw new SecurityException( + "Settings key: <" + name + "> is not readable. From S+, new public " + + "settings keys need to be annotated with @Readable unless they are " + + "annotated with @hide."); + } final boolean isSelf = (userHandle == UserHandle.myUserId()); int currentGeneration = -1; if (isSelf) { @@ -2900,6 +2924,19 @@ public final class Settings { } } + private static boolean isSystemOrPrivilegedApp() { + if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) { + return true; + } + final Application application = ActivityThread.currentApplication(); + if (application == null || application.getApplicationInfo() == null) { + return false; + } + final ApplicationInfo applicationInfo = application.getApplicationInfo(); + return applicationInfo.isSystemApp() || applicationInfo.isPrivilegedApp() + || applicationInfo.isSignedWithPlatformKey(); + } + public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix, List<String> names) { String namespace = prefix.substring(0, prefix.length() - 1); @@ -3067,6 +3104,39 @@ public final class Settings { } /** + * This annotation indicates that the value of a setting is allowed to be read + * with the get* methods. The following settings should be readable: + * 1) all the public settings + * 2) all the hidden settings added before S + */ + @Target({ ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + private @interface Readable { + } + + private static <T extends NameValueTable> void getPublicSettingsForClass( + Class<T> callerClass, Set<String> allKeys, Set<String> readableKeys) { + final Field[] allFields = callerClass.getDeclaredFields(); + try { + for (int i = 0; i < allFields.length; i++) { + final Field field = allFields[i]; + if (!field.getType().equals(String.class)) { + continue; + } + final Object value = field.get(callerClass); + if (!value.getClass().equals(String.class)) { + continue; + } + allKeys.add((String) value); + if (field.getAnnotation(Readable.class) != null) { + readableKeys.add((String) value); + } + } + } catch (IllegalAccessException ignored) { + } + } + + /** * System settings, containing miscellaneous system preferences. This * table holds simple name/value pairs. There are convenience * functions for accessing individual settings entries. @@ -3093,7 +3163,8 @@ public final class Settings { CONTENT_URI, CALL_METHOD_GET_SYSTEM, CALL_METHOD_PUT_SYSTEM, - sProviderHolder); + sProviderHolder, + System.class); @UnsupportedAppUsage private static final HashSet<String> MOVED_TO_SECURE; @@ -3147,8 +3218,11 @@ public final class Settings { MOVED_TO_SECURE_THEN_GLOBAL.add(Global.BLUETOOTH_ON); MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DATA_ROAMING); MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DEVICE_PROVISIONED); - MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED); MOVED_TO_SECURE_THEN_GLOBAL.add(Global.HTTP_PROXY); + MOVED_TO_SECURE_THEN_GLOBAL.add(Global.NETWORK_PREFERENCE); + MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED); + MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS); + MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MAX_DHCP_RETRY_COUNT); // these are moving directly from system to global MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_ON); @@ -3186,6 +3260,12 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL); MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_CONTENT_URL); MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_METADATA_URL); + MOVED_TO_GLOBAL.add(Settings.Global.RADIO_NFC); + MOVED_TO_GLOBAL.add(Settings.Global.RADIO_CELL); + MOVED_TO_GLOBAL.add(Settings.Global.RADIO_WIFI); + MOVED_TO_GLOBAL.add(Settings.Global.RADIO_BLUETOOTH); + MOVED_TO_GLOBAL.add(Settings.Global.RADIO_WIMAX); + MOVED_TO_GLOBAL.add(Settings.Global.SHOW_PROCESSES); } /** @hide */ @@ -3210,6 +3290,11 @@ public final class Settings { sNameValueCache.clearGenerationTrackerForTest(); } + /** @hide */ + public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) { + getPublicSettingsForClass(System.class, allKeys, readableKeys); + } + /** * Look up a name in the database. * @param resolver to access the database with @@ -3234,6 +3319,7 @@ public final class Settings { + " to android.provider.Settings.Global, returning read-only value."); return Global.getStringForUser(resolver, name, userHandle); } + return sNameValueCache.getStringForUser(resolver, name, userHandle); } @@ -3711,6 +3797,7 @@ public final class Settings { * 3 - The end button goes to the home screen. If the user is already on the * home screen, it puts the device to sleep. */ + @Readable public static final String END_BUTTON_BEHAVIOR = "end_button_behavior"; /** @@ -3735,6 +3822,7 @@ public final class Settings { * Is advanced settings mode turned on. 0 == no, 1 == yes * @hide */ + @Readable public static final String ADVANCED_SETTINGS = "advanced_settings"; /** @@ -3835,6 +3923,7 @@ public final class Settings { * @deprecated Use {@link WifiManager} instead */ @Deprecated + @Readable public static final String WIFI_USE_STATIC_IP = "wifi_use_static_ip"; /** @@ -3845,6 +3934,7 @@ public final class Settings { * @deprecated Use {@link WifiManager} instead */ @Deprecated + @Readable public static final String WIFI_STATIC_IP = "wifi_static_ip"; /** @@ -3855,6 +3945,7 @@ public final class Settings { * @deprecated Use {@link WifiManager} instead */ @Deprecated + @Readable public static final String WIFI_STATIC_GATEWAY = "wifi_static_gateway"; /** @@ -3865,6 +3956,7 @@ public final class Settings { * @deprecated Use {@link WifiManager} instead */ @Deprecated + @Readable public static final String WIFI_STATIC_NETMASK = "wifi_static_netmask"; /** @@ -3875,6 +3967,7 @@ public final class Settings { * @deprecated Use {@link WifiManager} instead */ @Deprecated + @Readable public static final String WIFI_STATIC_DNS1 = "wifi_static_dns1"; /** @@ -3885,6 +3978,7 @@ public final class Settings { * @deprecated Use {@link WifiManager} instead */ @Deprecated + @Readable public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2"; /** @@ -3895,6 +3989,7 @@ public final class Settings { * 1 -- connectable but not discoverable * 0 -- neither connectable nor discoverable */ + @Readable public static final String BLUETOOTH_DISCOVERABILITY = "bluetooth_discoverability"; @@ -3903,6 +3998,7 @@ public final class Settings { * Bluetooth becomes discoverable for a certain number of seconds, * after which is becomes simply connectable. The value is in seconds. */ + @Readable public static final String BLUETOOTH_DISCOVERABILITY_TIMEOUT = "bluetooth_discoverability_timeout"; @@ -3936,11 +4032,13 @@ public final class Settings { * @deprecated Use {@link android.app.AlarmManager#getNextAlarmClock()}. */ @Deprecated + @Readable public static final String NEXT_ALARM_FORMATTED = "next_alarm_formatted"; /** * Scaling factor for fonts, float. */ + @Readable public static final String FONT_SCALE = "font_scale"; /** @@ -3952,6 +4050,7 @@ public final class Settings { * instead. * @hide */ + @Readable public static final String SYSTEM_LOCALES = "system_locales"; @@ -3977,12 +4076,14 @@ public final class Settings { * @deprecated This setting is no longer used. */ @Deprecated + @Readable public static final String DIM_SCREEN = "dim_screen"; /** * The display color mode. * @hide */ + @Readable public static final String DISPLAY_COLOR_MODE = "display_color_mode"; /** @@ -3990,6 +4091,7 @@ public final class Settings { * unset or a match is not made, only the standard color modes will be restored. * @hide */ + @Readable public static final String DISPLAY_COLOR_MODE_VENDOR_HINT = "display_color_mode_vendor_hint"; @@ -3999,6 +4101,7 @@ public final class Settings { * If this isn't set, 0 will be used. * @hide */ + @Readable public static final String MIN_REFRESH_RATE = "min_refresh_rate"; /** @@ -4007,6 +4110,7 @@ public final class Settings { * If this isn't set, the system falls back to a device specific default. * @hide */ + @Readable public static final String PEAK_REFRESH_RATE = "peak_refresh_rate"; /** @@ -4019,23 +4123,27 @@ public final class Settings { * This value is bounded by maximum timeout set by * {@link android.app.admin.DevicePolicyManager#setMaximumTimeToLock(ComponentName, long)}. */ + @Readable public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout"; /** * The screen backlight brightness between 0 and 255. */ + @Readable public static final String SCREEN_BRIGHTNESS = "screen_brightness"; /** * The screen backlight brightness between 0 and 255. * @hide */ + @Readable public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr"; /** * The screen backlight brightness between 0.0f and 1.0f. * @hide */ + @Readable public static final String SCREEN_BRIGHTNESS_FOR_VR_FLOAT = "screen_brightness_for_vr_float"; @@ -4043,11 +4151,13 @@ public final class Settings { * The screen backlight brightness between 0.0f and 1.0f. * @hide */ + @Readable public static final String SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float"; /** * Control whether to enable automatic brightness mode. */ + @Readable public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; /** @@ -4056,6 +4166,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj"; /** @@ -4073,6 +4184,8 @@ public final class Settings { * @deprecated Use {@link android.provider.Settings.Secure#ADAPTIVE_SLEEP} instead. * @hide */ + @Deprecated + @Readable public static final String ADAPTIVE_SLEEP = "adaptive_sleep"; /** @@ -4099,6 +4212,7 @@ public final class Settings { * stream type's bit should be set to 1 if it should be muted when going * into an inaudible ringer mode. */ + @Readable public static final String MODE_RINGER_STREAMS_AFFECTED = "mode_ringer_streams_affected"; /** @@ -4106,12 +4220,14 @@ public final class Settings { * stream type's bit should be set to 1 if it should be muted when a mute request * is received. */ + @Readable public static final String MUTE_STREAMS_AFFECTED = "mute_streams_affected"; /** * Whether vibrate is on for different events. This is used internally, * changing this value will not change the vibrate. See AudioManager. */ + @Readable public static final String VIBRATE_ON = "vibrate_on"; /** @@ -4126,6 +4242,7 @@ public final class Settings { * * @hide */ + @Readable public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices"; /** @@ -4142,6 +4259,7 @@ public final class Settings { * 3 - Strong vibrations * @hide */ + @Readable public static final String NOTIFICATION_VIBRATION_INTENSITY = "notification_vibration_intensity"; /** @@ -4158,6 +4276,7 @@ public final class Settings { * 3 - Strong vibrations * @hide */ + @Readable public static final String RING_VIBRATION_INTENSITY = "ring_vibration_intensity"; @@ -4175,6 +4294,7 @@ public final class Settings { * 3 - Strong vibrations * @hide */ + @Readable public static final String HAPTIC_FEEDBACK_INTENSITY = "haptic_feedback_intensity"; @@ -4184,6 +4304,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String VOLUME_RING = "volume_ring"; /** @@ -4192,6 +4313,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String VOLUME_SYSTEM = "volume_system"; /** @@ -4200,6 +4322,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String VOLUME_VOICE = "volume_voice"; /** @@ -4208,6 +4331,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String VOLUME_MUSIC = "volume_music"; /** @@ -4216,6 +4340,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String VOLUME_ALARM = "volume_alarm"; /** @@ -4224,6 +4349,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String VOLUME_NOTIFICATION = "volume_notification"; /** @@ -4232,6 +4358,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco"; /** @@ -4239,12 +4366,14 @@ public final class Settings { * Acessibility volume. This is used internally, changing this * value will not change the volume. */ + @Readable public static final String VOLUME_ACCESSIBILITY = "volume_a11y"; /** * @hide * Volume index for virtual assistant. */ + @Readable public static final String VOLUME_ASSISTANT = "volume_assistant"; /** @@ -4252,6 +4381,7 @@ public final class Settings { * * @hide */ + @Readable public static final String VOLUME_MASTER = "volume_master"; /** @@ -4260,6 +4390,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String MASTER_MONO = "master_mono"; /** @@ -4267,6 +4398,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MASTER_BALANCE = "master_balance"; /** @@ -4284,6 +4416,7 @@ public final class Settings { * @deprecated */ @Deprecated + @Readable public static final String NOTIFICATIONS_USE_RING_VOLUME = "notifications_use_ring_volume"; @@ -4300,6 +4433,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String VIBRATE_IN_SILENT = "vibrate_in_silent"; /** @@ -4335,6 +4469,7 @@ public final class Settings { * * @removed Not used by anything since API 2. */ + @Readable public static final String APPEND_FOR_LAST_AUDIBLE = "_last_audible"; /** @@ -4346,6 +4481,7 @@ public final class Settings { * * @see #DEFAULT_RINGTONE_URI */ + @Readable public static final String RINGTONE = "ringtone"; /** @@ -4359,6 +4495,7 @@ public final class Settings { public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE); /** {@hide} */ + @Readable public static final String RINGTONE_CACHE = "ringtone_cache"; /** {@hide} */ public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE); @@ -4369,6 +4506,7 @@ public final class Settings { * @see #RINGTONE * @see #DEFAULT_NOTIFICATION_URI */ + @Readable public static final String NOTIFICATION_SOUND = "notification_sound"; /** @@ -4380,6 +4518,7 @@ public final class Settings { public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor(NOTIFICATION_SOUND); /** {@hide} */ + @Readable public static final String NOTIFICATION_SOUND_CACHE = "notification_sound_cache"; /** {@hide} */ public static final Uri NOTIFICATION_SOUND_CACHE_URI = getUriFor(NOTIFICATION_SOUND_CACHE); @@ -4390,6 +4529,7 @@ public final class Settings { * @see #RINGTONE * @see #DEFAULT_ALARM_ALERT_URI */ + @Readable public static final String ALARM_ALERT = "alarm_alert"; /** @@ -4401,6 +4541,7 @@ public final class Settings { public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT); /** {@hide} */ + @Readable public static final String ALARM_ALERT_CACHE = "alarm_alert_cache"; /** {@hide} */ public static final Uri ALARM_ALERT_CACHE_URI = getUriFor(ALARM_ALERT_CACHE); @@ -4410,29 +4551,35 @@ public final class Settings { * * @hide */ + @Readable public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver"; /** * Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off */ + @Readable public static final String TEXT_AUTO_REPLACE = "auto_replace"; /** * Setting to enable Auto Caps in text editors. 1 = On, 0 = Off */ + @Readable public static final String TEXT_AUTO_CAPS = "auto_caps"; /** * Setting to enable Auto Punctuate in text editors. 1 = On, 0 = Off. This * feature converts two spaces to a "." and space. */ + @Readable public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate"; /** * Setting to showing password characters in text editors. 1 = On, 0 = Off */ + @Readable public static final String TEXT_SHOW_PASSWORD = "show_password"; + @Readable public static final String SHOW_GTALK_SERVICE_STATUS = "SHOW_GTALK_SERVICE_STATUS"; @@ -4442,6 +4589,7 @@ public final class Settings { * @deprecated Use {@link WallpaperManager} instead. */ @Deprecated + @Readable public static final String WALLPAPER_ACTIVITY = "wallpaper_activity"; /** @@ -4463,6 +4611,7 @@ public final class Settings { * 12 * 24 */ + @Readable public static final String TIME_12_24 = "time_12_24"; /** @@ -4471,6 +4620,7 @@ public final class Settings { * dd/mm/yyyy * yyyy/mm/dd */ + @Readable public static final String DATE_FORMAT = "date_format"; /** @@ -4480,6 +4630,7 @@ public final class Settings { * nonzero = it has been run in the past * 0 = it has not been run in the past */ + @Readable public static final String SETUP_WIZARD_HAS_RUN = "setup_wizard_has_run"; /** @@ -4516,6 +4667,7 @@ public final class Settings { * by the application; if 1, it will be used by default unless explicitly * disabled by the application. */ + @Readable public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation"; /** @@ -4526,6 +4678,7 @@ public final class Settings { * * @see Display#getRotation */ + @Readable public static final String USER_ROTATION = "user_rotation"; /** @@ -4540,6 +4693,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY = "hide_rotation_lock_toggle_for_accessibility"; @@ -4553,6 +4707,7 @@ public final class Settings { * relied on the setting, while this is purely about the vibration setting for incoming * calls. */ + @Readable public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing"; /** @@ -4560,6 +4715,7 @@ public final class Settings { * {@code 0}, enhanced call blocking functionality is disabled. * @hide */ + @Readable public static final String DEBUG_ENABLE_ENHANCED_CALL_BLOCKING = "debug.enable_enhanced_calling"; @@ -4567,6 +4723,7 @@ public final class Settings { * Whether the audible DTMF tones are played by the dialer when dialing. The value is * boolean (1 or 0). */ + @Readable public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone"; /** @@ -4575,6 +4732,7 @@ public final class Settings { * 0 = Normal * 1 = Long */ + @Readable public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type"; /** @@ -4583,6 +4741,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String HEARING_AID = "hearing_aid"; /** @@ -4595,18 +4754,21 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String TTY_MODE = "tty_mode"; /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is * boolean (1 or 0). */ + @Readable public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled"; /** * Whether haptic feedback (Vibrate on tap) is enabled. The value is * boolean (1 or 0). */ + @Readable public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled"; /** @@ -4614,6 +4776,7 @@ public final class Settings { * setting for this. */ @Deprecated + @Readable public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions"; /** @@ -4622,6 +4785,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; /** @@ -4631,6 +4795,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String POINTER_LOCATION = "pointer_location"; /** @@ -4640,6 +4805,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String SHOW_TOUCHES = "show_touches"; /** @@ -4650,6 +4816,7 @@ public final class Settings { * 1 = yes * @hide */ + @Readable public static final String WINDOW_ORIENTATION_LISTENER_LOG = "window_orientation_listener_log"; @@ -4675,12 +4842,14 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled"; /** * Whether the lockscreen should be completely disabled. * @hide */ + @Readable public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled"; /** @@ -4751,6 +4920,7 @@ public final class Settings { * 1 = yes * @hide */ + @Readable public static final String SIP_RECEIVE_CALLS = "sip_receive_calls"; /** @@ -4759,18 +4929,21 @@ public final class Settings { * "SIP_ADDRESS_ONLY" : Only if destination is a SIP address * @hide */ + @Readable public static final String SIP_CALL_OPTIONS = "sip_call_options"; /** * One of the sip call options: Always use SIP with network access. * @hide */ + @Readable public static final String SIP_ALWAYS = "SIP_ALWAYS"; /** * One of the sip call options: Only if destination is a SIP address. * @hide */ + @Readable public static final String SIP_ADDRESS_ONLY = "SIP_ADDRESS_ONLY"; /** @@ -4781,6 +4954,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME"; /** @@ -4792,12 +4966,14 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String POINTER_SPEED = "pointer_speed"; /** * Whether lock-to-app will be triggered by long-press on recents. * @hide */ + @Readable public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled"; /** @@ -4807,6 +4983,7 @@ public final class Settings { * Backward-compatible with <code>PrefGetPreference(prefAllowEasterEggs)</code>. * @hide */ + @Readable public static final String EGG_MODE = "egg_mode"; /** @@ -4815,6 +4992,7 @@ public final class Settings { * 1 - Show percentage * @hide */ + @Readable public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent"; /** @@ -4823,6 +5001,7 @@ public final class Settings { * for instance pausing media apps when another starts. * @hide */ + @Readable public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled"; /** @@ -5017,6 +5196,7 @@ public final class Settings { * @see android.telephony.TelephonyManager.WifiCallingChoices * @hide */ + @Readable public static final String WHEN_TO_MAKE_WIFI_CALLS = "when_to_make_wifi_calls"; // Settings moved to Settings.Secure @@ -5173,6 +5353,7 @@ public final class Settings { * instead */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE = Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE; @@ -5187,6 +5368,7 @@ public final class Settings { * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS} instead */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS; @@ -5195,6 +5377,7 @@ public final class Settings { * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED = Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED; @@ -5204,6 +5387,7 @@ public final class Settings { * instead */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS = Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS; @@ -5212,6 +5396,7 @@ public final class Settings { * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} instead */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT = Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT; @@ -5246,6 +5431,7 @@ public final class Settings { * instead */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS; @@ -5295,7 +5481,8 @@ public final class Settings { CONTENT_URI, CALL_METHOD_GET_SECURE, CALL_METHOD_PUT_SECURE, - sProviderHolder); + sProviderHolder, + Secure.class); private static ILockSettings sLockSettings = null; @@ -5433,6 +5620,11 @@ public final class Settings { sNameValueCache.clearGenerationTrackerForTest(); } + /** @hide */ + public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) { + getPublicSettingsForClass(Secure.class, allKeys, readableKeys); + } + /** * Look up a name in the database. * @param resolver to access the database with @@ -5928,6 +6120,7 @@ public final class Settings { * Control whether to enable adaptive sleep mode. * @hide */ + @Readable public static final String ADAPTIVE_SLEEP = "adaptive_sleep"; /** @@ -5935,6 +6128,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAMERA_AUTOROTATE = "camera_autorotate"; /** @@ -5952,6 +6146,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; /** @@ -5969,6 +6164,7 @@ public final class Settings { * @deprecated This settings is not used anymore. */ @Deprecated + @Readable public static final String ALLOW_MOCK_LOCATION = "mock_location"; /** @@ -5977,6 +6173,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled"; /** @@ -6016,6 +6213,7 @@ public final class Settings { * to the Instant App, it is generated when the Instant App is first installed and reset if * the user clears the Instant App. */ + @Readable public static final String ANDROID_ID = "android_id"; /** @@ -6034,12 +6232,14 @@ public final class Settings { * Setting to record the input method used by default, holding the ID * of the desired method. */ + @Readable public static final String DEFAULT_INPUT_METHOD = "default_input_method"; /** * Setting to record the input method subtype used by default, holding the ID * of the desired method. */ + @Readable public static final String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype"; @@ -6048,12 +6248,14 @@ public final class Settings { * and its last used subtype. * @hide */ + @Readable public static final String INPUT_METHODS_SUBTYPE_HISTORY = "input_methods_subtype_history"; /** * Setting to record the visibility of input method selector */ + @Readable public static final String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility"; @@ -6062,6 +6264,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; /** @@ -6069,6 +6272,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String AUTOFILL_SERVICE = "autofill_service"; /** @@ -6079,6 +6283,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; @@ -6087,6 +6292,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DARK_MODE_DIALOG_SEEN = "dark_mode_dialog_seen"; @@ -6095,6 +6301,7 @@ public final class Settings { * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). * @hide */ + @Readable public static final String DARK_THEME_CUSTOM_START_TIME = "dark_theme_custom_start_time"; @@ -6103,6 +6310,7 @@ public final class Settings { * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). * @hide */ + @Readable public static final String DARK_THEME_CUSTOM_END_TIME = "dark_theme_custom_end_time"; @@ -6112,6 +6320,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size"; @@ -6122,6 +6331,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size"; @@ -6132,6 +6342,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count"; @@ -6141,6 +6352,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length"; @@ -6150,6 +6362,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length"; @@ -6162,6 +6375,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled"; /** @@ -6179,6 +6393,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MANAGED_PROVISIONING_DPC_DOWNLOADED = "managed_provisioning_dpc_downloaded"; @@ -6189,6 +6404,7 @@ public final class Settings { * <p> * Type: int (0 for false, 1 for true) */ + @Readable public static final String SECURE_FRP_MODE = "secure_frp_mode"; /** @@ -6199,6 +6415,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String USER_SETUP_COMPLETE = "user_setup_complete"; /** @@ -6254,6 +6471,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String USER_SETUP_PERSONALIZATION_STATE = "user_setup_personalization_state"; @@ -6264,6 +6482,7 @@ public final class Settings { * * @hide */ + @Readable public static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete"; /** @@ -6275,6 +6494,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category."; /** @@ -6285,6 +6505,7 @@ public final class Settings { * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0" * where imeId is ComponentName and subtype is int32. */ + @Readable public static final String ENABLED_INPUT_METHODS = "enabled_input_methods"; /** @@ -6293,6 +6514,7 @@ public final class Settings { * by ':'. * @hide */ + @Readable public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods"; /** @@ -6301,6 +6523,7 @@ public final class Settings { * @hide */ @TestApi + @Readable @SuppressLint("NoSettingsProvider") public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; @@ -6318,6 +6541,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ALWAYS_ON_VPN_APP = "always_on_vpn_app"; /** @@ -6326,6 +6550,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown"; /** @@ -6335,6 +6560,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ALWAYS_ON_VPN_LOCKDOWN_WHITELIST = "always_on_vpn_lockdown_whitelist"; @@ -6348,6 +6574,8 @@ public final class Settings { * {@link PackageManager#canRequestPackageInstalls()} * @see PackageManager#canRequestPackageInstalls() */ + @Deprecated + @Readable public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; /** @@ -6359,6 +6587,7 @@ public final class Settings { * * @hide */ + @Readable public static final String UNKNOWN_SOURCES_DEFAULT_REVERSED = "unknown_sources_default_reversed"; @@ -6372,6 +6601,7 @@ public final class Settings { * instead. */ @Deprecated + @Readable public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed"; /** @@ -6383,12 +6613,14 @@ public final class Settings { * {@link LocationManager#MODE_CHANGED_ACTION}. */ @Deprecated + @Readable public static final String LOCATION_MODE = "location_mode"; /** * The App or module that changes the location mode. * @hide */ + @Readable public static final String LOCATION_CHANGER = "location_changer"; /** @@ -6457,6 +6689,7 @@ public final class Settings { * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update. * @hide */ + @Readable public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED = "location_time_zone_detection_enabled"; @@ -6466,6 +6699,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy"; /** @@ -6473,6 +6707,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String LOCK_BIOMETRIC_WEAK_FLAGS = "lock_biometric_weak_flags"; @@ -6480,6 +6715,7 @@ public final class Settings { * Whether lock-to-app will lock the keyguard when exiting. * @hide */ + @Readable public static final String LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked"; /** @@ -6490,6 +6726,7 @@ public final class Settings { * {@link VERSION_CODES#M} or later throws a {@code SecurityException}. */ @Deprecated + @Readable public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock"; /** @@ -6499,6 +6736,7 @@ public final class Settings { * {@link VERSION_CODES#M} or later throws a {@code SecurityException}. */ @Deprecated + @Readable public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern"; /** @@ -6512,6 +6750,7 @@ public final class Settings { * {@link VERSION_CODES#M} or later throws a {@code SecurityException}. */ @Deprecated + @Readable public static final String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled"; @@ -6521,6 +6760,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String LOCK_SCREEN_LOCK_AFTER_TIMEOUT = "lock_screen_lock_after_timeout"; @@ -6530,6 +6770,7 @@ public final class Settings { * @deprecated */ @Deprecated + @Readable public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info"; /** @@ -6537,6 +6778,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String LOCK_SCREEN_APPWIDGET_IDS = "lock_screen_appwidget_ids"; @@ -6545,6 +6787,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String LOCK_SCREEN_FALLBACK_APPWIDGET_ID = "lock_screen_fallback_appwidget_id"; @@ -6553,6 +6796,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String LOCK_SCREEN_STICKY_APPWIDGET = "lock_screen_sticky_appwidget"; @@ -6563,6 +6807,7 @@ public final class Settings { */ @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String LOCK_SCREEN_OWNER_INFO_ENABLED = "lock_screen_owner_info_enabled"; @@ -6575,6 +6820,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications"; @@ -6583,6 +6829,7 @@ public final class Settings { * without having to unlock * @hide */ + @Readable public static final String LOCK_SCREEN_ALLOW_REMOTE_INPUT = "lock_screen_allow_remote_input"; @@ -6592,12 +6839,14 @@ public final class Settings { * {"clock": id, "_applied_timestamp": timestamp} * @hide */ + @Readable public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face"; /** * Indicates which clock face to show on lock screen and AOD while docked. * @hide */ + @Readable public static final String DOCKED_CLOCK_FACE = "docked_clock_face"; /** @@ -6605,6 +6854,7 @@ public final class Settings { * the lockscreen notification policy. * @hide */ + @Readable public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING = "show_note_about_notification_hiding"; @@ -6612,6 +6862,7 @@ public final class Settings { * Set to 1 by the system after trust agents have been initialized. * @hide */ + @Readable public static final String TRUST_AGENTS_INITIALIZED = "trust_agents_initialized"; @@ -6622,6 +6873,7 @@ public final class Settings { * many collisions. It should not be used. */ @Deprecated + @Readable public static final String LOGGING_ID = "logging_id"; /** @@ -6633,16 +6885,19 @@ public final class Settings { /** * No longer supported. */ + @Readable public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled"; /** * No longer supported. */ + @Readable public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update"; /** * No longer supported. */ + @Readable public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url"; /** @@ -6651,6 +6906,7 @@ public final class Settings { * and new Settings apps. */ // TODO: 881807 + @Readable public static final String SETTINGS_CLASSNAME = "settings_classname"; /** @@ -6668,12 +6924,14 @@ public final class Settings { /** * If accessibility is enabled. */ + @Readable public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled"; /** * Setting specifying if the accessibility shortcut is enabled. * @hide */ + @Readable public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN = "accessibility_shortcut_on_lock_screen"; @@ -6681,6 +6939,7 @@ public final class Settings { * Setting specifying if the accessibility shortcut dialog has been shown to this user. * @hide */ + @Readable public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN = "accessibility_shortcut_dialog_shown"; @@ -6695,6 +6954,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service"; @@ -6705,6 +6965,7 @@ public final class Settings { * accessibility feature. * @hide */ + @Readable public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT = "accessibility_button_target_component"; @@ -6717,6 +6978,7 @@ public final class Settings { * accessibility feature. * @hide */ + @Readable public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets"; /** @@ -6725,17 +6987,20 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER = "com.android.server.accessibility.MagnificationController"; /** * If touch exploration is enabled. */ + @Readable public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled"; /** * List of the enabled accessibility providers. */ + @Readable public static final String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services"; @@ -6745,6 +7010,7 @@ public final class Settings { * * @hide */ + @Readable public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES = "touch_exploration_granted_accessibility_services"; @@ -6752,12 +7018,14 @@ public final class Settings { * Whether the Global Actions Panel is enabled. * @hide */ + @Readable public static final String GLOBAL_ACTIONS_PANEL_ENABLED = "global_actions_panel_enabled"; /** * Whether the Global Actions Panel can be toggled on or off in Settings. * @hide */ + @Readable public static final String GLOBAL_ACTIONS_PANEL_AVAILABLE = "global_actions_panel_available"; @@ -6765,6 +7033,7 @@ public final class Settings { * Enables debug mode for the Global Actions Panel. * @hide */ + @Readable public static final String GLOBAL_ACTIONS_PANEL_DEBUG_ENABLED = "global_actions_panel_debug_enabled"; @@ -6773,24 +7042,28 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String HUSH_GESTURE_USED = "hush_gesture_used"; /** * Number of times the user has manually clicked the ringer toggle * @hide */ + @Readable public static final String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count"; /** * Whether to play a sound for charging events. * @hide */ + @Readable public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled"; /** * Whether to vibrate for charging events. * @hide */ + @Readable public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled"; /** @@ -6800,6 +7073,7 @@ public final class Settings { * user to specify a duration. * @hide */ + @Readable public static final String ZEN_DURATION = "zen_duration"; /** @hide */ public static final int ZEN_DURATION_PROMPT = -1; @@ -6809,24 +7083,28 @@ public final class Settings { * If nonzero, will show the zen upgrade notification when the user toggles DND on/off. * @hide */ + @Readable public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification"; /** * If nonzero, will show the zen update settings suggestion. * @hide */ + @Readable public static final String SHOW_ZEN_SETTINGS_SUGGESTION = "show_zen_settings_suggestion"; /** * If nonzero, zen has not been updated to reflect new changes. * @hide */ + @Readable public static final String ZEN_SETTINGS_UPDATED = "zen_settings_updated"; /** * If nonzero, zen setting suggestion has been viewed by user * @hide */ + @Readable public static final String ZEN_SETTINGS_SUGGESTION_VIEWED = "zen_settings_suggestion_viewed"; @@ -6835,6 +7113,7 @@ public final class Settings { * boolean (1 or 0). * @hide */ + @Readable public static final String IN_CALL_NOTIFICATION_ENABLED = "in_call_notification_enabled"; /** @@ -6843,6 +7122,7 @@ public final class Settings { * * @hide */ + @Readable public static final String KEYGUARD_SLICE_URI = "keyguard_slice_uri"; /** @@ -6855,6 +7135,7 @@ public final class Settings { * * @hide */ + @Readable public static final String FONT_WEIGHT_ADJUSTMENT = "font_weight_adjustment"; /** @@ -6865,6 +7146,7 @@ public final class Settings { * at all times, which was the behavior when this value was {@code true}. */ @Deprecated + @Readable public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; /** @@ -6872,6 +7154,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED = "high_text_contrast_enabled"; @@ -6885,6 +7168,7 @@ public final class Settings { */ @UnsupportedAppUsage @TestApi + @Readable public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; @@ -6900,6 +7184,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled"; @@ -6913,6 +7198,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE = "accessibility_display_magnification_scale"; @@ -6923,6 +7209,7 @@ public final class Settings { * @deprecated */ @Deprecated + @Readable public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE = "accessibility_display_magnification_auto_update"; @@ -6932,6 +7219,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_SOFT_KEYBOARD_MODE = "accessibility_soft_keyboard_mode"; @@ -6965,6 +7253,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_ENABLED = "accessibility_captioning_enabled"; @@ -6975,6 +7264,7 @@ public final class Settings { * @see java.util.Locale#toString * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_LOCALE = "accessibility_captioning_locale"; @@ -6989,6 +7279,7 @@ public final class Settings { * @see java.util.Locale#toString * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_PRESET = "accessibility_captioning_preset"; @@ -6999,6 +7290,7 @@ public final class Settings { * @see android.graphics.Color#argb * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR = "accessibility_captioning_background_color"; @@ -7009,6 +7301,7 @@ public final class Settings { * @see android.graphics.Color#argb * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR = "accessibility_captioning_foreground_color"; @@ -7023,6 +7316,7 @@ public final class Settings { * @see #ACCESSIBILITY_CAPTIONING_EDGE_COLOR * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE = "accessibility_captioning_edge_type"; @@ -7034,6 +7328,7 @@ public final class Settings { * @see android.graphics.Color#argb * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR = "accessibility_captioning_edge_color"; @@ -7044,6 +7339,7 @@ public final class Settings { * @see android.graphics.Color#argb * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR = "accessibility_captioning_window_color"; @@ -7060,6 +7356,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE = "accessibility_captioning_typeface"; @@ -7068,12 +7365,14 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE = "accessibility_captioning_font_scale"; /** * Setting that specifies whether display color inversion is enabled. */ + @Readable public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; @@ -7084,6 +7383,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED = "accessibility_display_daltonizer_enabled"; @@ -7100,6 +7400,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String ACCESSIBILITY_DISPLAY_DALTONIZER = "accessibility_display_daltonizer"; @@ -7110,6 +7411,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String ACCESSIBILITY_AUTOCLICK_ENABLED = "accessibility_autoclick_enabled"; @@ -7120,6 +7422,7 @@ public final class Settings { * @see #ACCESSIBILITY_AUTOCLICK_ENABLED * @hide */ + @Readable public static final String ACCESSIBILITY_AUTOCLICK_DELAY = "accessibility_autoclick_delay"; @@ -7130,6 +7433,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String ACCESSIBILITY_LARGE_POINTER_ICON = "accessibility_large_pointer_icon"; @@ -7138,6 +7442,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String LONG_PRESS_TIMEOUT = "long_press_timeout"; /** @@ -7145,6 +7450,7 @@ public final class Settings { * down event for an interaction to be considered part of the same multi-press. * @hide */ + @Readable public static final String MULTI_PRESS_TIMEOUT = "multi_press_timeout"; /** @@ -7153,6 +7459,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS = "accessibility_non_interactive_ui_timeout_ms"; @@ -7162,6 +7469,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS = "accessibility_interactive_ui_timeout_ms"; @@ -7172,6 +7480,7 @@ public final class Settings { * * @hide */ + @Readable public static final String REDUCE_BRIGHT_COLORS_ACTIVATED = "reduce_bright_colors_activated"; @@ -7181,6 +7490,7 @@ public final class Settings { * * @hide */ + @Readable public static final String REDUCE_BRIGHT_COLORS_LEVEL = "reduce_bright_colors_level"; @@ -7189,6 +7499,7 @@ public final class Settings { * * @hide */ + @Readable public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS = "reduce_bright_colors_persist_across_reboots"; @@ -7201,6 +7512,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String ENABLED_PRINT_SERVICES = "enabled_print_services"; @@ -7210,6 +7522,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String DISABLED_PRINT_SERVICES = "disabled_print_services"; @@ -7220,6 +7533,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DISPLAY_DENSITY_FORCED = "display_density_forced"; /** @@ -7232,21 +7546,25 @@ public final class Settings { * the framework text to speech APIs as of the Ice Cream Sandwich release. */ @Deprecated + @Readable public static final String TTS_USE_DEFAULTS = "tts_use_defaults"; /** * Default text-to-speech engine speech rate. 100 = 1x */ + @Readable public static final String TTS_DEFAULT_RATE = "tts_default_rate"; /** * Default text-to-speech engine pitch. 100 = 1x */ + @Readable public static final String TTS_DEFAULT_PITCH = "tts_default_pitch"; /** * Default text-to-speech engine. */ + @Readable public static final String TTS_DEFAULT_SYNTH = "tts_default_synth"; /** @@ -7258,6 +7576,7 @@ public final class Settings { * locale. {@link TextToSpeech#getLanguage()}. */ @Deprecated + @Readable public static final String TTS_DEFAULT_LANG = "tts_default_lang"; /** @@ -7269,6 +7588,7 @@ public final class Settings { * locale. {@link TextToSpeech#getLanguage()}. */ @Deprecated + @Readable public static final String TTS_DEFAULT_COUNTRY = "tts_default_country"; /** @@ -7280,6 +7600,7 @@ public final class Settings { * locale that is in use {@link TextToSpeech#getLanguage()}. */ @Deprecated + @Readable public static final String TTS_DEFAULT_VARIANT = "tts_default_variant"; /** @@ -7294,11 +7615,13 @@ public final class Settings { * * @hide */ + @Readable public static final String TTS_DEFAULT_LOCALE = "tts_default_locale"; /** * Space delimited list of plugin packages that are enabled. */ + @Readable public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins"; /** @@ -7338,6 +7661,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE = "wifi_watchdog_acceptable_packet_loss_percentage"; @@ -7347,6 +7671,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count"; /** @@ -7354,6 +7679,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = "wifi_watchdog_background_check_delay_ms"; @@ -7363,6 +7689,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED = "wifi_watchdog_background_check_enabled"; @@ -7371,6 +7698,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS = "wifi_watchdog_background_check_timeout_ms"; @@ -7382,6 +7710,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT = "wifi_watchdog_initial_ignored_ping_count"; @@ -7393,12 +7722,14 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = "wifi_watchdog_max_ap_checks"; /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_WATCHDOG_ON} instead */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on"; /** @@ -7406,6 +7737,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_WATCH_LIST = "wifi_watchdog_watch_list"; /** @@ -7413,6 +7745,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count"; /** @@ -7420,6 +7753,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_PING_DELAY_MS = "wifi_watchdog_ping_delay_ms"; /** @@ -7427,6 +7761,7 @@ public final class Settings { * @deprecated This setting is not used. */ @Deprecated + @Readable public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms"; /** @@ -7451,6 +7786,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS = "connectivity_release_pending_intent_delay_ms"; @@ -7464,12 +7800,14 @@ public final class Settings { * now appear disconnected. */ @Deprecated + @Readable public static final String BACKGROUND_DATA = "background_data"; /** * Origins for which browsers should allow geolocation by default. * The value is a space-separated list of origins. */ + @Readable public static final String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins"; @@ -7480,6 +7818,7 @@ public final class Settings { * 3 = TTY VCO * @hide */ + @Readable public static final String PREFERRED_TTY_MODE = "preferred_tty_mode"; @@ -7489,6 +7828,7 @@ public final class Settings { * 1 = enhanced voice privacy * @hide */ + @Readable public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled"; /** @@ -7497,6 +7837,7 @@ public final class Settings { * 1 = enabled * @hide */ + @Readable public static final String TTY_MODE_ENABLED = "tty_mode_enabled"; /** @@ -7505,6 +7846,7 @@ public final class Settings { * 0 = OFF * 1 = ON */ + @Readable public static final String RTT_CALLING_MODE = "rtt_calling_mode"; /** @@ -7514,6 +7856,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String BACKUP_ENABLED = "backup_enabled"; /** @@ -7523,6 +7866,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String BACKUP_AUTO_RESTORE = "backup_auto_restore"; /** @@ -7531,6 +7875,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String BACKUP_PROVISIONED = "backup_provisioned"; /** @@ -7538,6 +7883,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String BACKUP_TRANSPORT = "backup_transport"; /** @@ -7547,6 +7893,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String LAST_SETUP_SHOWN = "last_setup_shown"; /** @@ -7569,6 +7916,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SEARCH_GLOBAL_SEARCH_ACTIVITY = "search_global_search_activity"; @@ -7576,21 +7924,25 @@ public final class Settings { * The number of promoted sources in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_NUM_PROMOTED_SOURCES = "search_num_promoted_sources"; /** * The maximum number of suggestions returned by GlobalSearch. * @hide */ + @Readable public static final String SEARCH_MAX_RESULTS_TO_DISPLAY = "search_max_results_to_display"; /** * The number of suggestions GlobalSearch will ask each non-web search source for. * @hide */ + @Readable public static final String SEARCH_MAX_RESULTS_PER_SOURCE = "search_max_results_per_source"; /** * The number of suggestions the GlobalSearch will ask the web search source for. * @hide */ + @Readable public static final String SEARCH_WEB_RESULTS_OVERRIDE_LIMIT = "search_web_results_override_limit"; /** @@ -7598,69 +7950,81 @@ public final class Settings { * promoted sources before continuing with all other sources. * @hide */ + @Readable public static final String SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS = "search_promoted_source_deadline_millis"; /** * The number of milliseconds before GlobalSearch aborts search suggesiton queries. * @hide */ + @Readable public static final String SEARCH_SOURCE_TIMEOUT_MILLIS = "search_source_timeout_millis"; /** * The maximum number of milliseconds that GlobalSearch shows the previous results * after receiving a new query. * @hide */ + @Readable public static final String SEARCH_PREFILL_MILLIS = "search_prefill_millis"; /** * The maximum age of log data used for shortcuts in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_MAX_STAT_AGE_MILLIS = "search_max_stat_age_millis"; /** * The maximum age of log data used for source ranking in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS = "search_max_source_event_age_millis"; /** * The minimum number of impressions needed to rank a source in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING = "search_min_impressions_for_source_ranking"; /** * The minimum number of clicks needed to rank a source in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING = "search_min_clicks_for_source_ranking"; /** * The maximum number of shortcuts shown by GlobalSearch. * @hide */ + @Readable public static final String SEARCH_MAX_SHORTCUTS_RETURNED = "search_max_shortcuts_returned"; /** * The size of the core thread pool for suggestion queries in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_QUERY_THREAD_CORE_POOL_SIZE = "search_query_thread_core_pool_size"; /** * The maximum size of the thread pool for suggestion queries in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_QUERY_THREAD_MAX_POOL_SIZE = "search_query_thread_max_pool_size"; /** * The size of the core thread pool for shortcut refreshing in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE = "search_shortcut_refresh_core_pool_size"; /** * The maximum size of the thread pool for shortcut refreshing in GlobalSearch. * @hide */ + @Readable public static final String SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE = "search_shortcut_refresh_max_pool_size"; /** @@ -7668,12 +8032,14 @@ public final class Settings { * wait before terminating. * @hide */ + @Readable public static final String SEARCH_THREAD_KEEPALIVE_SECONDS = "search_thread_keepalive_seconds"; /** * The maximum number of concurrent suggestion queries to each source. * @hide */ + @Readable public static final String SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT = "search_per_source_concurrent_query_limit"; @@ -7682,24 +8048,28 @@ public final class Settings { * (0 = false, 1 = true) * @hide */ + @Readable public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd"; /** * Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true) * @hide */ + @Readable public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart"; /** * Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true) * @hide */ + @Readable public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt"; /** * Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true) * @hide */ + @Readable public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled"; /** @@ -7711,6 +8081,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable @SuppressLint("NoSettingsProvider") public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; @@ -7720,6 +8091,7 @@ public final class Settings { * @hide */ @TestApi + @Readable @SuppressLint("NoSettingsProvider") public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option"; @@ -7731,6 +8103,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service"; /** @@ -7741,6 +8114,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable @SuppressLint("NoSettingsProvider") public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker"; @@ -7753,6 +8127,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable @SuppressLint("NoSettingsProvider") public static final String SELECTED_SPELL_CHECKER_SUBTYPE = "selected_spell_checker_subtype"; @@ -7762,6 +8137,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled"; /** @@ -7774,6 +8150,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior"; /** @@ -7788,6 +8165,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MINIMAL_POST_PROCESSING_ALLOWED = "minimal_post_processing_allowed"; @@ -7832,6 +8210,7 @@ public final class Settings { * @see #MATCH_CONTENT_FRAMERATE_ALWAYS * @hide */ + @Readable public static final String MATCH_CONTENT_FRAME_RATE = "match_content_frame_rate"; @@ -7863,6 +8242,7 @@ public final class Settings { * * @hide */ + @Readable public static final String INCALL_BACK_BUTTON_BEHAVIOR = "incall_back_button_behavior"; /** @@ -7888,6 +8268,7 @@ public final class Settings { * Whether the device should wake when the wake gesture sensor detects motion. * @hide */ + @Readable public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled"; /** @@ -7895,6 +8276,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String DOZE_ENABLED = "doze_enabled"; /** @@ -7905,36 +8287,42 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String DOZE_ALWAYS_ON = "doze_always_on"; /** * Whether the device should pulse on pick up gesture. * @hide */ + @Readable public static final String DOZE_PICK_UP_GESTURE = "doze_pulse_on_pick_up"; /** * Whether the device should pulse on long press gesture. * @hide */ + @Readable public static final String DOZE_PULSE_ON_LONG_PRESS = "doze_pulse_on_long_press"; /** * Whether the device should pulse on double tap gesture. * @hide */ + @Readable public static final String DOZE_DOUBLE_TAP_GESTURE = "doze_pulse_on_double_tap"; /** * Whether the device should respond to the SLPI tap gesture. * @hide */ + @Readable public static final String DOZE_TAP_SCREEN_GESTURE = "doze_tap_gesture"; /** * Gesture that wakes up the display, showing some version of the lock screen. * @hide */ + @Readable public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_screen_gesture"; /** @@ -7942,84 +8330,98 @@ public final class Settings { * {@link Display.STATE_DOZE}. * @hide */ + @Readable public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture"; /** * Whether the device should suppress the current doze configuration and disable dozing. * @hide */ + @Readable public static final String SUPPRESS_DOZE = "suppress_doze"; /** * Gesture that skips media. * @hide */ + @Readable public static final String SKIP_GESTURE = "skip_gesture"; /** * Count of successful gestures. * @hide */ + @Readable public static final String SKIP_GESTURE_COUNT = "skip_gesture_count"; /** * Count of non-gesture interaction. * @hide */ + @Readable public static final String SKIP_TOUCH_COUNT = "skip_touch_count"; /** * Direction to advance media for skip gesture * @hide */ + @Readable public static final String SKIP_DIRECTION = "skip_gesture_direction"; /** * Gesture that silences sound (alarms, notification, calls). * @hide */ + @Readable public static final String SILENCE_GESTURE = "silence_gesture"; /** * Count of successful silence alarms gestures. * @hide */ + @Readable public static final String SILENCE_ALARMS_GESTURE_COUNT = "silence_alarms_gesture_count"; /** * Count of successful silence timer gestures. * @hide */ + @Readable public static final String SILENCE_TIMER_GESTURE_COUNT = "silence_timer_gesture_count"; /** * Count of successful silence call gestures. * @hide */ + @Readable public static final String SILENCE_CALL_GESTURE_COUNT = "silence_call_gesture_count"; /** * Count of non-gesture interaction. * @hide */ + @Readable public static final String SILENCE_ALARMS_TOUCH_COUNT = "silence_alarms_touch_count"; /** * Count of non-gesture interaction. * @hide */ + @Readable public static final String SILENCE_TIMER_TOUCH_COUNT = "silence_timer_touch_count"; /** * Count of non-gesture interaction. * @hide */ + @Readable public static final String SILENCE_CALL_TOUCH_COUNT = "silence_call_touch_count"; /** * Number of successful "Motion Sense" tap gestures to pause media. * @hide */ + @Readable public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count"; /** @@ -8027,12 +8429,14 @@ public final class Settings { * have been used. * @hide */ + @Readable public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count"; /** * For user preference if swipe bottom to expand notification gesture enabled. * @hide */ + @Readable public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED = "swipe_bottom_to_notification_enabled"; @@ -8040,24 +8444,28 @@ public final class Settings { * For user preference if One-Handed Mode enabled. * @hide */ + @Readable public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled"; /** * For user preference if One-Handed Mode timeout. * @hide */ + @Readable public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout"; /** * For user taps app to exit One-Handed Mode. * @hide */ + @Readable public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit"; /** * Internal use, one handed mode tutorial showed times. * @hide */ + @Readable public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT = "one_handed_tutorial_show_count"; @@ -8067,6 +8475,7 @@ public final class Settings { * UiModeManager. * @hide */ + @Readable public static final String UI_NIGHT_MODE = "ui_night_mode"; /** @@ -8075,12 +8484,14 @@ public final class Settings { * UiModeManager. * @hide */ + @Readable public static final String UI_NIGHT_MODE_OVERRIDE_ON = "ui_night_mode_override_on"; /** * The last computed night mode bool the last time the phone was on * @hide */ + @Readable public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed"; /** @@ -8089,12 +8500,14 @@ public final class Settings { * UiModeManager. * @hide */ + @Readable public static final String UI_NIGHT_MODE_OVERRIDE_OFF = "ui_night_mode_override_off"; /** * Whether screensavers are enabled. * @hide */ + @Readable public static final String SCREENSAVER_ENABLED = "screensaver_enabled"; /** @@ -8104,6 +8517,7 @@ public final class Settings { * battery, or upon dock insertion (if SCREENSAVER_ACTIVATE_ON_DOCK is set to 1). * @hide */ + @Readable public static final String SCREENSAVER_COMPONENTS = "screensaver_components"; /** @@ -8111,6 +8525,7 @@ public final class Settings { * when the device is inserted into a (desk) dock. * @hide */ + @Readable public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock"; /** @@ -8118,12 +8533,14 @@ public final class Settings { * when the screen times out when not on battery. * @hide */ + @Readable public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep"; /** * If screensavers are enabled, the default screensaver component. * @hide */ + @Readable public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component"; /** @@ -8132,12 +8549,14 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; /** * Whether NFC payment is handled by the foreground application or a default. * @hide */ + @Readable public static final String NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground"; /** @@ -8145,6 +8564,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String SMS_DEFAULT_APPLICATION = "sms_default_application"; /** @@ -8152,6 +8572,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application"; /** @@ -8159,6 +8580,7 @@ public final class Settings { * application * @hide */ + @Readable public static final String CALL_SCREENING_DEFAULT_COMPONENT = "call_screening_default_component"; @@ -8169,6 +8591,7 @@ public final class Settings { * * @hide */ + @Readable public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application"; /** @@ -8177,6 +8600,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled"; /** @@ -8185,6 +8609,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled"; /** @@ -8196,6 +8621,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled"; /** @@ -8207,7 +8633,7 @@ public final class Settings { * * @hide */ - + @Readable public static final String SHOW_ROTATION_SUGGESTIONS = "show_rotation_suggestions"; /** @@ -8234,6 +8660,7 @@ public final class Settings { * introduced to rotation suggestions. * @hide */ + @Readable public static final String NUM_ROTATION_SUGGESTIONS_ACCEPTED = "num_rotation_suggestions_accepted"; @@ -8246,6 +8673,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String ENABLED_NOTIFICATION_ASSISTANT = "enabled_notification_assistant"; @@ -8259,6 +8687,7 @@ public final class Settings { */ @Deprecated @UnsupportedAppUsage + @Readable public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; /** @@ -8270,6 +8699,7 @@ public final class Settings { */ @Deprecated @TestApi + @Readable public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; @@ -8283,6 +8713,7 @@ public final class Settings { * @hide */ @TestApi + @Readable @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; @@ -8291,6 +8722,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations"; /** @@ -8298,6 +8730,7 @@ public final class Settings { * * @hide */ + @Readable public static final String PRINT_SERVICE_SEARCH_URI = "print_service_search_uri"; /** @@ -8305,6 +8738,7 @@ public final class Settings { * * @hide */ + @Readable public static final String PAYMENT_SERVICE_SEARCH_URI = "payment_service_search_uri"; /** @@ -8312,6 +8746,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOFILL_SERVICE_SEARCH_URI = "autofill_service_search_uri"; /** @@ -8320,6 +8755,7 @@ public final class Settings { * <p> * Type : int (0 to show hints, 1 to skip showing hints) */ + @Readable public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints"; /** @@ -8327,6 +8763,7 @@ public final class Settings { * * @hide */ + @Readable public static final String UNSAFE_VOLUME_MUSIC_ACTIVE_MS = "unsafe_volume_music_active_ms"; /** @@ -8337,6 +8774,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications"; @@ -8347,6 +8785,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS = "lock_screen_show_silent_notifications"; @@ -8357,6 +8796,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze"; /** @@ -8365,6 +8805,7 @@ public final class Settings { * {@link android.net.Uri#encode(String)} and separated by ':'. * @hide */ + @Readable public static final String TV_INPUT_HIDDEN_INPUTS = "tv_input_hidden_inputs"; /** @@ -8373,6 +8814,7 @@ public final class Settings { * and separated by ','. Each pair is separated by ':'. * @hide */ + @Readable public static final String TV_INPUT_CUSTOM_LABELS = "tv_input_custom_labels"; /** @@ -8390,6 +8832,7 @@ public final class Settings { * * @hide */ + @Readable public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs"; /** @@ -8399,6 +8842,7 @@ public final class Settings { * * @hide */ + @Readable public static final String USB_AUDIO_AUTOMATIC_ROUTING_DISABLED = "usb_audio_automatic_routing_disabled"; @@ -8414,6 +8858,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SLEEP_TIMEOUT = "sleep_timeout"; /** @@ -8428,12 +8873,14 @@ public final class Settings { * * @hide */ + @Readable public static final String ATTENTIVE_TIMEOUT = "attentive_timeout"; /** * Controls whether double tap to wake is enabled. * @hide */ + @Readable public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake"; /** @@ -8447,6 +8894,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String ASSISTANT = "assistant"; /** @@ -8454,6 +8902,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled"; /** @@ -8461,6 +8910,7 @@ public final class Settings { * * @hide */ + @Readable public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled"; /** @@ -8468,6 +8918,7 @@ public final class Settings { * * @hide */ + @Readable public static final String EMERGENCY_GESTURE_SOUND_ENABLED = "emergency_gesture_sound_enabled"; @@ -8477,6 +8928,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED = "camera_double_tap_power_gesture_disabled"; @@ -8486,6 +8938,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED = "camera_double_twist_to_flip_enabled"; @@ -8495,6 +8948,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAMERA_LIFT_TRIGGER_ENABLED = "camera_lift_trigger_enabled"; /** @@ -8510,6 +8964,7 @@ public final class Settings { * * @hide */ + @Readable public static final String FLASHLIGHT_AVAILABLE = "flashlight_available"; /** @@ -8517,18 +8972,21 @@ public final class Settings { * * @hide */ + @Readable public static final String FLASHLIGHT_ENABLED = "flashlight_enabled"; /** * Whether or not face unlock is allowed on Keyguard. * @hide */ + @Readable public static final String FACE_UNLOCK_KEYGUARD_ENABLED = "face_unlock_keyguard_enabled"; /** * Whether or not face unlock dismisses the keyguard. * @hide */ + @Readable public static final String FACE_UNLOCK_DISMISSES_KEYGUARD = "face_unlock_dismisses_keyguard"; @@ -8536,6 +8994,7 @@ public final class Settings { * Whether or not media is shown automatically when bypassing as a heads up. * @hide */ + @Readable public static final String SHOW_MEDIA_WHEN_BYPASSING = "show_media_when_bypassing"; @@ -8544,6 +9003,7 @@ public final class Settings { * truth is obtained through the HAL. * @hide */ + @Readable public static final String FACE_UNLOCK_ATTENTION_REQUIRED = "face_unlock_attention_required"; @@ -8552,6 +9012,7 @@ public final class Settings { * cached value, the source of truth is obtained through the HAL. * @hide */ + @Readable public static final String FACE_UNLOCK_DIVERSITY_REQUIRED = "face_unlock_diversity_required"; @@ -8560,6 +9021,7 @@ public final class Settings { * Whether or not face unlock is allowed for apps (through BiometricPrompt). * @hide */ + @Readable public static final String FACE_UNLOCK_APP_ENABLED = "face_unlock_app_enabled"; /** @@ -8569,6 +9031,7 @@ public final class Settings { * setConfirmationRequired API. * @hide */ + @Readable public static final String FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION = "face_unlock_always_require_confirmation"; @@ -8583,12 +9046,14 @@ public final class Settings { * * @hide */ + @Readable public static final String FACE_UNLOCK_RE_ENROLL = "face_unlock_re_enroll"; /** * Whether or not debugging is enabled. * @hide */ + @Readable public static final String BIOMETRIC_DEBUG_ENABLED = "biometric_debug_enabled"; @@ -8597,6 +9062,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled"; /** @@ -8604,6 +9070,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity"; /** @@ -8611,6 +9078,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED = "assist_gesture_silence_alerts_enabled"; @@ -8619,6 +9087,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_GESTURE_WAKE_ENABLED = "assist_gesture_wake_enabled"; @@ -8630,36 +9099,42 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete"; /** * Control whether Trust Agents are in active unlock or extend unlock mode. * @hide */ + @Readable public static final String TRUST_AGENTS_EXTEND_UNLOCK = "trust_agents_extend_unlock"; /** * Control whether the screen locks when trust is lost. * @hide */ + @Readable public static final String LOCK_SCREEN_WHEN_TRUST_LOST = "lock_screen_when_trust_lost"; /** * Control whether Night display is currently activated. * @hide */ + @Readable public static final String NIGHT_DISPLAY_ACTIVATED = "night_display_activated"; /** * Control whether Night display will automatically activate/deactivate. * @hide */ + @Readable public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode"; /** * Control the color temperature of Night Display, represented in Kelvin. * @hide */ + @Readable public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE = "night_display_color_temperature"; @@ -8668,6 +9143,7 @@ public final class Settings { * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). * @hide */ + @Readable public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time"; @@ -8676,6 +9152,7 @@ public final class Settings { * Represented as milliseconds from midnight (e.g. 21600000 == 6am). * @hide */ + @Readable public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time"; /** @@ -8684,6 +9161,7 @@ public final class Settings { * legacy cases, this is represented by the time in milliseconds (since epoch). * @hide */ + @Readable public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME = "night_display_last_activated_time"; @@ -8691,6 +9169,7 @@ public final class Settings { * Control whether display white balance is currently enabled. * @hide */ + @Readable public static final String DISPLAY_WHITE_BALANCE_ENABLED = "display_white_balance_enabled"; /** @@ -8701,6 +9180,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; /** @@ -8710,6 +9190,7 @@ public final class Settings { * * @hide */ + @Readable public static final String VR_DISPLAY_MODE = "vr_display_mode"; /** @@ -8743,6 +9224,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled"; /** @@ -8750,6 +9232,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MANAGED_PROFILE_CONTACT_REMOTE_SEARCH = "managed_profile_contact_remote_search"; @@ -8758,6 +9241,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CROSS_PROFILE_CALENDAR_ENABLED = "cross_profile_calendar_enabled"; @@ -8766,6 +9250,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOMATIC_STORAGE_MANAGER_ENABLED = "automatic_storage_manager_enabled"; @@ -8774,6 +9259,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN = "automatic_storage_manager_days_to_retain"; @@ -8789,6 +9275,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED = "automatic_storage_manager_bytes_cleared"; @@ -8797,6 +9284,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOMATIC_STORAGE_MANAGER_LAST_RUN = "automatic_storage_manager_last_run"; /** @@ -8806,6 +9294,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY = "automatic_storage_manager_turned_off_by_policy"; @@ -8813,6 +9302,7 @@ public final class Settings { * Whether SystemUI navigation keys is enabled. * @hide */ + @Readable public static final String SYSTEM_NAVIGATION_KEYS_ENABLED = "system_navigation_keys_enabled"; @@ -8821,6 +9311,7 @@ public final class Settings { * * @hide */ + @Readable public static final String QS_TILES = "sysui_qs_tiles"; /** @@ -8831,6 +9322,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CONTROLS_ENABLED = "controls_enabled"; /** @@ -8840,6 +9332,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content"; @@ -8849,18 +9342,21 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled"; /** * Has this pairable device been paired or upgraded from a previously paired system. * @hide */ + @Readable public static final String DEVICE_PAIRED = "device_paired"; /** * Specifies additional package name for broadcasting the CMAS messages. * @hide */ + @Readable public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg"; /** @@ -8870,6 +9366,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable public static final String NOTIFICATION_BADGING = "notification_badging"; /** @@ -8879,6 +9376,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled"; /** @@ -8887,6 +9385,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String BUBBLE_IMPORTANT_CONVERSATIONS = "bubble_important_conversations"; @@ -8896,18 +9395,21 @@ public final class Settings { * * @hide */ + @Readable public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl"; /** * Comma separated list of QS tiles that have been auto-added already. * @hide */ + @Readable public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles"; /** * Whether the Lockdown button should be shown in the power menu. * @hide */ + @Readable public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu"; /** @@ -8935,6 +9437,7 @@ public final class Settings { * Type: string * @hide */ + @Readable public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants"; @@ -8952,6 +9455,7 @@ public final class Settings { * Type: string * @hide */ + @Readable public static final String BACKUP_LOCAL_TRANSPORT_PARAMETERS = "backup_local_transport_parameters"; @@ -8960,6 +9464,7 @@ public final class Settings { * the user is driving. * @hide */ + @Readable public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving"; /** @@ -8969,6 +9474,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture"; /** @hide */ @@ -8985,6 +9491,7 @@ public final class Settings { * The number of times (integer) the user has manually enabled battery saver. * @hide */ + @Readable public static final String LOW_POWER_MANUAL_ACTIVATION_COUNT = "low_power_manual_activation_count"; @@ -8994,6 +9501,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOW_POWER_WARNING_ACKNOWLEDGED = "low_power_warning_acknowledged"; @@ -9002,6 +9510,7 @@ public final class Settings { * suppressed. * @hide */ + @Readable public static final String SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION = "suppress_auto_battery_saver_suggestion"; @@ -9010,6 +9519,7 @@ public final class Settings { * Type: string * @hide */ + @Readable public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE = "packages_to_clear_data_before_full_restore"; @@ -9018,6 +9528,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; @@ -9026,6 +9537,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; @@ -9035,6 +9547,7 @@ public final class Settings { */ @SystemApi @Deprecated + @Readable public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE = "location_permissions_upgrade_to_q_mode"; @@ -9043,6 +9556,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled"; /** @@ -9053,6 +9567,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages"; @@ -9063,6 +9578,7 @@ public final class Settings { * 2 = fully gestural * @hide */ + @Readable public static final String NAVIGATION_MODE = "navigation_mode"; @@ -9070,6 +9586,7 @@ public final class Settings { * Scale factor for the back gesture inset size on the left side of the screen. * @hide */ + @Readable public static final String BACK_GESTURE_INSET_SCALE_LEFT = "back_gesture_inset_scale_left"; @@ -9077,6 +9594,7 @@ public final class Settings { * Scale factor for the back gesture inset size on the right side of the screen. * @hide */ + @Readable public static final String BACK_GESTURE_INSET_SCALE_RIGHT = "back_gesture_inset_scale_right"; @@ -9086,30 +9604,35 @@ public final class Settings { * No VALIDATOR as this setting will not be backed up. * @hide */ + @Readable public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component"; /** * Controls whether aware is enabled. * @hide */ + @Readable public static final String AWARE_ENABLED = "aware_enabled"; /** * Controls whether aware_lock is enabled. * @hide */ + @Readable public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled"; /** * Controls whether tap gesture is enabled. * @hide */ + @Readable public static final String TAP_GESTURE = "tap_gesture"; /** * Controls whether the people strip is enabled. * @hide */ + @Readable public static final String PEOPLE_STRIP = "people_strip"; /** @@ -9119,6 +9642,7 @@ public final class Settings { * @see Settings.Global#SHOW_MEDIA_ON_QUICK_SETTINGS * @hide */ + @Readable public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption"; /** @@ -9127,6 +9651,7 @@ public final class Settings { * @see Settings.Secure#MEDIA_CONTROLS_RESUME * @hide */ + @Readable public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked"; /** @@ -9138,6 +9663,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String ACCESSIBILITY_MAGNIFICATION_MODE = "accessibility_magnification_mode"; @@ -9173,6 +9699,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability"; @@ -9182,6 +9709,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT = "accessibility_show_window_magnification_prompt"; @@ -9198,6 +9726,7 @@ public final class Settings { * @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU * @hide */ + @Readable public static final String ACCESSIBILITY_BUTTON_MODE = "accessibility_button_mode"; @@ -9226,6 +9755,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_FLOATING_MENU_SIZE = "accessibility_floating_menu_size"; @@ -9238,6 +9768,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE = "accessibility_floating_menu_icon_type"; @@ -9246,6 +9777,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED = "accessibility_floating_menu_fade_enabled"; @@ -9255,6 +9787,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY = "accessibility_floating_menu_opacity"; @@ -9263,6 +9796,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled"; /** @@ -9274,6 +9808,7 @@ public final class Settings { * * @hide */ + @Readable public static final String[] LEGACY_RESTORE_SETTINGS = { ENABLED_NOTIFICATION_LISTENERS, ENABLED_NOTIFICATION_ASSISTANT, @@ -9285,6 +9820,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS = "reminder_exp_learning_time_elapsed"; @@ -9293,6 +9829,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT = "reminder_exp_learning_event_count"; @@ -9406,6 +9943,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String NOTIFICATION_BUBBLES = "notification_bubbles"; /** @@ -9414,6 +9952,7 @@ public final class Settings { * Type: int * @hide */ + @Readable public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked"; /** @@ -9421,6 +9960,7 @@ public final class Settings { * <p>1 = apply ramping ringer * <p>0 = do not apply ramping ringer */ + @Readable public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer"; /** @@ -9432,12 +9972,14 @@ public final class Settings { * No longer used. Should be removed once all dependencies have been updated. */ @UnsupportedAppUsage + @Readable public static final String ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED = "enable_accessibility_global_gesture_enabled"; /** * Whether Airplane Mode is on. */ + @Readable public static final String AIRPLANE_MODE_ON = "airplane_mode_on"; /** @@ -9445,30 +9987,36 @@ public final class Settings { * {@hide} */ @SystemApi + @Readable public static final String THEATER_MODE_ON = "theater_mode_on"; /** * Constant for use in AIRPLANE_MODE_RADIOS to specify Bluetooth radio. */ + @Readable public static final String RADIO_BLUETOOTH = "bluetooth"; /** * Constant for use in AIRPLANE_MODE_RADIOS to specify Wi-Fi radio. */ + @Readable public static final String RADIO_WIFI = "wifi"; /** * {@hide} */ + @Readable public static final String RADIO_WIMAX = "wimax"; /** * Constant for use in AIRPLANE_MODE_RADIOS to specify Cellular radio. */ + @Readable public static final String RADIO_CELL = "cell"; /** * Constant for use in AIRPLANE_MODE_RADIOS to specify NFC radio. */ + @Readable public static final String RADIO_NFC = "nfc"; /** @@ -9476,6 +10024,7 @@ public final class Settings { * is on. This overrides WIFI_ON and BLUETOOTH_ON, if Wi-Fi and bluetooth are * included in the comma separated list. */ + @Readable public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios"; /** @@ -9487,6 +10036,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios"; /** @@ -9494,6 +10044,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device"; /** @@ -9501,6 +10052,7 @@ public final class Settings { * See {@link android.bluetooth.BluetoothProfile}. * {@hide} */ + @Readable public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles"; /** @@ -9513,6 +10065,7 @@ public final class Settings { * "00:11:22,0;01:02:03:04,2" * @hide */ + @Readable public static final String BLUETOOTH_INTEROPERABILITY_LIST = "bluetooth_interoperability_list"; /** @@ -9525,6 +10078,7 @@ public final class Settings { * @deprecated This is no longer used or set by the platform. */ @Deprecated + @Readable public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy"; /** @@ -9556,60 +10110,70 @@ public final class Settings { * Value to specify if the user prefers the date, time and time zone * to be automatically fetched from the network (NITZ). 1=yes, 0=no */ + @Readable public static final String AUTO_TIME = "auto_time"; /** * Value to specify if the user prefers the time zone * to be automatically fetched from the network (NITZ). 1=yes, 0=no */ + @Readable public static final String AUTO_TIME_ZONE = "auto_time_zone"; /** * URI for the car dock "in" event sound. * @hide */ + @Readable public static final String CAR_DOCK_SOUND = "car_dock_sound"; /** * URI for the car dock "out" event sound. * @hide */ + @Readable public static final String CAR_UNDOCK_SOUND = "car_undock_sound"; /** * URI for the desk dock "in" event sound. * @hide */ + @Readable public static final String DESK_DOCK_SOUND = "desk_dock_sound"; /** * URI for the desk dock "out" event sound. * @hide */ + @Readable public static final String DESK_UNDOCK_SOUND = "desk_undock_sound"; /** * Whether to play a sound for dock events. * @hide */ + @Readable public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled"; /** * Whether to play a sound for dock events, only when an accessibility service is on. * @hide */ + @Readable public static final String DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY = "dock_sounds_enabled_when_accessbility"; /** * URI for the "device locked" (keyguard shown) sound. * @hide */ + @Readable public static final String LOCK_SOUND = "lock_sound"; /** * URI for the "device unlocked" sound. * @hide */ + @Readable public static final String UNLOCK_SOUND = "unlock_sound"; /** @@ -9617,24 +10181,28 @@ public final class Settings { * state without unlocking. * @hide */ + @Readable public static final String TRUSTED_SOUND = "trusted_sound"; /** * URI for the low battery sound file. * @hide */ + @Readable public static final String LOW_BATTERY_SOUND = "low_battery_sound"; /** * Whether to play a sound for low-battery alerts. * @hide */ + @Readable public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled"; /** * URI for the "wireless charging started" sound. * @hide */ + @Readable public static final String WIRELESS_CHARGING_STARTED_SOUND = "wireless_charging_started_sound"; @@ -9642,6 +10210,7 @@ public final class Settings { * URI for "wired charging started" sound. * @hide */ + @Readable public static final String CHARGING_STARTED_SOUND = "charging_started_sound"; /** @@ -9671,6 +10240,7 @@ public final class Settings { * </ul> * These values can be OR-ed together. */ + @Readable public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in"; /** @@ -9678,6 +10248,7 @@ public final class Settings { * in the power menu. * @hide */ + @Readable public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; /** @@ -9686,6 +10257,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CUSTOM_BUGREPORT_HANDLER_APP = "custom_bugreport_handler_app"; /** @@ -9694,29 +10266,34 @@ public final class Settings { * * @hide */ + @Readable public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user"; /** * Whether ADB over USB is enabled. */ + @Readable public static final String ADB_ENABLED = "adb_enabled"; /** * Whether ADB over Wifi is enabled. * @hide */ + @Readable public static final String ADB_WIFI_ENABLED = "adb_wifi_enabled"; /** * Whether Views are allowed to save their attribute data. * @hide */ + @Readable public static final String DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes"; /** * Which application package is allowed to save View attribute data. * @hide */ + @Readable public static final String DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE = "debug_view_attributes_application_package"; @@ -9724,12 +10301,14 @@ public final class Settings { * Whether assisted GPS should be enabled or not. * @hide */ + @Readable public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled"; /** * Whether bluetooth is enabled/disabled * 0=disabled. 1=enabled. */ + @Readable public static final String BLUETOOTH_ON = "bluetooth_on"; /** @@ -9738,6 +10317,7 @@ public final class Settings { * 1 = CDMA Cell Broadcast SMS enabled * @hide */ + @Readable public static final String CDMA_CELL_BROADCAST_SMS = "cdma_cell_broadcast_sms"; @@ -9747,6 +10327,7 @@ public final class Settings { * 2 = Roaming on any networks * @hide */ + @Readable public static final String CDMA_ROAMING_MODE = "roaming_settings"; /** @@ -9754,6 +10335,7 @@ public final class Settings { * 1 = NV * @hide */ + @Readable public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode"; /** @@ -9763,44 +10345,49 @@ public final class Settings { * * @hide */ + @Readable public static final String DEFAULT_RESTRICT_BACKGROUND_DATA = "default_restrict_background_data"; /** Inactivity timeout to track mobile data activity. - * - * If set to a positive integer, it indicates the inactivity timeout value in seconds to - * infer the data activity of mobile network. After a period of no activity on mobile - * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE} - * intent is fired to indicate a transition of network status from "active" to "idle". Any - * subsequent activity on mobile networks triggers the firing of {@code - * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active". - * - * Network activity refers to transmitting or receiving data on the network interfaces. - * - * Tracking is disabled if set to zero or negative value. - * - * @hide - */ - public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile"; + * + * If set to a positive integer, it indicates the inactivity timeout value in seconds to + * infer the data activity of mobile network. After a period of no activity on mobile + * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE} + * intent is fired to indicate a transition of network status from "active" to "idle". Any + * subsequent activity on mobile networks triggers the firing of {@code + * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active". + * + * Network activity refers to transmitting or receiving data on the network interfaces. + * + * Tracking is disabled if set to zero or negative value. + * + * @hide + */ + @Readable + public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile"; - /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE} - * but for Wifi network. - * @hide - */ - public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi"; + /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE} + * but for Wifi network. + * @hide + */ + @Readable + public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi"; - /** - * Whether or not data roaming is enabled. (0 = false, 1 = true) - */ - public static final String DATA_ROAMING = "data_roaming"; + /** + * Whether or not data roaming is enabled. (0 = false, 1 = true) + */ + @Readable + public static final String DATA_ROAMING = "data_roaming"; - /** - * The value passed to a Mobile DataConnection via bringUp which defines the - * number of retries to preform when setting up the initial connection. The default - * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1. - * @hide - */ - public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry"; + /** + * The value passed to a Mobile DataConnection via bringUp which defines the + * number of retries to perform when setting up the initial connection. The default + * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1. + * @hide + */ + @Readable + public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry"; /** * Whether any package can be on external storage. When this is true, any @@ -9808,6 +10395,7 @@ public final class Settings { * or moving onto external storage. (0 = false, 1 = true) * @hide */ + @Readable public static final String FORCE_ALLOW_ON_EXTERNAL = "force_allow_on_external"; /** @@ -9822,6 +10410,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus"; /** @@ -9833,6 +10422,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String EUICC_PROVISIONED = "euicc_provisioned"; /** @@ -9848,6 +10438,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries"; /** @@ -9863,6 +10454,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries"; /** @@ -9871,6 +10463,7 @@ public final class Settings { * (0 = false, 1 = true) * @hide */ + @Readable public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES = "force_resizable_activities"; @@ -9878,6 +10471,7 @@ public final class Settings { * Whether to enable experimental freeform support for windows. * @hide */ + @Readable public static final String DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT = "enable_freeform_support"; @@ -9885,6 +10479,7 @@ public final class Settings { * Whether to enable experimental desktop mode on secondary displays. * @hide */ + @Readable public static final String DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS = "force_desktop_mode_on_external_displays"; @@ -9896,6 +10491,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM = "enable_sizecompat_freeform"; @@ -9906,6 +10502,7 @@ public final class Settings { * @hide */ @TestApi + @Readable @SuppressLint("NoSettingsProvider") public static final String DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW = "enable_non_resizable_multi_window"; @@ -9917,6 +10514,7 @@ public final class Settings { * (0 = false, 1 = true) * @hide */ + @Readable public static final String DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR = "render_shadows_in_compositor"; @@ -9925,6 +10523,7 @@ public final class Settings { * (0 = false, 1 = true) * @hide */ + @Readable public static final String DEVELOPMENT_USE_BLAST_ADAPTER_VR = "use_blast_adapter_vr"; @@ -9933,6 +10532,7 @@ public final class Settings { * (0 = false, 1 = true) * @hide */ + @Readable public static final String DEVELOPMENT_USE_BLAST_ADAPTER_SV = "use_blast_adapter_sv"; @@ -9942,21 +10542,24 @@ public final class Settings { * * @hide */ + @Readable public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH = "wm_display_settings_path"; - /** + /** * Whether user has enabled development settings. */ - public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled"; + @Readable + public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled"; - /** + /** * Whether the device has been provisioned (0 = false, 1 = true). * <p>On a multiuser device with a separate system user, the screen may be locked * as soon as this is set to true and further activities cannot be launched on the * system user unless they are marked to show over keyguard. */ - public static final String DEVICE_PROVISIONED = "device_provisioned"; + @Readable + public static final String DEVICE_PROVISIONED = "device_provisioned"; /** * Indicates whether mobile data should be allowed while the device is being provisioned. @@ -9969,52 +10572,58 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = "device_provisioning_mobile_data"; - /** + /** * The saved value for WindowManagerService.setForcedDisplaySize(). * Two integers separated by a comma. If unset, then use the real display size. * @hide */ - public static final String DISPLAY_SIZE_FORCED = "display_size_forced"; + @Readable + public static final String DISPLAY_SIZE_FORCED = "display_size_forced"; - /** + /** * The saved value for WindowManagerService.setForcedDisplayScalingMode(). * 0 or unset if scaling is automatic, 1 if scaling is disabled. * @hide */ - public static final String DISPLAY_SCALING_FORCE = "display_scaling_force"; + @Readable + public static final String DISPLAY_SCALING_FORCE = "display_scaling_force"; - /** + /** * The maximum size, in bytes, of a download that the download manager will transfer over * a non-wifi connection. * @hide */ - public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE = + @Readable + public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE = "download_manager_max_bytes_over_mobile"; - /** + /** * The recommended maximum size, in bytes, of a download that the download manager should * transfer over a non-wifi connection. Over this size, the use will be warned, but will * have the option to start the download over the mobile connection anyway. * @hide */ - public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE = + @Readable + public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE = "download_manager_recommended_max_bytes_over_mobile"; - /** + /** * @deprecated Use {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS} instead */ - @Deprecated - public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS; + @Deprecated + public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS; - /** + /** * Whether HDMI control shall be enabled. If disabled, no CEC/MHL command will be * sent or processed. (0 = false, 1 = true) * @hide */ - public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled"; + @Readable + public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled"; /** * Controls whether volume control commands via HDMI CEC are enabled. (0 = false, 1 = @@ -10050,16 +10659,18 @@ public final class Settings { * @hide * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean) */ + @Readable public static final String HDMI_CONTROL_VOLUME_CONTROL_ENABLED = "hdmi_control_volume_control_enabled"; - /** + /** * Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on * system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will * be played on AVR instead of TV spaeker. If disabled, the system audio mode will never be * activated. * @hide */ + @Readable public static final String HDMI_SYSTEM_AUDIO_CONTROL_ENABLED = "hdmi_system_audio_control_enabled"; @@ -10069,6 +10680,7 @@ public final class Settings { * disabled, you can only switch the input via controls on this device. * @hide */ + @Readable public static final String HDMI_CEC_SWITCH_ENABLED = "hdmi_cec_switch_enabled"; @@ -10076,6 +10688,7 @@ public final class Settings { * HDMI CEC version to use. Defaults to v1.4b. * @hide */ + @Readable public static final String HDMI_CEC_VERSION = "hdmi_cec_version"; @@ -10085,6 +10698,7 @@ public final class Settings { * * @hide */ + @Readable public static final String HDMI_CONTROL_AUTO_WAKEUP_ENABLED = "hdmi_control_auto_wakeup_enabled"; @@ -10094,6 +10708,7 @@ public final class Settings { * * @hide */ + @Readable public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED = "hdmi_control_auto_device_off_enabled"; @@ -10115,6 +10730,7 @@ public final class Settings { * * @hide */ + @Readable public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP = "hdmi_control_send_standby_on_sleep"; @@ -10122,6 +10738,7 @@ public final class Settings { * Whether or not media is shown automatically when bypassing as a heads up. * @hide */ + @Readable public static final String SHOW_MEDIA_ON_QUICK_SETTINGS = "qs_media_player"; @@ -10131,6 +10748,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS = "location_background_throttle_interval_ms"; @@ -10139,6 +10757,7 @@ public final class Settings { * to request. * @hide */ + @Readable public static final String LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS = "location_background_throttle_proximity_alert_interval_ms"; @@ -10146,6 +10765,7 @@ public final class Settings { * Packages that are whitelisted for background throttling (throttling will not be applied). * @hide */ + @Readable public static final String LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST = "location_background_throttle_package_whitelist"; @@ -10155,6 +10775,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist"; @@ -10163,23 +10784,26 @@ public final class Settings { * (0 = false, 1 = true) * @hide */ - public static final String MHL_INPUT_SWITCHING_ENABLED = "mhl_input_switching_enabled"; + @Readable + public static final String MHL_INPUT_SWITCHING_ENABLED = "mhl_input_switching_enabled"; - /** + /** * Whether TV will charge the mobile device connected at MHL port. (0 = false, 1 = true) * @hide */ - public static final String MHL_POWER_CHARGE_ENABLED = "mhl_power_charge_enabled"; + @Readable + public static final String MHL_POWER_CHARGE_ENABLED = "mhl_power_charge_enabled"; - /** + /** * Whether mobile data connections are allowed by the user. See * ConnectivityManager for more info. * @hide */ - @UnsupportedAppUsage - public static final String MOBILE_DATA = "mobile_data"; + @UnsupportedAppUsage + @Readable + public static final String MOBILE_DATA = "mobile_data"; - /** + /** * Whether the mobile data connection should remain active even when higher * priority networks like WiFi are active, to help make network switching faster. * @@ -10188,7 +10812,8 @@ public final class Settings { * (0 = disabled, 1 = enabled) * @hide */ - public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; + @Readable + public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; /** * Whether the wifi data connection should remain active even when higher @@ -10201,195 +10826,249 @@ public final class Settings { * (0 = disabled, 1 = enabled) * @hide */ + @Readable public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested"; /** * Size of the event buffer for IP connectivity metrics. * @hide */ + @Readable public static final String CONNECTIVITY_METRICS_BUFFER_SIZE = "connectivity_metrics_buffer_size"; - /** {@hide} */ - public static final String NETSTATS_ENABLED = "netstats_enabled"; - /** {@hide} */ - public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval"; - /** {@hide} */ - @Deprecated - public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age"; - /** {@hide} */ - public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes"; - /** {@hide} */ - public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled"; - /** {@hide} */ - public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled"; - /** {@hide} */ - public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED = "netstats_combine_subtype_enabled"; - - /** {@hide} */ - public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration"; - /** {@hide} */ - public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes"; - /** {@hide} */ - public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age"; - /** {@hide} */ - public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age"; - - /** {@hide} */ - public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration"; - /** {@hide} */ - public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes"; - /** {@hide} */ - public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age"; - /** {@hide} */ - public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age"; - - /** {@hide} */ - public static final String NETSTATS_UID_TAG_BUCKET_DURATION = "netstats_uid_tag_bucket_duration"; - /** {@hide} */ - public static final String NETSTATS_UID_TAG_PERSIST_BYTES = "netstats_uid_tag_persist_bytes"; - /** {@hide} */ - public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age"; - /** {@hide} */ - public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age"; - - /** {@hide} */ - public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled"; - /** {@hide} */ - public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited"; - /** {@hide} */ - public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited"; - /** {@hide} */ - public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs"; - /** {@hide} */ - public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = "netpolicy_quota_frac_multipath"; - - /** {@hide} */ - public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled"; + /** {@hide} */ + @Readable + public static final String NETSTATS_ENABLED = "netstats_enabled"; + /** {@hide} */ + @Readable + public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval"; + /** + * @deprecated + * {@hide} + */ + @Deprecated + @Readable + public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age"; + /** {@hide} */ + @Readable + public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes"; + /** {@hide} */ + @Readable + public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled"; + /** {@hide} */ + @Readable + public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled"; + /** {@hide} */ + @Readable + public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED = + "netstats_combine_subtype_enabled"; - /** + /** {@hide} */ + @Readable + public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration"; + /** {@hide} */ + @Readable + public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes"; + /** {@hide} */ + @Readable + public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age"; + /** {@hide} */ + @Readable + public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age"; + + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration"; + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes"; + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age"; + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age"; + + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_TAG_BUCKET_DURATION = + "netstats_uid_tag_bucket_duration"; + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_TAG_PERSIST_BYTES = + "netstats_uid_tag_persist_bytes"; + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age"; + /** {@hide} */ + @Readable + public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age"; + + /** {@hide} */ + @Readable + public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled"; + /** {@hide} */ + @Readable + public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited"; + /** {@hide} */ + @Readable + public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited"; + /** {@hide} */ + @Readable + public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs"; + /** {@hide} */ + @Readable + public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = + "netpolicy_quota_frac_multipath"; + + /** {@hide} */ + @Readable + public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled"; + + /** * User preference for which network(s) should be used. Only the * connectivity service should touch this. */ - public static final String NETWORK_PREFERENCE = "network_preference"; + @Readable + public static final String NETWORK_PREFERENCE = "network_preference"; - /** + /** * Which package name to use for network scoring. If null, or if the package is not a valid * scorer app, external network scores will neither be requested nor accepted. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final String NETWORK_SCORER_APP = "network_scorer_app"; + @Readable + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static final String NETWORK_SCORER_APP = "network_scorer_app"; /** * Whether night display forced auto mode is available. * 0 = unavailable, 1 = available. * @hide */ + @Readable public static final String NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE = "night_display_forced_auto_mode_available"; - /** + /** * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been * exceeded. * @hide */ - public static final String NITZ_UPDATE_DIFF = "nitz_update_diff"; + @Readable + public static final String NITZ_UPDATE_DIFF = "nitz_update_diff"; - /** + /** * The length of time in milli-seconds that automatic small adjustments to * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded. * @hide */ - public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing"; + @Readable + public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing"; - /** Preferred NTP server. {@hide} */ - public static final String NTP_SERVER = "ntp_server"; - /** Timeout in milliseconds to wait for NTP server. {@hide} */ - public static final String NTP_TIMEOUT = "ntp_timeout"; + /** Preferred NTP server. {@hide} */ + @Readable + public static final String NTP_SERVER = "ntp_server"; + /** Timeout in milliseconds to wait for NTP server. {@hide} */ + @Readable + public static final String NTP_TIMEOUT = "ntp_timeout"; - /** {@hide} */ - public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval"; + /** {@hide} */ + @Readable + public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval"; /** * Whether or not Settings should enable psd API. * {@hide} */ + @Readable public static final String SETTINGS_USE_PSD_API = "settings_use_psd_api"; /** * Whether or not Settings should enable external provider API. * {@hide} */ + @Readable public static final String SETTINGS_USE_EXTERNAL_PROVIDER_API = "settings_use_external_provider_api"; - /** + /** * Sample validity in seconds to configure for the system DNS resolver. * {@hide} */ - public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS = + @Readable + public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS = "dns_resolver_sample_validity_seconds"; - /** + /** * Success threshold in percent for use with the system DNS resolver. * {@hide} */ - public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT = + @Readable + public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT = "dns_resolver_success_threshold_percent"; - /** + /** * Minimum number of samples needed for statistics to be considered meaningful in the * system DNS resolver. * {@hide} */ - public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples"; + @Readable + public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples"; - /** + /** * Maximum number taken into account for statistics purposes in the system DNS resolver. * {@hide} */ - public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples"; + @Readable + public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples"; - /** + /** * Whether to disable the automatic scheduling of system updates. * 1 = system updates won't be automatically scheduled (will always * present notification instead). * 0 = system updates will be automatically scheduled. (default) * @hide */ - @SystemApi - public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; + @SystemApi + @Readable + public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; - /** Timeout for package verification. + /** Timeout for package verification. * @hide */ - public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout"; + @Readable + public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout"; /** Timeout for app integrity verification. * @hide */ + @Readable public static final String APP_INTEGRITY_VERIFICATION_TIMEOUT = "app_integrity_verification_timeout"; - /** Default response code for package verification. + /** Default response code for package verification. * @hide */ - public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response"; + @Readable + public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response"; - /** + /** * Show package verification setting in the Settings app. * 1 = show (default) * 0 = hide * @hide */ - public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible"; + @Readable + public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible"; - /** + /** * Run package verification on apps installed through ADB/ADT/USB * 1 = perform package verification on ADB installs (default) * 0 = bypass package verification on ADB installs * @hide */ - public static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs"; + @Readable + public static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs"; /** * Run integrity checks for integrity rule providers. @@ -10397,117 +11076,131 @@ public final class Settings { * 1 = perform integrity verification on installs from rule providers * @hide */ + @Readable public static final String INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER = "verify_integrity_for_rule_provider"; - /** + /** * Time since last fstrim (milliseconds) after which we force one to happen * during device startup. If unset, the default is 3 days. * @hide */ - public static final String FSTRIM_MANDATORY_INTERVAL = "fstrim_mandatory_interval"; + @Readable + public static final String FSTRIM_MANDATORY_INTERVAL = "fstrim_mandatory_interval"; - /** + /** * The interval in milliseconds at which to check packet counts on the * mobile data interface when screen is on, to detect possible data * connection problems. * @hide */ - public static final String PDP_WATCHDOG_POLL_INTERVAL_MS = + @Readable + public static final String PDP_WATCHDOG_POLL_INTERVAL_MS = "pdp_watchdog_poll_interval_ms"; - /** + /** * The interval in milliseconds at which to check packet counts on the * mobile data interface when screen is off, to detect possible data * connection problems. * @hide */ - public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS = + @Readable + public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS = "pdp_watchdog_long_poll_interval_ms"; - /** + /** * The interval in milliseconds at which to check packet counts on the * mobile data interface after {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} * outgoing packets has been reached without incoming packets. * @hide */ - public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS = + @Readable + public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS = "pdp_watchdog_error_poll_interval_ms"; - /** + /** * The number of outgoing packets sent without seeing an incoming packet * that triggers a countdown (of {@link #PDP_WATCHDOG_ERROR_POLL_COUNT} * device is logged to the event log * @hide */ - public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT = + @Readable + public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT = "pdp_watchdog_trigger_packet_count"; - /** + /** * The number of polls to perform (at {@link #PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS}) * after hitting {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} before * attempting data connection recovery. * @hide */ - public static final String PDP_WATCHDOG_ERROR_POLL_COUNT = + @Readable + public static final String PDP_WATCHDOG_ERROR_POLL_COUNT = "pdp_watchdog_error_poll_count"; - /** + /** * The number of failed PDP reset attempts before moving to something more * drastic: re-registering to the network. * @hide */ - public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT = + @Readable + public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT = "pdp_watchdog_max_pdp_reset_fail_count"; - /** + /** * URL to open browser on to allow user to manage a prepay account * @hide */ - public static final String SETUP_PREPAID_DATA_SERVICE_URL = + @Readable + public static final String SETUP_PREPAID_DATA_SERVICE_URL = "setup_prepaid_data_service_url"; - /** + /** * URL to attempt a GET on to see if this is a prepay device * @hide */ - public static final String SETUP_PREPAID_DETECTION_TARGET_URL = + @Readable + public static final String SETUP_PREPAID_DETECTION_TARGET_URL = "setup_prepaid_detection_target_url"; - /** + /** * Host to check for a redirect to after an attempt to GET * SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there, * this is a prepaid device with zero balance.) * @hide */ - public static final String SETUP_PREPAID_DETECTION_REDIR_HOST = + @Readable + public static final String SETUP_PREPAID_DETECTION_REDIR_HOST = "setup_prepaid_detection_redir_host"; - /** + /** * The interval in milliseconds at which to check the number of SMS sent out without asking * for use permit, to limit the un-authorized SMS usage. * * @hide */ - public static final String SMS_OUTGOING_CHECK_INTERVAL_MS = + @Readable + public static final String SMS_OUTGOING_CHECK_INTERVAL_MS = "sms_outgoing_check_interval_ms"; - /** + /** * The number of outgoing SMS sent without asking for user permit (of {@link * #SMS_OUTGOING_CHECK_INTERVAL_MS} * * @hide */ - public static final String SMS_OUTGOING_CHECK_MAX_COUNT = + @Readable + public static final String SMS_OUTGOING_CHECK_MAX_COUNT = "sms_outgoing_check_max_count"; - /** + /** * Used to disable SMS short code confirmation - defaults to true. * True indcates we will do the check, etc. Set to false to disable. * @see com.android.internal.telephony.SmsUsageMonitor * @hide */ - public static final String SMS_SHORT_CODE_CONFIRMATION = "sms_short_code_confirmation"; + @Readable + public static final String SMS_SHORT_CODE_CONFIRMATION = "sms_short_code_confirmation"; /** * Used to select which country we use to determine premium sms codes. @@ -10516,6 +11209,7 @@ public final class Settings { * or com.android.internal.telephony.SMSDispatcher.PREMIUM_RULE_USE_BOTH. * @hide */ + @Readable public static final String SMS_SHORT_CODE_RULE = "sms_short_code_rule"; /** @@ -10523,6 +11217,7 @@ public final class Settings { * build config value. * @hide */ + @Readable public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd"; /** @@ -10530,6 +11225,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String TETHER_SUPPORTED = "tether_supported"; /** @@ -10537,6 +11233,7 @@ public final class Settings { * which defaults to false. * @hide */ + @Readable public static final String TETHER_DUN_REQUIRED = "tether_dun_required"; /** @@ -10548,6 +11245,7 @@ public final class Settings { * note that empty fields can be omitted: "name,apn,,,,,,,,,310,260,,DUN" * @hide */ + @Readable public static final String TETHER_DUN_APN = "tether_dun_apn"; /** @@ -10558,6 +11256,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled"; /** @@ -10567,6 +11266,7 @@ public final class Settings { * is interpreted as |false|. * @hide */ + @Readable public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = "tether_enable_legacy_dhcp_server"; @@ -10581,6 +11281,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist"; /** @@ -10591,62 +11292,71 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String CARRIER_APP_NAMES = "carrier_app_names"; - /** + /** * USB Mass Storage Enabled */ - public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; + @Readable + public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; - /** + /** * If this setting is set (to anything), then all references * to Gmail on the device must change to Google Mail. */ - public static final String USE_GOOGLE_MAIL = "use_google_mail"; + @Readable + public static final String USE_GOOGLE_MAIL = "use_google_mail"; /** * Whether or not switching/creating users is enabled by user. * @hide */ + @Readable public static final String USER_SWITCHER_ENABLED = "user_switcher_enabled"; /** * Webview Data reduction proxy key. * @hide */ + @Readable public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY = "webview_data_reduction_proxy_key"; - /** + /** * Name of the package used as WebView provider (if unset the provider is instead determined * by the system). * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final String WEBVIEW_PROVIDER = "webview_provider"; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable + public static final String WEBVIEW_PROVIDER = "webview_provider"; - /** + /** * Developer setting to enable WebView multiprocess rendering. * @hide */ - @SystemApi - public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; + @SystemApi + @Readable + public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; - /** + /** * The maximum number of notifications shown in 24 hours when switching networks. * @hide */ - public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT = + @Readable + public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT = "network_switch_notification_daily_limit"; - /** + /** * The minimum time in milliseconds between notifications when switching networks. * @hide */ - public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS = + @Readable + public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS = "network_switch_notification_rate_limit_millis"; - /** + /** * Whether to automatically switch away from wifi networks that lose Internet access. * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always * avoids such networks. Valid values are: @@ -10657,16 +11367,18 @@ public final class Settings { * * @hide */ - public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi"; + @Readable + public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi"; - /** + /** * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be * overridden by the system based on device or application state. If null, the value * specified by config_networkMeteredMultipathPreference is used. * * @hide */ - public static final String NETWORK_METERED_MULTIPATH_PREFERENCE = + @Readable + public static final String NETWORK_METERED_MULTIPATH_PREFERENCE = "network_metered_multipath_preference"; /** @@ -10675,6 +11387,7 @@ public final class Settings { * from data plan or data limit/warning set by the user. * @hide */ + @Readable public static final String NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES = "network_default_daily_multipath_quota_bytes"; @@ -10682,10 +11395,11 @@ public final class Settings { * Network watchlist last report time. * @hide */ + @Readable public static final String NETWORK_WATCHLIST_LAST_REPORT_TIME = "network_watchlist_last_report_time"; - /** + /** * The thresholds of the wifi throughput badging (SD, HD etc.) as a comma-delimited list of * colon-delimited key-value pairs. The key is the badging enum value defined in * android.net.ScoredNetwork and the value is the minimum sustained network throughput in @@ -10693,25 +11407,28 @@ public final class Settings { * * @hide */ - @SystemApi - public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds"; + @SystemApi + @Readable + public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds"; - /** + /** * Whether Wifi display is enabled/disabled * 0=disabled. 1=enabled. * @hide */ - public static final String WIFI_DISPLAY_ON = "wifi_display_on"; + @Readable + public static final String WIFI_DISPLAY_ON = "wifi_display_on"; - /** + /** * Whether Wifi display certification mode is enabled/disabled * 0=disabled. 1=enabled. * @hide */ - public static final String WIFI_DISPLAY_CERTIFICATION_ON = + @Readable + public static final String WIFI_DISPLAY_CERTIFICATION_ON = "wifi_display_certification_on"; - /** + /** * WPS Configuration method used by Wifi display, this setting only * takes effect when WIFI_DISPLAY_CERTIFICATION_ON is 1 (enabled). * @@ -10723,10 +11440,11 @@ public final class Settings { * WpsInfo.DISPLAY: use Display * @hide */ - public static final String WIFI_DISPLAY_WPS_CONFIG = + @Readable + public static final String WIFI_DISPLAY_WPS_CONFIG = "wifi_display_wps_config"; - /** + /** * Whether to notify the user of open networks. * <p> * If not connected and the scan results have an open network, we will @@ -10738,68 +11456,78 @@ public final class Settings { * @deprecated This feature is no longer controlled by this setting in * {@link android.os.Build.VERSION_CODES#O}. */ - @Deprecated - public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = + @Deprecated + @Readable + public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on"; - /** + /** * {@hide} */ - public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON = + @Readable + public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wimax_networks_available_notification_on"; - /** + /** * Delay (in seconds) before repeating the Wi-Fi networks available notification. * Connecting to a network will reset the timer. * @deprecated This is no longer used or set by the platform. */ - @Deprecated - public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = + @Deprecated + @Readable + public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay"; - /** + /** * 802.11 country code in ISO 3166 format * @hide */ - public static final String WIFI_COUNTRY_CODE = "wifi_country_code"; + @Readable + public static final String WIFI_COUNTRY_CODE = "wifi_country_code"; - /** + /** * The interval in milliseconds to issue wake up scans when wifi needs * to connect. This is necessary to connect to an access point when * device is on the move and the screen is off. * @hide */ - public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS = + @Readable + public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS = "wifi_framework_scan_interval_ms"; - /** + /** * The interval in milliseconds after which Wi-Fi is considered idle. * When idle, it is possible for the device to be switched from Wi-Fi to * the mobile data network. * @hide */ - public static final String WIFI_IDLE_MS = "wifi_idle_ms"; + @Readable + public static final String WIFI_IDLE_MS = "wifi_idle_ms"; - /** + /** * When the number of open networks exceeds this number, the * least-recently-used excess networks will be removed. * @deprecated This is no longer used or set by the platform. */ - @Deprecated - public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept"; + @Deprecated + @Readable + public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept"; - /** + /** * Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this. */ - public static final String WIFI_ON = "wifi_on"; + @Readable + public static final String WIFI_ON = "wifi_on"; - /** + /** * Setting to allow scans to be enabled even wifi is turned off for connectivity. * @hide * @deprecated To be removed. Use {@link WifiManager#setScanAlwaysAvailable(boolean)} for * setting the value and {@link WifiManager#isScanAlwaysAvailable()} for query. */ - public static final String WIFI_SCAN_ALWAYS_AVAILABLE = + @Deprecated + @Readable + public static final String WIFI_SCAN_ALWAYS_AVAILABLE = "wifi_scan_always_enabled"; /** @@ -10809,6 +11537,8 @@ public final class Settings { * @hide * @deprecated To be removed. */ + @Deprecated + @Readable public static final String WIFI_P2P_PENDING_FACTORY_RESET = "wifi_p2p_pending_factory_reset"; @@ -10821,6 +11551,8 @@ public final class Settings { * setAutoShutdownEnabled(boolean)} for setting the value and {@link SoftApConfiguration# * isAutoShutdownEnabled()} for query. */ + @Deprecated + @Readable public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled"; /** @@ -10833,6 +11565,7 @@ public final class Settings { */ @Deprecated @SystemApi + @Readable public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled"; /** @@ -10842,6 +11575,7 @@ public final class Settings { * Type: int (0 for false, 1 for true) * @hide */ + @Readable public static final String WIFI_MIGRATION_COMPLETED = "wifi_migration_completed"; /** @@ -10850,6 +11584,7 @@ public final class Settings { * Type: int (0 for false, 1 for true) * @hide */ + @Readable public static final String NETWORK_SCORING_UI_ENABLED = "network_scoring_ui_enabled"; /** @@ -10859,6 +11594,7 @@ public final class Settings { * Type: long * @hide */ + @Readable public static final String SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS = "speed_label_cache_eviction_age_millis"; @@ -10877,6 +11613,8 @@ public final class Settings { * @hide * @deprecated To be removed. */ + @Deprecated + @Readable public static final String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled"; @@ -10890,6 +11628,7 @@ public final class Settings { * Type: string - package name * @hide */ + @Readable public static final String NETWORK_RECOMMENDATIONS_PACKAGE = "network_recommendations_package"; @@ -10901,6 +11640,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; /** @@ -10910,6 +11650,7 @@ public final class Settings { * Type: long * @hide */ + @Readable public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS = "recommended_network_evaluator_cache_expiry_ms"; @@ -10921,6 +11662,8 @@ public final class Settings { * @deprecated Use {@link WifiManager#setScanThrottleEnabled(boolean)} for setting the value * and {@link WifiManager#isScanThrottleEnabled()} for query. */ + @Deprecated + @Readable public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled"; /** @@ -10928,24 +11671,28 @@ public final class Settings { * connectivity. * @hide */ + @Readable public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled"; /** * The length in milliseconds of a BLE scan window in a low-power scan mode. * @hide */ + @Readable public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms"; /** * The length in milliseconds of a BLE scan window in a balanced scan mode. * @hide */ + @Readable public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms"; /** * The length in milliseconds of a BLE scan window in a low-latency scan mode. * @hide */ + @Readable public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS = "ble_scan_low_latency_window_ms"; @@ -10953,6 +11700,7 @@ public final class Settings { * The length in milliseconds of a BLE scan interval in a low-power scan mode. * @hide */ + @Readable public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS = "ble_scan_low_power_interval_ms"; @@ -10960,6 +11708,7 @@ public final class Settings { * The length in milliseconds of a BLE scan interval in a balanced scan mode. * @hide */ + @Readable public static final String BLE_SCAN_BALANCED_INTERVAL_MS = "ble_scan_balanced_interval_ms"; @@ -10967,6 +11716,7 @@ public final class Settings { * The length in milliseconds of a BLE scan interval in a low-latency scan mode. * @hide */ + @Readable public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS = "ble_scan_low_latency_interval_ms"; @@ -10974,26 +11724,30 @@ public final class Settings { * The mode that BLE scanning clients will be moved to when in the background. * @hide */ + @Readable public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode"; - /** + /** * The interval in milliseconds to scan as used by the wifi supplicant * @hide */ - public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS = + @Readable + public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS = "wifi_supplicant_scan_interval_ms"; /** * whether frameworks handles wifi auto-join * @hide */ - public static final String WIFI_ENHANCED_AUTO_JOIN = + @Readable + public static final String WIFI_ENHANCED_AUTO_JOIN = "wifi_enhanced_auto_join"; /** * whether settings show RSSI * @hide */ + @Readable public static final String WIFI_NETWORK_SHOW_RSSI = "wifi_network_show_rssi"; @@ -11001,31 +11755,36 @@ public final class Settings { * The interval in milliseconds to scan at supplicant when p2p is connected * @hide */ - public static final String WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS = + @Readable + public static final String WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS = "wifi_scan_interval_p2p_connected_ms"; - /** + /** * Whether the Wi-Fi watchdog is enabled. */ - public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on"; + @Readable + public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on"; - /** + /** * Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and * the setting needs to be set to 0 to disable it. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED = + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable + public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED = "wifi_watchdog_poor_network_test_enabled"; - /** + /** * Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1 * will enable it. In the future, additional values may be supported. * @hide * @deprecated Use {@link WifiManager#setVerboseLoggingEnabled(boolean)} for setting the * value and {@link WifiManager#isVerboseLoggingEnabled()} for query. */ - public static final String WIFI_VERBOSE_LOGGING_ENABLED = + @Deprecated + @Readable + public static final String WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled"; /** @@ -11035,6 +11794,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED = "wifi_connected_mac_randomization_enabled"; @@ -11051,24 +11811,28 @@ public final class Settings { * @hide * @deprecated This is no longer used or set by the platform. */ + @Deprecated + @Readable public static final String WIFI_SCORE_PARAMS = "wifi_score_params"; - /** + /** * The maximum number of times we will retry a connection to an access * point for which we have failed in acquiring an IP address from DHCP. * A value of N means that we will make N+1 connection attempts in all. */ - public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count"; + @Readable + public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count"; - /** + /** * Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile * data connectivity to be established after a disconnect from Wi-Fi. */ - public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = + @Readable + public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms"; - /** + /** * This setting controls whether WiFi configurations created by a Device Owner app * should be locked down (that is, be editable or removable only by the Device Owner App, * not even by Settings app). @@ -11076,10 +11840,11 @@ public final class Settings { * are locked down. Value of zero means they are not. Default value in the absence of * actual value to this setting is 0. */ - public static final String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN = + @Readable + public static final String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN = "wifi_device_owner_configs_lockdown"; - /** + /** * The operational wifi frequency band * Set to one of {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO}, * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ} or @@ -11087,18 +11852,21 @@ public final class Settings { * * @hide */ - public static final String WIFI_FREQUENCY_BAND = "wifi_frequency_band"; + @Readable + public static final String WIFI_FREQUENCY_BAND = "wifi_frequency_band"; - /** + /** * The Wi-Fi peer-to-peer device name * @hide * @deprecated Use {@link WifiP2pManager#setDeviceName(WifiP2pManager.Channel, String, * WifiP2pManager.ActionListener)} for setting the value and * {@link android.net.wifi.p2p.WifiP2pDevice#deviceName} for query. */ - public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name"; + @Deprecated + @Readable + public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name"; - /** + /** * Timeout for ephemeral networks when all known BSSIDs go out of range. We will disconnect * from an ephemeral network if there is no BSSID for that network with a non-null score that * has been seen in this time period. @@ -11107,53 +11875,60 @@ public final class Settings { * for a non-null score from the currently connected or target BSSID. * @hide */ - public static final String WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS = + @Readable + public static final String WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS = "wifi_ephemeral_out_of_range_timeout_ms"; - /** + /** * The number of milliseconds to delay when checking for data stalls during * non-aggressive detection. (screen is turned off.) * @hide */ - public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS = + @Readable + public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS = "data_stall_alarm_non_aggressive_delay_in_ms"; - /** + /** * The number of milliseconds to delay when checking for data stalls during * aggressive detection. (screen on or suspected data stall) * @hide */ - public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS = + @Readable + public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS = "data_stall_alarm_aggressive_delay_in_ms"; - /** + /** * The number of milliseconds to allow the provisioning apn to remain active * @hide */ - public static final String PROVISIONING_APN_ALARM_DELAY_IN_MS = + @Readable + public static final String PROVISIONING_APN_ALARM_DELAY_IN_MS = "provisioning_apn_alarm_delay_in_ms"; - /** + /** * The interval in milliseconds at which to check gprs registration * after the first registration mismatch of gprs and voice service, * to detect possible data network registration problems. * * @hide */ - public static final String GPRS_REGISTER_CHECK_PERIOD_MS = + @Readable + public static final String GPRS_REGISTER_CHECK_PERIOD_MS = "gprs_register_check_period_ms"; - /** + /** * Nonzero causes Log.wtf() to crash. * @hide */ - public static final String WTF_IS_FATAL = "wtf_is_fatal"; + @Readable + public static final String WTF_IS_FATAL = "wtf_is_fatal"; - /** + /** * Ringer mode. This is used internally, changing this value will not * change the ringer mode. See AudioManager. */ - public static final String MODE_RINGER = "mode_ringer"; + @Readable + public static final String MODE_RINGER = "mode_ringer"; /** * Overlay display devices setting. @@ -11194,6 +11969,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices"; /** @@ -11202,10 +11978,12 @@ public final class Settings { * * @hide */ + @Readable public static final String BATTERY_DISCHARGE_DURATION_THRESHOLD = "battery_discharge_duration_threshold"; /** @hide */ + @Readable public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold"; /** @@ -11217,6 +11995,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SEND_ACTION_APP_ERROR = "send_action_app_error"; /** @@ -11224,6 +12003,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DROPBOX_AGE_SECONDS = "dropbox_age_seconds"; /** @@ -11232,6 +12012,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DROPBOX_MAX_FILES = "dropbox_max_files"; /** @@ -11240,6 +12021,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DROPBOX_QUOTA_KB = "dropbox_quota_kb"; /** @@ -11248,6 +12030,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DROPBOX_QUOTA_PERCENT = "dropbox_quota_percent"; /** @@ -11256,6 +12039,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DROPBOX_RESERVE_PERCENT = "dropbox_reserve_percent"; /** @@ -11263,6 +12047,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DROPBOX_TAG_PREFIX = "dropbox:"; /** @@ -11273,6 +12058,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ERROR_LOGCAT_PREFIX = "logcat_for_"; /** @@ -11286,6 +12072,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MAX_ERROR_BYTES_PREFIX = "max_error_bytes_for_"; /** @@ -11294,6 +12081,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SYS_FREE_STORAGE_LOG_INTERVAL = "sys_free_storage_log_interval"; /** @@ -11303,6 +12091,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DISK_FREE_CHANGE_REPORTING_THRESHOLD = "disk_free_change_reporting_threshold"; @@ -11315,6 +12104,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage"; @@ -11326,6 +12116,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes"; @@ -11336,6 +12127,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes"; @@ -11345,6 +12137,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage"; @@ -11354,6 +12147,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes"; @@ -11363,6 +12157,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SYNC_MAX_RETRY_DELAY_IN_SECONDS = "sync_max_retry_delay_in_seconds"; @@ -11372,6 +12167,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay"; @@ -11381,7 +12177,7 @@ public final class Settings { * * @hide */ - + @Readable public static final String CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS = "connectivity_sampling_interval_in_seconds"; @@ -11391,6 +12187,7 @@ public final class Settings { * * @hide */ + @Readable public static final String PAC_CHANGE_DELAY = "pac_change_delay"; /** @@ -11423,6 +12220,7 @@ public final class Settings { * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. * @hide */ + @Readable public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; /** @@ -11433,6 +12231,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled"; @@ -11443,6 +12242,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAPTIVE_PORTAL_SERVER = "captive_portal_server"; /** @@ -11451,6 +12251,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; /** @@ -11459,6 +12260,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; /** @@ -11467,6 +12269,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; /** @@ -11475,6 +12278,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = "captive_portal_other_fallback_urls"; @@ -11484,6 +12288,7 @@ public final class Settings { * by "@@,@@". * @hide */ + @Readable public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs"; @@ -11494,6 +12299,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; /** @@ -11502,6 +12308,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; /** @@ -11509,6 +12316,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DATA_STALL_RECOVERY_ON_BAD_NETWORK = "data_stall_recovery_on_bad_network"; @@ -11517,6 +12325,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS = "min_duration_between_recovery_steps"; /** @@ -11524,6 +12333,7 @@ public final class Settings { * * @hide */ + @Readable public static final String NSD_ON = "nsd_on"; /** @@ -11531,6 +12341,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SET_INSTALL_LOCATION = "set_install_location"; /** @@ -11540,6 +12351,7 @@ public final class Settings { * 2 = sdcard * @hide */ + @Readable public static final String DEFAULT_INSTALL_LOCATION = "default_install_location"; /** @@ -11548,6 +12360,7 @@ public final class Settings { * * @hide */ + @Readable public static final String INET_CONDITION_DEBOUNCE_UP_DELAY = "inet_condition_debounce_up_delay"; @@ -11557,10 +12370,12 @@ public final class Settings { * * @hide */ + @Readable public static final String INET_CONDITION_DEBOUNCE_DOWN_DELAY = "inet_condition_debounce_down_delay"; /** {@hide} */ + @Readable public static final String READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT = "read_external_storage_enforced_default"; @@ -11568,6 +12383,7 @@ public final class Settings { * Host name and port for global http proxy. Uses ':' seperator for * between host and port. */ + @Readable public static final String HTTP_PROXY = "http_proxy"; /** @@ -11575,6 +12391,7 @@ public final class Settings { * * @hide */ + @Readable public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host"; /** @@ -11582,6 +12399,7 @@ public final class Settings { * * @hide */ + @Readable public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port"; /** @@ -11593,6 +12411,7 @@ public final class Settings { * * @hide */ + @Readable public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list"; @@ -11600,6 +12419,7 @@ public final class Settings { * The location PAC File for the proxy. * @hide */ + @Readable public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url"; @@ -11609,6 +12429,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SET_GLOBAL_HTTP_PROXY = "set_global_http_proxy"; /** @@ -11616,6 +12437,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DEFAULT_DNS_SERVER = "default_dns_server"; /** @@ -11628,11 +12450,13 @@ public final class Settings { * * @hide */ + @Readable public static final String PRIVATE_DNS_MODE = "private_dns_mode"; /** * @hide */ + @Readable public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier"; /** @@ -11644,46 +12468,60 @@ public final class Settings { * * {@hide} */ + @Readable public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode"; /** {@hide} */ + @Readable public static final String BLUETOOTH_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode"; /** {@hide} */ + @Readable public static final String BLUETOOTH_HEADSET_PRIORITY_PREFIX = "bluetooth_headset_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX = "bluetooth_a2dp_sink_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX = "bluetooth_a2dp_supports_optional_codecs_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX = "bluetooth_a2dp_optional_codecs_enabled_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_"; /** {@hide} */ + @Readable public static final String BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_"; @@ -11692,6 +12530,7 @@ public final class Settings { * * {@hide} */ + @Readable public static final String ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection"; @@ -11700,6 +12539,7 @@ public final class Settings { * * {@hide} */ + @Readable public static final String RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD = "radio_bug_wakelock_timeout_count_threshold"; @@ -11709,6 +12549,7 @@ public final class Settings { * * {@hide} */ + @Readable public static final String RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD = "radio_bug_system_error_count_threshold"; @@ -11755,6 +12596,7 @@ public final class Settings { * @hide * @see com.android.server.am.ActivityManagerConstants */ + @Readable public static final String ACTIVITY_MANAGER_CONSTANTS = "activity_manager_constants"; /** @@ -11763,6 +12605,7 @@ public final class Settings { * Default: 1 * @hide */ + @Readable public static final String ACTIVITY_STARTS_LOGGING_ENABLED = "activity_starts_logging_enabled"; @@ -11772,6 +12615,7 @@ public final class Settings { * Default: 1 * @hide */ + @Readable public static final String FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED = "foreground_service_starts_logging_enabled"; @@ -11779,6 +12623,7 @@ public final class Settings { * @hide * @see com.android.server.appbinding.AppBindingConstants */ + @Readable public static final String APP_BINDING_CONSTANTS = "app_binding_constants"; /** @@ -11801,6 +12646,7 @@ public final class Settings { * @see com.android.server.AppOpsService.Constants */ @TestApi + @Readable public static final String APP_OPS_CONSTANTS = "app_ops_constants"; /** @@ -11836,6 +12682,7 @@ public final class Settings { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @Readable public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants"; /** @@ -11853,6 +12700,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS = "battery_saver_device_specific_constants"; @@ -11882,6 +12730,7 @@ public final class Settings { * </pre> * @hide */ + @Readable public static final String BATTERY_TIP_CONSTANTS = "battery_tip_constants"; /** @@ -11907,6 +12756,7 @@ public final class Settings { * </pre> * @hide */ + @Readable public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants"; /** @@ -11914,6 +12764,7 @@ public final class Settings { * current version is 1. * @hide */ + @Readable public static final String ANOMALY_CONFIG_VERSION = "anomaly_config_version"; /** @@ -11921,6 +12772,7 @@ public final class Settings { * {@link android.app.StatsManager}. * @hide */ + @Readable public static final String ANOMALY_CONFIG = "anomaly_config"; /** @@ -11940,6 +12792,7 @@ public final class Settings { * </pre> * @hide */ + @Readable public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants"; /** @@ -11950,6 +12803,7 @@ public final class Settings { * Any other value defaults to enabled. * @hide */ + @Readable public static final String SYS_UIDCPUPOWER = "sys_uidcpupower"; /** @@ -11961,6 +12815,7 @@ public final class Settings { * Any other value defaults to disabled. * @hide */ + @Readable public static final String SYS_TRACED = "sys_traced"; /** @@ -11969,6 +12824,7 @@ public final class Settings { * * @hide */ + @Readable public static final String FPS_DEVISOR = "fps_divisor"; /** @@ -11978,6 +12834,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DISPLAY_PANEL_LPM = "display_panel_lpm"; /** @@ -11992,6 +12849,7 @@ public final class Settings { * Need to reboot the device for this setting to take effect. * @hide */ + @Readable public static final String APP_TIME_LIMIT_USAGE_SOURCE = "app_time_limit_usage_source"; /** @@ -11999,6 +12857,7 @@ public final class Settings { * 0 = disable, 1 = enable. * @hide */ + @Readable public static final String ART_VERIFIER_VERIFY_DEBUGGABLE = "art_verifier_verify_debuggable"; @@ -12019,6 +12878,7 @@ public final class Settings { * @hide * @see com.android.server.power.PowerManagerConstants */ + @Readable public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants"; /** @@ -12044,6 +12904,7 @@ public final class Settings { * @hide * @see com.android.server.pm.ShortcutService.ConfigConstants */ + @Readable public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants"; /** @@ -12061,6 +12922,7 @@ public final class Settings { * @hide * see also com.android.server.devicepolicy.DevicePolicyConstants */ + @Readable public static final String DEVICE_POLICY_CONSTANTS = "device_policy_constants"; /** @@ -12097,6 +12959,7 @@ public final class Settings { * @hide * see also android.view.textclassifier.TextClassificationConstants */ + @Readable public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants"; /** @@ -12121,6 +12984,7 @@ public final class Settings { * @hide * see also com.android.internal.os.BatteryStatsImpl.Constants */ + @Readable public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants"; /** @@ -12131,6 +12995,7 @@ public final class Settings { * @hide * @see com.android.server.content.SyncManagerConstants */ + @Readable public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants"; /** @@ -12150,6 +13015,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BROADCAST_FG_CONSTANTS = "bcast_fg_constants"; /** @@ -12160,6 +13026,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BROADCAST_BG_CONSTANTS = "bcast_bg_constants"; /** @@ -12170,6 +13037,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BROADCAST_OFFLOAD_CONSTANTS = "bcast_offload_constants"; /** @@ -12182,6 +13050,7 @@ public final class Settings { * @see #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED */ @SystemApi + @Readable public static final String APP_STANDBY_ENABLED = "app_standby_enabled"; /** @@ -12192,6 +13061,7 @@ public final class Settings { * @hide * @see #APP_STANDBY_ENABLED */ + @Readable public static final String ADAPTIVE_BATTERY_MANAGEMENT_ENABLED = "adaptive_battery_management_enabled"; @@ -12203,6 +13073,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket"; /** @@ -12220,6 +13091,7 @@ public final class Settings { * * @hide */ + @Readable public static final String APP_AUTO_RESTRICTION_ENABLED = "app_auto_restriction_enabled"; @@ -12229,6 +13101,7 @@ public final class Settings { * Default: 1 * @hide */ + @Readable public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled"; /** @@ -12237,6 +13110,7 @@ public final class Settings { * Default: 0 * @hide */ + @Readable public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED = "forced_app_standby_for_small_battery_enabled"; @@ -12246,6 +13120,7 @@ public final class Settings { * Default: 0 * @hide */ + @Readable public static final String USER_ABSENT_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED = "user_absent_radios_off_for_small_battery_enabled"; @@ -12255,6 +13130,7 @@ public final class Settings { * Default: 0 * @hide */ + @Readable public static final String USER_ABSENT_TOUCH_OFF_FOR_SMALL_BATTERY_ENABLED = "user_absent_touch_off_for_small_battery_enabled"; @@ -12264,6 +13140,7 @@ public final class Settings { * Default: 1 * @hide */ + @Readable public static final String WIFI_ON_WHEN_PROXY_DISCONNECTED = "wifi_on_when_proxy_disconnected"; @@ -12282,6 +13159,7 @@ public final class Settings { * Type: string * @hide */ + @Readable public static final String TIME_ONLY_MODE_CONSTANTS = "time_only_mode_constants"; @@ -12292,6 +13170,7 @@ public final class Settings { * Default: 0 * @hide */ + @Readable public static final String UNGAZE_SLEEP_ENABLED = "ungaze_sleep_enabled"; /** @@ -12300,6 +13179,7 @@ public final class Settings { * Default: 0 * @hide */ + @Readable public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled"; /** @@ -12308,6 +13188,7 @@ public final class Settings { * Default: 1 * @hide */ + @Readable public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED = "show_hidden_icon_apps_enabled"; @@ -12317,6 +13198,7 @@ public final class Settings { * Default: 0 * @hide */ + @Readable public static final String SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED = "show_new_app_installed_notification_enabled"; @@ -12330,6 +13212,7 @@ public final class Settings { * * @hide */ + @Readable public static final String KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background"; /** @@ -12351,6 +13234,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ADB_ALLOWED_CONNECTION_TIME = "adb_allowed_connection_time"; @@ -12358,12 +13242,14 @@ public final class Settings { * Scaling factor for normal window animations. Setting to 0 will * disable window animations. */ + @Readable public static final String WINDOW_ANIMATION_SCALE = "window_animation_scale"; /** * Scaling factor for activity transition animations. Setting to 0 will * disable window animations. */ + @Readable public static final String TRANSITION_ANIMATION_SCALE = "transition_animation_scale"; /** @@ -12371,6 +13257,7 @@ public final class Settings { * start delay and duration of all such animations. Setting to 0 will * cause animations to end immediately. The default value is 1. */ + @Readable public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale"; /** @@ -12379,6 +13266,7 @@ public final class Settings { * * @hide */ + @Readable public static final String FANCY_IME_ANIMATIONS = "fancy_ime_animations"; /** @@ -12387,6 +13275,7 @@ public final class Settings { * TODO: remove this settings before code freeze (bug/1907571) * @hide */ + @Readable public static final String COMPATIBILITY_MODE = "compatibility_mode"; /** @@ -12396,6 +13285,7 @@ public final class Settings { * 2 = Vibrate * @hide */ + @Readable public static final String EMERGENCY_TONE = "emergency_tone"; /** @@ -12404,6 +13294,7 @@ public final class Settings { * boolean (1 or 0). * @hide */ + @Readable public static final String CALL_AUTO_RETRY = "call_auto_retry"; /** @@ -12411,6 +13302,7 @@ public final class Settings { * The value is a boolean (1 or 0). * @hide */ + @Readable public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed"; /** @@ -12420,6 +13312,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS = "enable_automatic_system_server_heap_dumps"; @@ -12428,18 +13321,21 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String PREFERRED_NETWORK_MODE = "preferred_network_mode"; /** * Name of an application package to be debugged. */ + @Readable public static final String DEBUG_APP = "debug_app"; /** * If 1, when launching DEBUG_APP it will wait for the debugger before * starting user code. If 0, it will run normally. */ + @Readable public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger"; /** @@ -12448,12 +13344,14 @@ public final class Settings { * 1 = yes * @hide */ + @Readable public static final String ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers"; /** * App allowed to load GPU debug layers * @hide */ + @Readable public static final String GPU_DEBUG_APP = "gpu_debug_app"; /** @@ -12461,6 +13359,7 @@ public final class Settings { * to dumpable apps that opt-in. * @hide */ + @Readable public static final String ANGLE_DEBUG_PACKAGE = "angle_debug_package"; /** @@ -12468,12 +13367,14 @@ public final class Settings { * The value is a boolean (1 or 0). * @hide */ + @Readable public static final String ANGLE_GL_DRIVER_ALL_ANGLE = "angle_gl_driver_all_angle"; /** * List of PKGs that have an OpenGL driver selected * @hide */ + @Readable public static final String ANGLE_GL_DRIVER_SELECTION_PKGS = "angle_gl_driver_selection_pkgs"; @@ -12481,6 +13382,7 @@ public final class Settings { * List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS * @hide */ + @Readable public static final String ANGLE_GL_DRIVER_SELECTION_VALUES = "angle_gl_driver_selection_values"; @@ -12488,6 +13390,7 @@ public final class Settings { * List of package names that should check ANGLE rules * @hide */ + @Readable public static final String ANGLE_ALLOWLIST = "angle_allowlist"; /** @@ -12497,6 +13400,7 @@ public final class Settings { * e.g. feature1:feature2:feature3,feature1:feature3:feature5 * @hide */ + @Readable public static final String ANGLE_EGL_FEATURES = "angle_egl_features"; /** @@ -12504,6 +13408,7 @@ public final class Settings { * The value is a boolean (1 or 0). * @hide */ + @Readable public static final String SHOW_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box"; /** @@ -12514,6 +13419,7 @@ public final class Settings { * 3 = All Apps use system graphics driver * @hide */ + @Readable public static final String UPDATABLE_DRIVER_ALL_APPS = "updatable_driver_all_apps"; /** @@ -12521,6 +13427,7 @@ public final class Settings { * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ + @Readable public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS = "updatable_driver_production_opt_in_apps"; @@ -12529,6 +13436,7 @@ public final class Settings { * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ + @Readable public static final String UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS = "updatable_driver_prerelease_opt_in_apps"; @@ -12537,6 +13445,7 @@ public final class Settings { * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ + @Readable public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS = "updatable_driver_production_opt_out_apps"; @@ -12544,6 +13453,7 @@ public final class Settings { * Apps on the denylist that are forbidden to use updatable production driver. * @hide */ + @Readable public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLIST = "updatable_driver_production_denylist"; @@ -12552,6 +13462,7 @@ public final class Settings { * updatable production driver. * @hide */ + @Readable public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLISTS = "updatable_driver_production_denylists"; @@ -12561,6 +13472,7 @@ public final class Settings { * i.e. <apk1>,<apk2>,...,<apkN> * @hide */ + @Readable public static final String UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST = "updatable_driver_production_allowlist"; @@ -12570,6 +13482,7 @@ public final class Settings { * i.e. <lib1>:<lib2>:...:<libN> * @hide */ + @Readable public static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES = "updatable_driver_sphal_libraries"; @@ -12578,6 +13491,7 @@ public final class Settings { * i.e. <layer1>:<layer2>:...:<layerN> * @hide */ + @Readable public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers"; /** @@ -12585,12 +13499,14 @@ public final class Settings { * i.e. <layer1>:<layer2>:...:<layerN> * @hide */ + @Readable public static final String GPU_DEBUG_LAYERS_GLES = "gpu_debug_layers_gles"; /** * Addition app for GPU layer discovery * @hide */ + @Readable public static final String GPU_DEBUG_LAYER_APP = "gpu_debug_layer_app"; /** @@ -12600,6 +13516,7 @@ public final class Settings { * {@link android.os.Build.VERSION_CODES#N_MR1}. */ @Deprecated + @Readable public static final String SHOW_PROCESSES = "show_processes"; /** @@ -12607,6 +13524,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String LOW_POWER_MODE = "low_power"; /** @@ -12615,6 +13533,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; /** @@ -12624,6 +13543,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL = "low_power_sticky_auto_disable_level"; @@ -12633,6 +13553,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED = "low_power_sticky_auto_disable_enabled"; @@ -12646,6 +13567,7 @@ public final class Settings { * @see android.os.PowerManager#getPowerSaveModeTrigger() * @hide */ + @Readable public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level"; /** @@ -12656,6 +13578,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode"; /** @@ -12666,6 +13589,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold"; @@ -12676,6 +13600,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled"; /** @@ -12687,6 +13612,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String TIME_REMAINING_ESTIMATE_MILLIS = "time_remaining_estimate_millis"; @@ -12700,6 +13626,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String TIME_REMAINING_ESTIMATE_BASED_ON_USAGE = "time_remaining_estimate_based_on_usage"; @@ -12712,6 +13639,7 @@ public final class Settings { * @hide */ @Deprecated + @Readable public static final String AVERAGE_TIME_TO_DISCHARGE = "average_time_to_discharge"; /** @@ -12723,6 +13651,7 @@ public final class Settings { * @deprecated No longer needed due to {@link PowerManager#getBatteryDischargePrediction}. */ @Deprecated + @Readable public static final String BATTERY_ESTIMATES_LAST_UPDATE_TIME = "battery_estimates_last_update_time"; @@ -12732,12 +13661,14 @@ public final class Settings { * * @hide */ + @Readable public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max"; /** * See com.android.settingslib.fuelgauge.BatterySaverUtils. * @hide */ + @Readable public static final String LOW_POWER_MODE_SUGGESTION_PARAMS = "low_power_mode_suggestion_params"; @@ -12746,6 +13677,7 @@ public final class Settings { * processes as soon as they are no longer needed. If 0, the normal * extended lifetime is used. */ + @Readable public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities"; /** @@ -12755,6 +13687,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs"; /** @@ -12763,6 +13696,7 @@ public final class Settings { * 1 = enabled * @hide */ + @Readable public static final String DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled"; /** @@ -12822,6 +13756,7 @@ public final class Settings { * ENCODED_SURROUND_OUTPUT_MANUAL * @hide */ + @Readable public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output"; /** @@ -12833,6 +13768,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS = "encoded_surround_output_enabled_formats"; @@ -12840,36 +13776,42 @@ public final class Settings { * Persisted safe headphone volume management state by AudioService * @hide */ + @Readable public static final String AUDIO_SAFE_VOLUME_STATE = "audio_safe_volume_state"; /** * URL for tzinfo (time zone) updates * @hide */ + @Readable public static final String TZINFO_UPDATE_CONTENT_URL = "tzinfo_content_url"; /** * URL for tzinfo (time zone) update metadata * @hide */ + @Readable public static final String TZINFO_UPDATE_METADATA_URL = "tzinfo_metadata_url"; /** * URL for selinux (mandatory access control) updates * @hide */ + @Readable public static final String SELINUX_UPDATE_CONTENT_URL = "selinux_content_url"; /** * URL for selinux (mandatory access control) update metadata * @hide */ + @Readable public static final String SELINUX_UPDATE_METADATA_URL = "selinux_metadata_url"; /** * URL for sms short code updates * @hide */ + @Readable public static final String SMS_SHORT_CODES_UPDATE_CONTENT_URL = "sms_short_codes_content_url"; @@ -12877,6 +13819,7 @@ public final class Settings { * URL for sms short code update metadata * @hide */ + @Readable public static final String SMS_SHORT_CODES_UPDATE_METADATA_URL = "sms_short_codes_metadata_url"; @@ -12884,30 +13827,35 @@ public final class Settings { * URL for apn_db updates * @hide */ + @Readable public static final String APN_DB_UPDATE_CONTENT_URL = "apn_db_content_url"; /** * URL for apn_db update metadata * @hide */ + @Readable public static final String APN_DB_UPDATE_METADATA_URL = "apn_db_metadata_url"; /** * URL for cert pinlist updates * @hide */ + @Readable public static final String CERT_PIN_UPDATE_CONTENT_URL = "cert_pin_content_url"; /** * URL for cert pinlist updates * @hide */ + @Readable public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url"; /** * URL for intent firewall updates * @hide */ + @Readable public static final String INTENT_FIREWALL_UPDATE_CONTENT_URL = "intent_firewall_content_url"; @@ -12915,6 +13863,7 @@ public final class Settings { * URL for intent firewall update metadata * @hide */ + @Readable public static final String INTENT_FIREWALL_UPDATE_METADATA_URL = "intent_firewall_metadata_url"; @@ -12922,18 +13871,21 @@ public final class Settings { * URL for lang id model updates * @hide */ + @Readable public static final String LANG_ID_UPDATE_CONTENT_URL = "lang_id_content_url"; /** * URL for lang id model update metadata * @hide */ + @Readable public static final String LANG_ID_UPDATE_METADATA_URL = "lang_id_metadata_url"; /** * URL for smart selection model updates * @hide */ + @Readable public static final String SMART_SELECTION_UPDATE_CONTENT_URL = "smart_selection_content_url"; @@ -12941,6 +13893,7 @@ public final class Settings { * URL for smart selection model update metadata * @hide */ + @Readable public static final String SMART_SELECTION_UPDATE_METADATA_URL = "smart_selection_metadata_url"; @@ -12948,6 +13901,7 @@ public final class Settings { * URL for conversation actions model updates * @hide */ + @Readable public static final String CONVERSATION_ACTIONS_UPDATE_CONTENT_URL = "conversation_actions_content_url"; @@ -12955,6 +13909,7 @@ public final class Settings { * URL for conversation actions model update metadata * @hide */ + @Readable public static final String CONVERSATION_ACTIONS_UPDATE_METADATA_URL = "conversation_actions_metadata_url"; @@ -12962,12 +13917,14 @@ public final class Settings { * SELinux enforcement status. If 0, permissive; if 1, enforcing. * @hide */ + @Readable public static final String SELINUX_STATUS = "selinux_status"; /** * Developer setting to force RTL layout. * @hide */ + @Readable public static final String DEVELOPMENT_FORCE_RTL = "debug.force_rtl"; /** @@ -12978,6 +13935,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOW_BATTERY_SOUND_TIMEOUT = "low_battery_sound_timeout"; /** @@ -12987,6 +13945,7 @@ public final class Settings { * * @hide */ + @Readable public static final String WIFI_BOUNCE_DELAY_OVERRIDE_MS = "wifi_bounce_delay_override_ms"; /** @@ -12996,6 +13955,7 @@ public final class Settings { * * @hide */ + @Readable public static final String POLICY_CONTROL = "policy_control"; /** @@ -13003,6 +13963,7 @@ public final class Settings { * * @hide */ + @Readable public static final String EMULATE_DISPLAY_CUTOUT = "emulate_display_cutout"; /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_OFF = 0; @@ -13013,6 +13974,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BLOCKED_SLICES = "blocked_slices"; /** @@ -13022,6 +13984,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String ZEN_MODE = "zen_mode"; /** @hide */ @@ -13061,6 +14024,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ZEN_MODE_RINGER_LEVEL = "zen_mode_ringer_level"; /** @@ -13069,6 +14033,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag"; /** @@ -13098,6 +14063,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Readable public static final String HEADS_UP_NOTIFICATIONS_ENABLED = "heads_up_notifications_enabled"; @@ -13111,6 +14077,7 @@ public final class Settings { /** * The name of the device */ + @Readable public static final String DEVICE_NAME = "device_name"; /** @@ -13119,6 +14086,7 @@ public final class Settings { * Type: int (0 for false, 1 for true) * @hide */ + @Readable public static final String NETWORK_SCORING_PROVISIONED = "network_scoring_provisioned"; /** @@ -13130,6 +14098,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt"; /** @@ -13143,6 +14112,7 @@ public final class Settings { * {@link android.provider.Telephony.SimInfo#COLUMN_ENHANCED_4G_MODE_ENABLED} instead. */ @Deprecated + @Readable public static final String ENHANCED_4G_MODE_ENABLED = Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED; @@ -13155,6 +14125,7 @@ public final class Settings { * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_VT_IMS_ENABLED} instead. */ @Deprecated + @Readable public static final String VT_IMS_ENABLED = Telephony.SimInfo.COLUMN_VT_IMS_ENABLED; /** @@ -13167,6 +14138,7 @@ public final class Settings { * {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_ENABLED} instead. */ @Deprecated + @Readable public static final String WFC_IMS_ENABLED = Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED; /** @@ -13178,6 +14150,7 @@ public final class Settings { * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_MODE} instead. */ @Deprecated + @Readable public static final String WFC_IMS_MODE = Telephony.SimInfo.COLUMN_WFC_IMS_MODE; /** @@ -13190,6 +14163,7 @@ public final class Settings { * instead. */ @Deprecated + @Readable public static final String WFC_IMS_ROAMING_MODE = Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE; @@ -13203,6 +14177,7 @@ public final class Settings { * instead */ @Deprecated + @Readable public static final String WFC_IMS_ROAMING_ENABLED = Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED; @@ -13213,6 +14188,7 @@ public final class Settings { * Type: int (0 for false, 1 for true) * @hide */ + @Readable public static final String LTE_SERVICE_FORCED = "lte_service_forced"; @@ -13222,6 +14198,7 @@ public final class Settings { * See WindowManagerPolicy.WindowManagerFuncs * @hide */ + @Readable public static final String LID_BEHAVIOR = "lid_behavior"; /** @@ -13230,6 +14207,7 @@ public final class Settings { * Type: int * @hide */ + @Readable public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES = "ephemeral_cookie_max_size_bytes"; @@ -13241,6 +14219,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature"; /** @@ -13251,6 +14230,7 @@ public final class Settings { * * @hide */ + @Readable public static final String INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled"; /** @@ -13259,6 +14239,7 @@ public final class Settings { * Type: long * @hide */ + @Readable public static final String INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD = "installed_instant_app_min_cache_period"; @@ -13268,6 +14249,7 @@ public final class Settings { * Type: long * @hide */ + @Readable public static final String INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD = "installed_instant_app_max_cache_period"; @@ -13277,6 +14259,7 @@ public final class Settings { * Type: long * @hide */ + @Readable public static final String UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD = "uninstalled_instant_app_min_cache_period"; @@ -13286,6 +14269,7 @@ public final class Settings { * Type: long * @hide */ + @Readable public static final String UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD = "uninstalled_instant_app_max_cache_period"; @@ -13295,6 +14279,7 @@ public final class Settings { * Type: long * @hide */ + @Readable public static final String UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD = "unused_static_shared_lib_min_cache_period"; @@ -13304,6 +14289,7 @@ public final class Settings { * Type: int * @hide */ + @Readable public static final String ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED = "allow_user_switching_when_system_user_locked"; @@ -13312,6 +14298,7 @@ public final class Settings { * <p> * Type: int */ + @Readable public static final String BOOT_COUNT = "boot_count"; /** @@ -13322,6 +14309,7 @@ public final class Settings { * before the user restrictions are loaded. * @hide */ + @Readable public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed"; /** @@ -13333,6 +14321,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String DEVICE_DEMO_MODE = "device_demo_mode"; /** @@ -13342,6 +14331,7 @@ public final class Settings { * * @hide */ + @Readable public static final String NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms"; /** @@ -13352,6 +14342,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DATABASE_DOWNGRADE_REASON = "database_downgrade_reason"; /** @@ -13362,6 +14353,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DATABASE_CREATION_BUILDID = "database_creation_buildid"; /** @@ -13370,6 +14362,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CONTACTS_DATABASE_WAL_ENABLED = "contacts_database_wal_enabled"; /** @@ -13377,6 +14370,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED = "location_settings_link_to_permissions_enabled"; @@ -13387,6 +14381,7 @@ public final class Settings { * * @hide */ + @Readable public static final String EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = "euicc_removing_invisible_profiles_timeout_millis"; @@ -13396,6 +14391,7 @@ public final class Settings { * * @hide */ + @Readable public static final String EUICC_FACTORY_RESET_TIMEOUT_MILLIS = "euicc_factory_reset_timeout_millis"; @@ -13405,6 +14401,7 @@ public final class Settings { * * @hide */ + @Readable public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS = "euicc_switch_slot_timeout_millis"; @@ -13414,6 +14411,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS = "enable_multi_slot_timeout_millis"; @@ -13423,6 +14421,7 @@ public final class Settings { * * @hide */ + @Readable public static final String STORAGE_SETTINGS_CLOBBER_THRESHOLD = "storage_settings_clobber_threshold"; @@ -13432,6 +14431,7 @@ public final class Settings { * * @hide */ + @Readable public static final String OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION = "override_settings_provider_restore_any_version"; /** @@ -13442,6 +14442,7 @@ public final class Settings { * * @hide */ + @Readable public static final String CHAINED_BATTERY_ATTRIBUTION_ENABLED = "chained_battery_attribution_enabled"; @@ -13454,6 +14455,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT = "enable_adb_incremental_install_default"; @@ -13471,6 +14473,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages"; @@ -13484,6 +14487,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOFILL_LOGGING_LEVEL = "autofill_logging_level"; /** @@ -13491,6 +14495,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOFILL_MAX_PARTITIONS_SIZE = "autofill_max_partitions_size"; /** @@ -13499,6 +14504,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets"; /** @@ -13507,6 +14513,7 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; @@ -13519,14 +14526,16 @@ public final class Settings { * @hide */ @TestApi + @Readable public static final String HIDDEN_API_POLICY = "hidden_api_policy"; - /** + /** * Flag for forcing {@link com.android.server.compat.OverrideValidatorImpl} * to consider this a non-debuggable build. * * @hide */ + @Readable public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT = "force_non_debuggable_final_build_for_compat"; @@ -13536,6 +14545,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SIGNED_CONFIG_VERSION = "signed_config_version"; /** @@ -13544,6 +14554,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT = "sound_trigger_detection_service_op_timeout"; @@ -13553,6 +14564,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY = "max_sound_trigger_detection_service_ops_per_day"; @@ -13560,6 +14572,7 @@ public final class Settings { * Indicates whether aware is available in the current location. * @hide */ + @Readable public static final String AWARE_ALLOWED = "aware_allowed"; /** @@ -13568,6 +14581,7 @@ public final class Settings { * Used by PhoneWindowManager. * @hide */ + @Readable public static final String POWER_BUTTON_LONG_PRESS = "power_button_long_press"; @@ -13577,6 +14591,7 @@ public final class Settings { * Used by PhoneWindowManager. * @hide */ + @Readable public static final String POWER_BUTTON_VERY_LONG_PRESS = "power_button_very_long_press"; @@ -13602,7 +14617,8 @@ public final class Settings { CONTENT_URI, CALL_METHOD_GET_GLOBAL, CALL_METHOD_PUT_GLOBAL, - sProviderHolder); + sProviderHolder, + Global.class); // Certain settings have been moved from global to the per-user secure namespace @UnsupportedAppUsage @@ -13631,6 +14647,11 @@ public final class Settings { sNameValueCache.clearGenerationTrackerForTest(); } + /** @hide */ + public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) { + getPublicSettingsForClass(Global.class, allKeys, readableKeys); + } + /** * Look up a name in the database. * @param resolver to access the database with @@ -14040,6 +15061,7 @@ public final class Settings { * Subscription Id to be used for voice call on a multi sim device. * @hide */ + @Readable public static final String MULTI_SIM_VOICE_CALL_SUBSCRIPTION = "multi_sim_voice_call"; /** @@ -14048,18 +15070,21 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String MULTI_SIM_VOICE_PROMPT = "multi_sim_voice_prompt"; /** * Subscription Id to be used for data call on a multi sim device. * @hide */ + @Readable public static final String MULTI_SIM_DATA_CALL_SUBSCRIPTION = "multi_sim_data_call"; /** * Subscription Id to be used for SMS on a multi sim device. * @hide */ + @Readable public static final String MULTI_SIM_SMS_SUBSCRIPTION = "multi_sim_sms"; /** @@ -14067,6 +15092,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt"; /** User preferred subscriptions setting. @@ -14076,6 +15102,7 @@ public final class Settings { * @hide */ @UnsupportedAppUsage + @Readable public static final String[] MULTI_SIM_USER_PREFERRED_SUBS = {"user_preferred_sub1", "user_preferred_sub2","user_preferred_sub3"}; @@ -14083,6 +15110,7 @@ public final class Settings { * Which subscription is enabled for a physical slot. * @hide */ + @Readable public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot"; /** @@ -14090,6 +15118,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot"; /** @@ -14097,6 +15126,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator"; /** @@ -14106,12 +15136,14 @@ public final class Settings { * @removed */ @Deprecated + @Readable public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync"; /** * Whether to enable contacts metadata syncing or not * The value 1 - enable, 0 - disable */ + @Readable public static final String CONTACT_METADATA_SYNC_ENABLED = "contact_metadata_sync_enabled"; /** @@ -14119,6 +15151,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot"; /** @@ -14127,6 +15160,7 @@ public final class Settings { * Should be a float, and includes updates only. * @hide */ + @Readable public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate"; /** @@ -14135,6 +15169,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS = "show_notification_channel_warnings"; @@ -14142,6 +15177,7 @@ public final class Settings { * Whether cell is enabled/disabled * @hide */ + @Readable public static final String CELL_ON = "cell_on"; /** @@ -14170,30 +15206,35 @@ public final class Settings { * Whether to show the high temperature warning notification. * @hide */ + @Readable public static final String SHOW_TEMPERATURE_WARNING = "show_temperature_warning"; /** * Whether to show the usb high temperature alarm notification. * @hide */ + @Readable public static final String SHOW_USB_TEMPERATURE_ALARM = "show_usb_temperature_alarm"; /** * Temperature at which the high temperature warning notification should be shown. * @hide */ + @Readable public static final String WARNING_TEMPERATURE = "warning_temperature"; /** * Whether the diskstats logging task is enabled/disabled. * @hide */ + @Readable public static final String ENABLE_DISKSTATS_LOGGING = "enable_diskstats_logging"; /** * Whether the cache quota calculation task is enabled/disabled. * @hide */ + @Readable public static final String ENABLE_CACHE_QUOTA_CALCULATION = "enable_cache_quota_calculation"; @@ -14201,6 +15242,7 @@ public final class Settings { * Whether the Deletion Helper no threshold toggle is available. * @hide */ + @Readable public static final String ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE = "enable_deletion_helper_no_threshold_toggle"; @@ -14221,6 +15263,7 @@ public final class Settings { * Options will be used in order up to the maximum allowed by the UI. * @hide */ + @Readable public static final String NOTIFICATION_SNOOZE_OPTIONS = "notification_snooze_options"; @@ -14232,6 +15275,7 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ + @Readable public static final String NOTIFICATION_FEEDBACK_ENABLED = "notification_feedback_enabled"; /** @@ -14243,6 +15287,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT = "blocking_helper_dismiss_to_view_ratio"; @@ -14254,6 +15299,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BLOCKING_HELPER_STREAK_LIMIT = "blocking_helper_streak_limit"; /** @@ -14281,6 +15327,7 @@ public final class Settings { * * @hide */ + @Readable public static final String SQLITE_COMPATIBILITY_WAL_FLAGS = "sqlite_compatibility_wal_flags"; @@ -14290,6 +15337,7 @@ public final class Settings { * 1 = yes * @hide */ + @Readable public static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING = "enable_gnss_raw_meas_full_tracking"; @@ -14301,6 +15349,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent"; @@ -14312,6 +15361,7 @@ public final class Settings { * @hide */ @SystemApi + @Readable public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis"; @@ -14321,6 +15371,7 @@ public final class Settings { * everything else is unspecified. * @hide */ + @Readable public static final String ZRAM_ENABLED = "zram_enabled"; @@ -14330,6 +15381,7 @@ public final class Settings { * "device_default" will let the system decide whether to enable the freezer or not * @hide */ + @Readable public static final String CACHED_APPS_FREEZER_ENABLED = "cached_apps_freezer"; /** @@ -14352,6 +15404,7 @@ public final class Settings { * @see com.android.systemui.statusbar.policy.SmartReplyConstants * @hide */ + @Readable public static final String SMART_REPLIES_IN_NOTIFICATIONS_FLAGS = "smart_replies_in_notifications_flags"; @@ -14368,6 +15421,7 @@ public final class Settings { * </pre> * @hide */ + @Readable public static final String SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS = "smart_suggestions_in_notifications_flags"; @@ -14377,6 +15431,7 @@ public final class Settings { * @hide */ @TestApi + @Readable @SuppressLint("NoSettingsProvider") public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog"; @@ -14384,6 +15439,7 @@ public final class Settings { * If nonzero, crash dialogs will show an option to restart the app. * @hide */ + @Readable public static final String SHOW_RESTART_IN_CRASH_DIALOG = "show_restart_in_crash_dialog"; /** @@ -14391,6 +15447,7 @@ public final class Settings { * this app. * @hide */ + @Readable public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog"; @@ -14446,6 +15503,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BACKUP_AGENT_TIMEOUT_PARAMETERS = "backup_agent_timeout_parameters"; @@ -14460,6 +15518,7 @@ public final class Settings { * * @hide */ + @Readable public static final String GNSS_SATELLITE_BLOCKLIST = "gnss_satellite_blocklist"; /** @@ -14471,6 +15530,7 @@ public final class Settings { * * @hide */ + @Readable public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS = "gnss_hal_location_request_duration_millis"; @@ -14487,6 +15547,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BINDER_CALLS_STATS = "binder_calls_stats"; /** @@ -14500,6 +15561,7 @@ public final class Settings { * * @hide */ + @Readable public static final String LOOPER_STATS = "looper_stats"; /** @@ -14514,6 +15576,7 @@ public final class Settings { * * @hide */ + @Readable public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader"; /** @@ -14521,6 +15584,7 @@ public final class Settings { * reboot. The value "1" enables native flags health check; otherwise it's disabled. * @hide */ + @Readable public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED = "native_flags_health_check_enabled"; @@ -14530,6 +15594,7 @@ public final class Settings { * * @hide */ + @Readable public static final String APPOP_HISTORY_MODE = "mode"; /** @@ -14539,6 +15604,7 @@ public final class Settings { * * @hide */ + @Readable public static final String APPOP_HISTORY_BASE_INTERVAL_MILLIS = "baseIntervalMillis"; /** @@ -14547,6 +15613,7 @@ public final class Settings { * * @hide */ + @Readable public static final String APPOP_HISTORY_INTERVAL_MULTIPLIER = "intervalMultiplier"; /** @@ -14568,6 +15635,7 @@ public final class Settings { * * @hide */ + @Readable public static final String APPOP_HISTORY_PARAMETERS = "appop_history_parameters"; @@ -14585,6 +15653,7 @@ public final class Settings { * * @hide */ + @Readable public static final String AUTO_REVOKE_PARAMETERS = "auto_revoke_parameters"; @@ -14596,6 +15665,7 @@ public final class Settings { * @see com.android.internal.os.BatteryStatsImpl.Constants.KEY_BATTERY_CHARGED_DELAY_MS * @hide */ + @Readable public static final String BATTERY_CHARGING_STATE_UPDATE_DELAY = "battery_charging_state_update_delay"; @@ -14604,6 +15674,7 @@ public final class Settings { * * @hide */ + @Readable public static final String TEXT_CLASSIFIER_ACTION_MODEL_PARAMS = "text_classifier_action_model_params"; @@ -14617,6 +15688,7 @@ public final class Settings { * * @hide */ + @Readable public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE = "power_button_suppression_delay_after_gesture_wake"; @@ -14625,6 +15697,7 @@ public final class Settings { * * @hide */ + @Readable public static final String ADVANCED_BATTERY_USAGE_AMOUNT = "advanced_battery_usage_amount"; /** @@ -14639,6 +15712,7 @@ public final class Settings { * 2: always on - All 5G NSA tracking indications are on whether the screen is on or off. * @hide */ + @Readable public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE = "nr_nsa_tracking_screen_off_mode"; @@ -14649,6 +15723,7 @@ public final class Settings { * 1: Enabled * @hide */ + @Readable public static final String SHOW_PEOPLE_SPACE = "show_people_space"; /** @@ -14659,6 +15734,7 @@ public final class Settings { * 2: All conversations * @hide */ + @Readable public static final String PEOPLE_SPACE_CONVERSATION_TYPE = "people_space_conversation_type"; @@ -14669,6 +15745,7 @@ public final class Settings { * 1: Enabled * @hide */ + @Readable public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss"; /** @@ -14683,6 +15760,7 @@ public final class Settings { * 1: Enabled (All apps will receive the new rules) * @hide */ + @Readable public static final String BACKPORT_S_NOTIF_RULES = "backport_s_notif_rules"; /** @@ -14697,6 +15775,7 @@ public final class Settings { * <p>See {@link android.app.Notification.DevFlags} for more details. * @hide */ + @Readable public static final String FULLY_CUSTOM_VIEW_NOTIF_DECORATION = "fully_custom_view_notif_decoration"; @@ -14710,6 +15789,7 @@ public final class Settings { * <p>See {@link android.app.Notification.DevFlags} for more details. * @hide */ + @Readable public static final String DECORATED_CUSTOM_VIEW_NOTIF_DECORATION = "decorated_custom_view_notif_decoration"; @@ -14726,6 +15806,7 @@ public final class Settings { * * @hide */ + @Readable public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches"; /** @@ -14751,6 +15832,7 @@ public final class Settings { * * @hide */ + @Readable public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = "maximum_obscuring_opacity_for_touch"; @@ -14763,6 +15845,7 @@ public final class Settings { * 1: enabled * @hide */ + @Readable public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode"; } @@ -14784,7 +15867,8 @@ public final class Settings { CALL_METHOD_PUT_CONFIG, CALL_METHOD_LIST_CONFIG, CALL_METHOD_SET_ALL_CONFIG, - sProviderHolder); + sProviderHolder, + Config.class); /** * Look up a name in the database. diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 990b7bdfa987..dad932c2ee19 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -123,14 +123,6 @@ interface IWindowSession { boolean outOfMemory(IWindow window); /** - * Give the window manager a hint of the part of the window that is - * completely transparent, allowing it to work with the surface flinger - * to optimize compositing of this part of the window. - */ - @UnsupportedAppUsage - oneway void setTransparentRegion(IWindow window, in Region region); - - /** * Tell the window manager about the content and visible insets of the * given window, which can be used to adjust the <var>outContentInsets</var> * and <var>outVisibleInsets</var> values returned by diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e1ccc51c71e1..1273b491d0e1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -740,6 +740,7 @@ import java.util.function.Predicate; * @attr ref android.R.styleable#View_alpha * @attr ref android.R.styleable#View_background * @attr ref android.R.styleable#View_clickable + * @attr ref android.R.styleable#View_clipToOutline * @attr ref android.R.styleable#View_contentDescription * @attr ref android.R.styleable#View_drawingCacheQuality * @attr ref android.R.styleable#View_duplicateParentState @@ -5968,6 +5969,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case R.styleable.View_scrollCaptureHint: setScrollCaptureHint((a.getInt(attr, SCROLL_CAPTURE_HINT_AUTO))); break; + case R.styleable.View_clipToOutline: + setClipToOutline(a.getBoolean(attr, false)); + break; } } @@ -17921,6 +17925,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #setOutlineProvider(ViewOutlineProvider) * @see #getClipToOutline() + * + * @attr ref android.R.styleable#View_clipToOutline */ @RemotableViewMethod public void setClipToOutline(boolean clipToOutline) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 56d98e78303e..4716141ee8d3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1675,7 +1675,8 @@ public final class ViewRootImpl implements ViewParent, requestLayout(); // See comment for View.sForceLayoutWhenInsetsChanged - if (View.sForceLayoutWhenInsetsChanged && mView != null) { + if (View.sForceLayoutWhenInsetsChanged && mView != null + && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) { forceLayout(mView); } @@ -3063,11 +3064,14 @@ public final class ViewRootImpl implements ViewParent, if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { mPreviousTransparentRegion.set(mTransparentRegion); mFullRedrawNeeded = true; - // reconfigure window manager - try { - mWindowSession.setTransparentRegion(mWindow, mTransparentRegion); - } catch (RemoteException e) { - } + // TODO: Ideally we would do this in prepareSurfaces, + // but prepareSurfaces is currently working under + // the assumption that we paused the render thread + // via the WM relayout code path. We probably eventually + // want to synchronize transparent region hint changes + // with draws. + mTransaction.setTransparentRegionHint(getSurfaceControl(), + mTransparentRegion).apply(); } } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 3aedda1a6bd3..39d3c01dd409 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -314,10 +314,6 @@ public class WindowlessWindowManager implements IWindowSession { } @Override - public void setTransparentRegion(android.view.IWindow window, android.graphics.Region region) { - } - - @Override public void setInsets(android.view.IWindow window, int touchableInsets, android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets, android.graphics.Region touchableRegion) { diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 794181e388cf..decbf8c0c59e 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -200,6 +200,34 @@ public final class AutofillManager { "android.view.autofill.extra.AUTHENTICATION_RESULT"; /** + * Intent extra: The optional boolean extra field provided by the + * {@link android.service.autofill.AutofillService} accompanying the {@link + * android.service.autofill.Dataset} result of an authentication operation. + * + * <p> Before {@link android.os.Build.VERSION_CODES#R}, if the authentication result is a + * {@link android.service.autofill.Dataset}, it'll be used to autofill the fields, and also + * replace the existing dataset in the cached {@link android.service.autofill.FillResponse}. + * That means if the user clears the field values, the autofill suggestion will show up again + * with the new authenticated Dataset. + * + * <p> In {@link android.os.Build.VERSION_CODES#R}, we added an exception to this behavior + * that if the Dataset being authenticated is a pinned dataset (see + * {@link android.service.autofill.InlinePresentation#isPinned()}), the old Dataset will not be + * replaced. + * + * <p> In {@link android.os.Build.VERSION_CODES#S}, we added this boolean extra field to + * allow the {@link android.service.autofill.AutofillService} to explicitly specify whether + * the returned authenticated Dataset is ephemeral. An ephemeral Dataset will be used to + * autofill once and then thrown away. Therefore, when the boolean extra is set to true, the + * returned Dataset will not replace the old dataset from the existing + * {@link android.service.autofill.FillResponse}. When it's set to false, it will. When it's not + * set, the old dataset will be replaced, unless it is a pinned inline suggestion, which is + * consistent with the behavior in {@link android.os.Build.VERSION_CODES#R}. + */ + public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET = + "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET"; + + /** * Intent extra: The optional extras provided by the * {@link android.service.autofill.AutofillService}. * @@ -1755,6 +1783,11 @@ public final class AutofillManager { if (newClientState != null) { responseData.putBundle(EXTRA_CLIENT_STATE, newClientState); } + if (data.getExtras().containsKey(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) { + responseData.putBoolean(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET, + data.getBooleanExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET, + false)); + } try { mService.setAuthenticationResult(responseData, mSessionId, authenticationId, mContext.getUserId()); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index dfef7ca825a1..6cb4b81827b9 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -47,6 +47,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; +import android.graphics.Outline; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -79,6 +80,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewManager; +import android.view.ViewOutlineProvider; import android.view.ViewParent; import android.view.ViewStub; import android.widget.AdapterView.OnItemClickListener; @@ -194,6 +196,7 @@ public class RemoteViews implements Parcelable, Filter { private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25; private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26; private static final int SET_RADIO_GROUP_CHECKED = 27; + private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28; /** @hide **/ @IntDef(prefix = "MARGIN_", value = { @@ -2642,6 +2645,88 @@ public class RemoteViews implements Parcelable, Filter { } } + private static class SetViewOutlinePreferredRadiusAction extends Action { + + private final boolean mIsDimen; + private final int mValue; + + SetViewOutlinePreferredRadiusAction(@IdRes int viewId, @DimenRes int dimenResId) { + this.viewId = viewId; + this.mIsDimen = true; + this.mValue = dimenResId; + } + + SetViewOutlinePreferredRadiusAction( + @IdRes int viewId, float radius, @ComplexDimensionUnit int units) { + this.viewId = viewId; + this.mIsDimen = false; + this.mValue = TypedValue.createComplexDimension(radius, units); + + } + + SetViewOutlinePreferredRadiusAction(Parcel in) { + viewId = in.readInt(); + mIsDimen = in.readBoolean(); + mValue = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(viewId); + dest.writeBoolean(mIsDimen); + dest.writeInt(mValue); + } + + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) + throws ActionException { + final View target = root.findViewById(viewId); + if (target == null) return; + + float radius; + if (mIsDimen) { + radius = mValue == 0 ? 0 : target.getResources().getDimension(mValue); + } else { + radius = TypedValue.complexToDimensionPixelSize(mValue, + target.getResources().getDisplayMetrics()); + } + target.setOutlineProvider(new RemoteViewOutlineProvider(radius)); + } + + @Override + public int getActionTag() { + return SET_VIEW_OUTLINE_RADIUS_TAG; + } + } + + /** + * OutlineProvider for a view with a radius set by + * {@link #setViewOutlinePreferredRadius(int, float, int)}. + */ + public static final class RemoteViewOutlineProvider extends ViewOutlineProvider { + + private final float mRadius; + + public RemoteViewOutlineProvider(float radius) { + mRadius = radius; + } + + /** Returns the corner radius used when providing the view outline. */ + public float getRadius() { + return mRadius; + } + + @Override + public void getOutline(@NonNull View view, @NonNull Outline outline) { + outline.setRoundRect( + 0 /*left*/, + 0 /* top */, + view.getWidth() /* right */, + view.getHeight() /* bottom */, + mRadius); + } + } + /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. @@ -2860,6 +2945,8 @@ public class RemoteViews implements Parcelable, Filter { return new SetCompoundButtonCheckedAction(parcel); case SET_RADIO_GROUP_CHECKED: return new SetRadioGroupCheckedAction(parcel); + case SET_VIEW_OUTLINE_RADIUS_TAG: + return new SetViewOutlinePreferredRadiusAction(parcel); default: throw new ActionException("Tag " + tag + " not found"); } @@ -3595,6 +3682,28 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Sets an OutlineProvider on the view whose corner radius is a dimension calculated using + * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}. This outline may change shape + * during system transitions. + * + * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0. + * Setting margins in pixels will behave poorly when the RemoteViews object is used on a + * display with a different density. + */ + public void setViewOutlinePreferredRadius( + @IdRes int viewId, float radius, @ComplexDimensionUnit int units) { + addAction(new SetViewOutlinePreferredRadiusAction(viewId, radius, units)); + } + + /** + * Sets an OutlineProvider on the view whose corner radius is a dimension resource with + * {@code resId}. This outline may change shape during system transitions. + */ + public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) { + addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId)); + } + + /** * Call a method taking one boolean on a view in the layout for this RemoteViews. * * @param viewId The id of the view on which to call the method. diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index 8fe17fb1565c..16b6f3a155f8 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -43,8 +43,7 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { */ @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final double powerMah = mPowerEstimator.calculatePower(durationMs); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1f7a7aa42669..6e41b3f4ae06 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -104,6 +104,7 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeRead import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket; import com.android.internal.util.ArrayUtils; @@ -10842,6 +10843,10 @@ public class BatteryStatsImpl extends BatteryStats { mSystemServerCpuThreadReader.startTrackingThreadCpuTime(); } + public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() { + return mSystemServerCpuThreadReader.readAbsolute(); + } + public void setCallback(BatteryCallback cb) { mCallback = cb; } diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index e76e34f840ba..094724c00508 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -24,7 +24,6 @@ import android.os.BatteryUsageStatsQuery; import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManager; import android.util.SparseArray; import java.util.ArrayList; @@ -89,26 +88,27 @@ public class BatteryUsageStatsProvider { final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext, false /* collectBatteryBroadcast */); batteryStatsHelper.create((Bundle) null); - final UserManager userManager = mContext.getSystemService(UserManager.class); - final List<UserHandle> asUsers = userManager.getUserProfiles(); - final int n = asUsers.size(); - SparseArray<UserHandle> users = new SparseArray<>(n); - for (int i = 0; i < n; ++i) { - UserHandle userHandle = asUsers.get(i); - users.put(userHandle.getIdentifier(), userHandle); + final List<UserHandle> users = new ArrayList<>(); + for (int i = 0; i < queries.size(); i++) { + BatteryUsageStatsQuery query = queries.get(i); + for (int userId : query.getUserIds()) { + UserHandle userHandle = UserHandle.of(userId); + if (!users.contains(userHandle)) { + users.add(userHandle); + } + } } - batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, users); ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size()); for (int i = 0; i < queries.size(); i++) { - results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper, users)); + results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper)); } return results; } private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query, - BatteryStatsHelper batteryStatsHelper, SparseArray<UserHandle> users) { + BatteryStatsHelper batteryStatsHelper) { // TODO(b/174186358): read extra power component number from configuration final int customPowerComponentCount = 0; final int customTimeComponentCount = 0; @@ -128,8 +128,8 @@ public class BatteryUsageStatsProvider { final List<PowerCalculator> powerCalculators = getPowerCalculators(); for (PowerCalculator powerCalculator : powerCalculators) { - powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs, query, - users); + powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs, + query); } return batteryUsageStatsBuilder.build(); diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index 4c3b950ff715..7d42de4486a4 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -50,8 +50,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) { return; } @@ -68,7 +67,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); calculateApp(app, total); if (app.getUid() == Process.BLUETOOTH_UID) { - app.setSystemComponent(true); + app.excludeFromBatteryUsageStats(); systemBatteryConsumerBuilder.addUidBatteryConsumer(app); } } diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 11c87618762e..45d81280af4a 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -17,81 +17,166 @@ package com.android.internal.os; import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.UidBatteryConsumer; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; + +import java.util.List; public class CpuPowerCalculator extends PowerCalculator { private static final String TAG = "CpuPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000; - private final PowerProfile mProfile; + private final int mNumCpuClusters; + + // Time-in-state based CPU power estimation model computes the estimated power + // by adding up three components: + // - CPU Active power: the constant amount of charge consumed by the CPU when it is on + // - Per Cluster power: the additional amount of charge consumed by a CPU cluster + // when it is running + // - Per frequency power: the additional amount of charge caused by dynamic frequency scaling + + private final UsageBasedPowerEstimator mCpuActivePowerEstimator; + // One estimator per cluster + private final UsageBasedPowerEstimator[] mPerClusterPowerEstimators; + // Multiple estimators per cluster: one per available scaling frequency. Note that different + // clusters have different sets of frequencies and corresponding power consumption averages. + private final UsageBasedPowerEstimator[][] mPerCpuFreqPowerEstimators; + + private static class Result { + public long durationMs; + public double powerMah; + public long durationFgMs; + public String packageWithHighestDrain; + } public CpuPowerCalculator(PowerProfile profile) { - mProfile = profile; + mNumCpuClusters = profile.getNumCpuClusters(); + + mCpuActivePowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE)); + + mPerClusterPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters]; + for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { + mPerClusterPowerEstimators[cluster] = new UsageBasedPowerEstimator( + profile.getAveragePowerForCpuCluster(cluster)); + } + + mPerCpuFreqPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters][]; + for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { + final int speedsForCluster = profile.getNumSpeedStepsInCpuCluster(cluster); + mPerCpuFreqPowerEstimators[cluster] = new UsageBasedPowerEstimator[speedsForCluster]; + for (int speed = 0; speed < speedsForCluster; speed++) { + mPerCpuFreqPowerEstimators[cluster][speed] = + new UsageBasedPowerEstimator( + profile.getAveragePowerForCpuCore(cluster, speed)); + } + } } @Override - protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - final int statsType = BatteryStats.STATS_SINCE_CHARGED; + Result result = new Result(); + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + calculateApp(app, app.getBatteryStatsUid(), result); + } + } - long cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; - final int numClusters = mProfile.getNumCpuClusters(); + private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) { + calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result); - double cpuPowerMaUs = 0; - for (int cluster = 0; cluster < numClusters; cluster++) { - final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster); - for (int speed = 0; speed < speedsForCluster; speed++) { - final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); - final double cpuSpeedStepPower = timeUs * - mProfile.getAveragePowerForCpuCore(cluster, speed); - if (DEBUG) { - Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" - + speed + " timeUs=" + timeUs + " power=" - + formatCharge(cpuSpeedStepPower / MICROSEC_IN_HR)); - } - cpuPowerMaUs += cpuSpeedStepPower; + app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, + result.durationFgMs) + .setPackageWithHighestDrain(result.packageWithHighestDrain); + } + + @Override + public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { + Result result = new Result(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateApp(app, app.uidObj, statsType, result); } } - cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower( - PowerProfile.POWER_CPU_ACTIVE); + } + + private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) { + calculatePowerAndDuration(u, statsType, result); + + app.cpuPowerMah = result.powerMah; + app.cpuTimeMs = result.durationMs; + app.cpuFgTimeMs = result.durationFgMs; + app.packageWithHighestDrain = result.packageWithHighestDrain; + } + + private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) { + long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; + + // Constant battery drain when CPU is active + double powerMah = mCpuActivePowerEstimator.calculatePower(u.getCpuActiveTime()); + + // Additional per-cluster battery drain long[] cpuClusterTimes = u.getCpuClusterTimes(); if (cpuClusterTimes != null) { - if (cpuClusterTimes.length == numClusters) { - for (int i = 0; i < numClusters; i++) { - double power = - cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i); - cpuPowerMaUs += power; + if (cpuClusterTimes.length == mNumCpuClusters) { + for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { + double power = mPerClusterPowerEstimators[cluster] + .calculatePower(cpuClusterTimes[cluster]); + powerMah += power; if (DEBUG) { - Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs=" - + cpuClusterTimes[i] + " power=" - + formatCharge(power / MICROSEC_IN_HR)); + Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + + " clusterTimeMs=" + cpuClusterTimes[cluster] + + " power=" + formatCharge(power)); } } } else { Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # " - + numClusters + " actual # " + cpuClusterTimes.length); + + mNumCpuClusters + " actual # " + cpuClusterTimes.length); } } - final double cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; - if (DEBUG && (cpuTimeMs != 0 || cpuPowerMah != 0)) { - Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + cpuTimeMs + " ms power=" - + formatCharge(cpuPowerMah)); + // Additional per-frequency battery drain + for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { + final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length; + for (int speed = 0; speed < speedsForCluster; speed++) { + final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); + final double power = + mPerCpuFreqPowerEstimators[cluster][speed].calculatePower(timeUs / 1000); + if (DEBUG) { + Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" + + speed + " timeUs=" + timeUs + " power=" + + formatCharge(power)); + } + powerMah += power; + } + } + + if (DEBUG && (durationMs != 0 || powerMah != 0)) { + Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + durationMs + " ms power=" + + formatCharge(powerMah)); } // Keep track of the package with highest drain. double highestDrain = 0; String packageWithHighestDrain = null; - long cpuFgTimeMs = 0; + long durationFgMs = 0; final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); final int processStatsCount = processStats.size(); for (int i = 0; i < processStatsCount; i++) { final BatteryStats.Uid.Proc ps = processStats.valueAt(i); final String processName = processStats.keyAt(i); - cpuFgTimeMs += ps.getForegroundTime(statsType); + durationFgMs += ps.getForegroundTime(statsType); final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType) + ps.getForegroundTime(statsType); @@ -107,20 +192,19 @@ public class CpuPowerCalculator extends PowerCalculator { } } - // Ensure that the CPU times make sense. - if (cpuFgTimeMs > cpuTimeMs) { - if (DEBUG && cpuFgTimeMs > cpuTimeMs + 10000) { + if (durationFgMs > durationMs) { + if (DEBUG && durationFgMs > durationMs + 10000) { Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); } // Statistics may not have been gathered yet. - cpuTimeMs = cpuFgTimeMs; + durationMs = durationFgMs; } - app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, cpuPowerMah) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, cpuTimeMs) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, cpuFgTimeMs) - .setPackageWithHighestDrain(packageWithHighestDrain); + result.durationMs = durationMs; + result.durationFgMs = durationFgMs; + result.powerMah = powerMah; + result.packageWithHighestDrain = packageWithHighestDrain; } } diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java index 9ea934af642a..df25cdaee17f 100644 --- a/core/java/com/android/internal/os/GnssPowerCalculator.java +++ b/core/java/com/android/internal/os/GnssPowerCalculator.java @@ -45,8 +45,7 @@ public class GnssPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java index dcc8a15b2f50..4a4991bd3966 100644 --- a/core/java/com/android/internal/os/IdlePowerCalculator.java +++ b/core/java/com/android/internal/os/IdlePowerCalculator.java @@ -49,8 +49,7 @@ public class IdlePowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs, BatteryStats.STATS_SINCE_CHARGED); if (mPowerMah != 0) { diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java index df4605838b28..21dcce9fb64f 100644 --- a/core/java/com/android/internal/os/MemoryPowerCalculator.java +++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java @@ -26,8 +26,7 @@ public class MemoryPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final double powerMah = calculatePower(batteryStats, rawRealtimeUs, diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java index e3bd64d77e9b..22001d4ffbb7 100644 --- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java +++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java @@ -85,8 +85,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { PowerAndDuration total = new PowerAndDuration(); diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java index 6ab8c90d21d9..362ca0761b2c 100644 --- a/core/java/com/android/internal/os/PhonePowerCalculator.java +++ b/core/java/com/android/internal/os/PhonePowerCalculator.java @@ -39,8 +39,7 @@ public class PhonePowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java index 05fcc705e734..141363fa8b25 100644 --- a/core/java/com/android/internal/os/PowerCalculator.java +++ b/core/java/com/android/internal/os/PowerCalculator.java @@ -67,17 +67,10 @@ public abstract class PowerCalculator { * @param batteryStats The recorded battery stats. * @param rawRealtimeUs The raw system realtime in microseconds. * @param rawUptimeUs The raw system uptime in microseconds. - * @param statsType The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this - * can only be {@link BatteryStats#STATS_SINCE_CHARGED}, since - * {@link BatteryStats#STATS_CURRENT} and - * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated. - * @param asUsers An array of users for which the attribution is requested. It may - * contain {@link UserHandle#USER_ALL} to indicate that the attribution - * should be performed for all users. + * @param query The query parameters for the calculator. */ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = builder.getUidBatteryConsumerBuilders(); for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { @@ -99,19 +92,6 @@ public abstract class PowerCalculator { */ protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { - - // TODO(b/175156498): Temporary code during the transition from BatterySippers to - // BatteryConsumers. - UidBatteryConsumer.Builder builder = new UidBatteryConsumer.Builder(0, 0, u); - calculateApp(builder, u, rawRealtimeUs, rawUptimeUs, BatteryUsageStatsQuery.DEFAULT); - final UidBatteryConsumer uidBatteryConsumer = builder.build(); - app.cpuPowerMah = uidBatteryConsumer.getConsumedPower( - UidBatteryConsumer.POWER_COMPONENT_CPU); - app.cpuTimeMs = uidBatteryConsumer.getUsageDurationMillis( - UidBatteryConsumer.TIME_COMPONENT_CPU); - app.cpuFgTimeMs = uidBatteryConsumer.getUsageDurationMillis( - UidBatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND); - app.packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain(); } /** diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index c86c795c965a..bb1222eac11c 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -51,8 +51,7 @@ public class ScreenPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final long durationMs = computeDuration(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final double powerMah = computePower(batteryStats, rawRealtimeUs, diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java index fbad75e93a17..3ed59f1d4370 100644 --- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -110,4 +110,24 @@ public class SystemServerCpuThreadReader { return mDeltaCpuThreadTimes; } + + /** Returns CPU times, per thread group, since tracking started. */ + @Nullable + public SystemServiceCpuThreadTimes readAbsolute() { + final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount(); + final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = + mKernelCpuThreadReader.getProcessCpuUsage(); + if (processCpuUsage == null) { + return null; + } + final SystemServiceCpuThreadTimes result = new SystemServiceCpuThreadTimes(); + result.threadCpuTimesUs = new long[numCpuFrequencies]; + result.binderThreadCpuTimesUs = new long[numCpuFrequencies]; + for (int i = 0; i < numCpuFrequencies; ++i) { + result.threadCpuTimesUs[i] = processCpuUsage.threadCpuTimesMillis[i] * 1_000; + result.binderThreadCpuTimesUs[i] = + processCpuUsage.selectedThreadCpuTimesMillis[i] * 1_000; + } + return result; + } } diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java index 55fc1bb607a9..955f6afe579c 100644 --- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -51,10 +51,9 @@ public class SystemServicePowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - SparseArray<UserHandle> asUsers) { + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { calculateSystemServicePower(batteryStats); - super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query, asUsers); + super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query); } @Override diff --git a/core/java/com/android/internal/os/UserPowerCalculator.java b/core/java/com/android/internal/os/UserPowerCalculator.java index 53f85154330c..8e802869e2fc 100644 --- a/core/java/com/android/internal/os/UserPowerCalculator.java +++ b/core/java/com/android/internal/os/UserPowerCalculator.java @@ -17,10 +17,15 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; import android.os.Process; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.SparseArray; +import com.android.internal.util.ArrayUtils; + import java.util.List; /** @@ -29,6 +34,33 @@ import java.util.List; public class UserPowerCalculator extends PowerCalculator { @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final int[] userIds = query.getUserIds(); + if (ArrayUtils.contains(userIds, UserHandle.USER_ALL)) { + return; + } + + SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + UidBatteryConsumer.Builder uidBuilder = uidBatteryConsumerBuilders.valueAt(i); + final int uid = uidBuilder.getUid(); + if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) { + continue; + } + + final int userId = UserHandle.getUserId(uid); + if (!ArrayUtils.contains(userIds, userId)) { + uidBuilder.excludeFromBatteryUsageStats(); + builder.getOrCreateUserBatteryConsumerBuilder(userId) + .addUidBatteryConsumer(uidBuilder); + } + } + } + + @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null); diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java new file mode 100644 index 000000000000..6cc5a4aacda5 --- /dev/null +++ b/core/java/com/android/internal/widget/CallLayout.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.widget; + +import android.annotation.AttrRes; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StyleRes; +import android.app.Notification; +import android.app.Person; +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.RemotableViewMethod; +import android.widget.FrameLayout; +import android.widget.RemoteViews; +import android.widget.TextView; + +import com.android.internal.R; + +/** + * A custom-built layout for the Notification.CallStyle. + */ +@RemoteViews.RemoteView +public class CallLayout extends FrameLayout { + private final PeopleHelper mPeopleHelper = new PeopleHelper(); + + private int mLayoutColor; + private Icon mLargeIcon; + private Person mUser; + + private CachingIconView mConversationIconView; + private CachingIconView mIcon; + private CachingIconView mConversationIconBadgeBg; + private TextView mConversationText; + + public CallLayout(@NonNull Context context) { + super(context); + } + + public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mPeopleHelper.init(getContext()); + mConversationText = findViewById(R.id.conversation_text); + mConversationIconView = findViewById(R.id.conversation_icon); + mIcon = findViewById(R.id.icon); + mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg); + + // When the small icon is gone, hide the rest of the badge + mIcon.setOnForceHiddenChangedListener((forceHidden) -> { + mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden); + }); + } + + private void updateCallLayout() { + CharSequence callerName = ""; + String symbol = ""; + Icon icon = null; + if (mUser != null) { + icon = mUser.getIcon(); + callerName = mUser.getName(); + symbol = mPeopleHelper.findNamePrefix(callerName, ""); + } + if (icon == null) { + icon = mLargeIcon; + } + if (icon == null) { + icon = mPeopleHelper.createAvatarSymbol(callerName, symbol, mLayoutColor); + } + // TODO(b/179178086): crop/clip the icon to a circle? + mConversationIconView.setImageIcon(icon); + mConversationText.setText(callerName); + } + + @RemotableViewMethod + public void setLayoutColor(int color) { + mLayoutColor = color; + } + + /** + * @param color the color of the notification background + */ + @RemotableViewMethod + public void setNotificationBackgroundColor(int color) { + mConversationIconBadgeBg.setImageTintList(ColorStateList.valueOf(color)); + } + + @RemotableViewMethod + public void setLargeIcon(Icon largeIcon) { + mLargeIcon = largeIcon; + } + + /** + * Set the notification extras so that this layout has access + */ + @RemotableViewMethod + public void setData(Bundle extras) { + setUser(extras.getParcelable(Notification.EXTRA_CALL_PERSON)); + updateCallLayout(); + } + + private void setUser(Person user) { + mUser = user; + } + +} diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 40e671ffd27c..1b1e0bfb3a58 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -18,8 +18,6 @@ package com.android.internal.widget; import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL; import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE; -import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN; -import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -34,10 +32,7 @@ import android.app.Person; import android.app.RemoteInputHistoryItem; import android.content.Context; import android.content.res.ColorStateList; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.GradientDrawable; @@ -68,14 +63,12 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.graphics.ColorUtils; -import com.android.internal.util.ContrastColorUtil; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.function.Consumer; -import java.util.regex.Pattern; /** * A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal @@ -85,16 +78,6 @@ import java.util.regex.Pattern; public class ConversationLayout extends FrameLayout implements ImageMessageConsumer, IMessagingLayout { - private static final float COLOR_SHIFT_AMOUNT = 60; - /** - * Pattern for filter some ignorable characters. - * p{Z} for any kind of whitespace or invisible separator. - * p{C} for any kind of punctuation character. - */ - private static final Pattern IGNORABLE_CHAR_PATTERN - = Pattern.compile("[\\p{C}\\p{Z}]"); - private static final Pattern SPECIAL_CHAR_PATTERN - = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); private static final Consumer<MessagingMessage> REMOVE_MESSAGE = MessagingMessage::removeMessage; public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); @@ -106,6 +89,7 @@ public class ConversationLayout extends FrameLayout public static final int IMPORTANCE_ANIM_GROW_DURATION = 250; public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200; public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25; + private final PeopleHelper mPeopleHelper = new PeopleHelper(); private List<MessagingMessage> mMessages = new ArrayList<>(); private List<MessagingMessage> mHistoricMessages = new ArrayList<>(); private MessagingLinearLayout mMessagingLinearLayout; @@ -114,9 +98,6 @@ public class ConversationLayout extends FrameLayout private int mLayoutColor; private int mSenderTextColor; private int mMessageTextColor; - private int mAvatarSize; - private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint mTextPaint = new Paint(); private Icon mAvatarReplacement; private boolean mIsOneToOne; private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>(); @@ -196,6 +177,7 @@ public class ConversationLayout extends FrameLayout @Override protected void onFinishInflate() { super.onFinishInflate(); + mPeopleHelper.init(getContext()); mMessagingLinearLayout = findViewById(R.id.notification_messaging); mActions = findViewById(R.id.actions); mImageMessageContainer = findViewById(R.id.conversation_image_message_container); @@ -205,9 +187,6 @@ public class ConversationLayout extends FrameLayout int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels); mMessagingClipRect = new Rect(0, 0, size, size); setMessagingClippingDisabled(false); - mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); - mTextPaint.setTextAlign(Paint.Align.CENTER); - mTextPaint.setAntiAlias(true); mConversationIconView = findViewById(R.id.conversation_icon); mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); @@ -250,15 +229,15 @@ public class ConversationLayout extends FrameLayout }); // When the small icon is gone, hide the rest of the badge mIcon.setOnForceHiddenChangedListener((forceHidden) -> { - animateViewForceHidden(mConversationIconBadgeBg, forceHidden); - animateViewForceHidden(mImportanceRingView, forceHidden); + mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden); + mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden); }); // When the conversation icon is gone, hide the whole badge mConversationIconView.setOnForceHiddenChangedListener((forceHidden) -> { - animateViewForceHidden(mConversationIconBadgeBg, forceHidden); - animateViewForceHidden(mImportanceRingView, forceHidden); - animateViewForceHidden(mIcon, forceHidden); + mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden); + mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden); + mPeopleHelper.animateViewForceHidden(mIcon, forceHidden); }); mConversationText = findViewById(R.id.conversation_text); mExpandButtonContainer = findViewById(R.id.expand_button_container); @@ -317,28 +296,6 @@ public class ConversationLayout extends FrameLayout R.dimen.notification_header_separating_margin); } - private void animateViewForceHidden(CachingIconView view, boolean forceHidden) { - boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden(); - if (forceHidden == nowForceHidden) { - // We are either already forceHidden or will be - return; - } - view.animate().cancel(); - view.setWillBeForceHidden(forceHidden); - view.animate() - .scaleX(forceHidden ? 0.5f : 1.0f) - .scaleY(forceHidden ? 0.5f : 1.0f) - .alpha(forceHidden ? 0.0f : 1.0f) - .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN) - .setDuration(160); - if (view.getVisibility() != VISIBLE) { - view.setForceHidden(forceHidden); - } else { - view.animate().withEndAction(() -> view.setForceHidden(forceHidden)); - } - view.animate().start(); - } - @RemotableViewMethod public void setAvatarReplacement(Icon icon) { mAvatarReplacement = icon; @@ -561,7 +518,8 @@ public class ConversationLayout extends FrameLayout if (mConversationIcon == null) { Icon avatarIcon = messagingGroup.getAvatarIcon(); if (avatarIcon == null) { - avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor); + avatarIcon = mPeopleHelper.createAvatarSymbol(conversationText, "", + mLayoutColor); } mConversationIcon = avatarIcon; } @@ -674,11 +632,11 @@ public class ConversationLayout extends FrameLayout } } if (lastIcon == null) { - lastIcon = createAvatarSymbol(" ", "", mLayoutColor); + lastIcon = mPeopleHelper.createAvatarSymbol(" ", "", mLayoutColor); } bottomView.setImageIcon(lastIcon); if (secondLastIcon == null) { - secondLastIcon = createAvatarSymbol("", "", mLayoutColor); + secondLastIcon = mPeopleHelper.createAvatarSymbol("", "", mLayoutColor); } topView.setImageIcon(secondLastIcon); } @@ -838,8 +796,10 @@ public class ConversationLayout extends FrameLayout } private void updateTitleAndNamesDisplay() { + // Map of unique names to their prefix ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>(); - ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>(); + // Map of single-character string prefix to the only name which uses it, or null if multiple + ArrayMap<String, CharSequence> uniqueCharacters = new ArrayMap<>(); for (int i = 0; i < mGroups.size(); i++) { MessagingGroup group = mGroups.get(i); CharSequence senderName = group.getSenderName(); @@ -847,22 +807,22 @@ public class ConversationLayout extends FrameLayout continue; } if (!uniqueNames.containsKey(senderName)) { - // Only use visible characters to get uniqueNames - String pureSenderName = IGNORABLE_CHAR_PATTERN - .matcher(senderName).replaceAll("" /* replacement */); - char c = pureSenderName.charAt(0); - if (uniqueCharacters.containsKey(c)) { + String charPrefix = mPeopleHelper.findNamePrefix(senderName, null); + if (charPrefix == null) { + continue; + } + if (uniqueCharacters.containsKey(charPrefix)) { // this character was already used, lets make it more unique. We first need to // resolve the existing character if it exists - CharSequence existingName = uniqueCharacters.get(c); + CharSequence existingName = uniqueCharacters.get(charPrefix); if (existingName != null) { - uniqueNames.put(existingName, findNameSplit((String) existingName)); - uniqueCharacters.put(c, null); + uniqueNames.put(existingName, mPeopleHelper.findNameSplit(existingName)); + uniqueCharacters.put(charPrefix, null); } - uniqueNames.put(senderName, findNameSplit((String) senderName)); + uniqueNames.put(senderName, mPeopleHelper.findNameSplit(senderName)); } else { - uniqueNames.put(senderName, Character.toString(c)); - uniqueCharacters.put(c, pureSenderName); + uniqueNames.put(senderName, charPrefix); + uniqueCharacters.put(charPrefix, senderName); } } } @@ -898,8 +858,8 @@ public class ConversationLayout extends FrameLayout } else { Icon cachedIcon = cachedAvatars.get(senderName); if (cachedIcon == null) { - cachedIcon = createAvatarSymbol(senderName, uniqueNames.get(senderName), - mLayoutColor); + cachedIcon = mPeopleHelper.createAvatarSymbol(senderName, + uniqueNames.get(senderName), mLayoutColor); cachedAvatars.put(senderName, cachedIcon); } group.setCreatedAvatar(cachedIcon, senderName, uniqueNames.get(senderName), @@ -908,49 +868,6 @@ public class ConversationLayout extends FrameLayout } } - private Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) { - if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) || - SPECIAL_CHAR_PATTERN.matcher(symbol).find()) { - Icon avatarIcon = Icon.createWithResource(getContext(), - R.drawable.messaging_user); - avatarIcon.setTint(findColor(senderName, layoutColor)); - return avatarIcon; - } else { - Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - float radius = mAvatarSize / 2.0f; - int color = findColor(senderName, layoutColor); - mPaint.setColor(color); - canvas.drawCircle(radius, radius, radius, mPaint); - boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f; - mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE); - mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f); - int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2)); - canvas.drawText(symbol, radius, yPos, mTextPaint); - return Icon.createWithBitmap(bitmap); - } - } - - private int findColor(CharSequence senderName, int layoutColor) { - double luminance = ContrastColorUtil.calculateLuminance(layoutColor); - float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f; - - // we need to offset the range if the luminance is too close to the borders - shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0); - shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0); - return ContrastColorUtil.getShiftedColor(layoutColor, - (int) (shift * COLOR_SHIFT_AMOUNT)); - } - - private String findNameSplit(String existingName) { - String[] split = existingName.split(" "); - if (split.length > 1) { - return Character.toString(split[0].charAt(0)) - + Character.toString(split[1].charAt(0)); - } - return existingName.substring(0, 1); - } - @RemotableViewMethod public void setLayoutColor(int color) { mLayoutColor = color; diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java index 5213746e5a12..058a9218def4 100644 --- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java +++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java @@ -16,17 +16,24 @@ package com.android.internal.widget; +import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; +import android.graphics.BlendMode; +import android.graphics.drawable.Drawable; import android.graphics.drawable.DrawableWrapper; import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.InsetDrawable; +import android.graphics.drawable.Icon; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.RemotableViewMethod; +import android.view.ViewGroup; import android.widget.Button; +import android.widget.LinearLayout; import android.widget.RemoteViews; +import com.android.internal.R; + /** * A button implementation for the emphasized notification style. * @@ -37,6 +44,7 @@ public class EmphasizedNotificationButton extends Button { private final RippleDrawable mRipple; private final int mStrokeWidth; private final int mStrokeColor; + private boolean mPriority; public EmphasizedNotificationButton(Context context) { this(context, null); @@ -80,4 +88,57 @@ public class EmphasizedNotificationButton extends Button { inner.setStroke(hasStroke ? mStrokeWidth : 0, mStrokeColor); invalidate(); } + + /** + * Sets an image icon which will have its size constrained and will be set to the same color as + * the text. Must be called after {@link #setTextColor(int)} for the latter to work. + */ + @RemotableViewMethod(asyncImpl = "setImageIconAsync") + public void setImageIcon(@Nullable Icon icon) { + final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext); + setImageDrawable(drawable); + } + + /** + * @hide + */ + @RemotableViewMethod + public Runnable setImageIconAsync(@Nullable Icon icon) { + final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext); + return () -> setImageDrawable(drawable); + } + + private void setImageDrawable(Drawable drawable) { + if (drawable != null) { + drawable.mutate(); + drawable.setTintList(getTextColors()); + drawable.setTintBlendMode(BlendMode.SRC_IN); + int iconSize = mContext.getResources().getDimensionPixelSize( + R.dimen.notification_actions_icon_drawable_size); + drawable.setBounds(0, 0, iconSize, iconSize); + } + setCompoundDrawablesRelative(drawable, null, null, null); + } + + /** + * Changes the LayoutParams.width to WRAP_CONTENT, with the argument representing if this view + * is a priority over its peers (which affects weight). + */ + @RemotableViewMethod + public void setWrapModePriority(boolean priority) { + mPriority = priority; + ViewGroup.LayoutParams layoutParams = getLayoutParams(); + layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; + if (layoutParams instanceof LinearLayout.LayoutParams) { + ((LinearLayout.LayoutParams) layoutParams).weight = 0; + } + setLayoutParams(layoutParams); + } + + /** + * Sizing this button is a priority compared with its peers. + */ + public boolean isPriority() { + return mPriority; + } } diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java index c7ea781b2793..8e6497b204c7 100644 --- a/core/java/com/android/internal/widget/NotificationActionListLayout.java +++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java @@ -16,6 +16,7 @@ package com.android.internal.widget; +import android.annotation.DimenRes; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.RippleDrawable; @@ -41,13 +42,16 @@ public class NotificationActionListLayout extends LinearLayout { private final int mGravity; private int mTotalWidth = 0; + private int mExtraStartPadding = 0; private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>(); private ArrayList<View> mMeasureOrderOther = new ArrayList<>(); private boolean mEmphasizedMode; + private boolean mPrioritizedWrapMode; private int mDefaultPaddingBottom; private int mDefaultPaddingTop; private int mEmphasizedHeight; private int mRegularHeight; + @DimenRes private int mCollapsibleIndentDimen; public NotificationActionListLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -68,7 +72,7 @@ public class NotificationActionListLayout extends LinearLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mEmphasizedMode) { + if (mEmphasizedMode && !mPrioritizedWrapMode) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } @@ -151,7 +155,15 @@ public class NotificationActionListLayout extends LinearLayout { measuredChildren++; } - mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft; + int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0 + : getResources().getDimensionPixelOffset(mCollapsibleIndentDimen); + if (innerWidth - usedWidth > collapsibleIndent) { + mExtraStartPadding = collapsibleIndent; + } else { + mExtraStartPadding = 0; + } + + mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding; setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec), resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } @@ -163,7 +175,11 @@ public class NotificationActionListLayout extends LinearLayout { final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View c = getChildAt(i); - if (c instanceof TextView && ((TextView) c).getText().length() > 0) { + if (c instanceof EmphasizedNotificationButton + && ((EmphasizedNotificationButton) c).isPriority()) { + // add with 0 length to ensure that this view is measured before others. + mMeasureOrderTextViews.add(Pair.create(0, (TextView) c)); + } else if (c instanceof TextView && ((TextView) c).getText().length() > 0) { mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(), (TextView)c)); } else { @@ -197,7 +213,7 @@ public class NotificationActionListLayout extends LinearLayout { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (mEmphasizedMode) { + if (mEmphasizedMode && !mPrioritizedWrapMode) { super.onLayout(changed, left, top, right, bottom); return; } @@ -214,6 +230,9 @@ public class NotificationActionListLayout extends LinearLayout { int absoluteGravity = Gravity.getAbsoluteGravity(Gravity.START, getLayoutDirection()); if (absoluteGravity == Gravity.RIGHT) { childLeft += right - left - mTotalWidth; + } else { + // Put the extra start padding (if any) on the left when LTR + childLeft += mExtraStartPadding; } } @@ -274,6 +293,26 @@ public class NotificationActionListLayout extends LinearLayout { } /** + * When used with emphasizedMode, changes the button sizing behavior to prioritize certain + * buttons (which are system generated) to not scrunch, and leave the remaining space for + * custom actions. + */ + @RemotableViewMethod + public void setPrioritizedWrapMode(boolean prioritizedWrapMode) { + mPrioritizedWrapMode = prioritizedWrapMode; + } + + /** + * When buttons are in wrap mode, this is a padding that will be applied at the start of the + * layout of the actions, but only when those actions would fit with the entire padding + * visible. Otherwise, this padding will be omitted entirely. + */ + @RemotableViewMethod + public void setCollapsibleIndentDimen(@DimenRes int collapsibleIndentDimen) { + mCollapsibleIndentDimen = collapsibleIndentDimen; + } + + /** * Set whether the list is in a mode where some actions are emphasized. This will trigger an * equal measuring where all actions are full height and change a few parameters like * the padding. diff --git a/core/java/com/android/internal/widget/PeopleHelper.java b/core/java/com/android/internal/widget/PeopleHelper.java new file mode 100644 index 000000000000..77f4c8f6bede --- /dev/null +++ b/core/java/com/android/internal/widget/PeopleHelper.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.widget; + +import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN; +import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT; + +import android.annotation.ColorInt; +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.Icon; +import android.text.TextUtils; +import android.view.View; + +import com.android.internal.R; +import com.android.internal.graphics.ColorUtils; +import com.android.internal.util.ContrastColorUtil; + +import java.util.regex.Pattern; + +/** + * This class provides some methods used by both the {@link ConversationLayout} and + * {@link CallLayout} which both use the visual design originally created for conversations in R. + */ +public class PeopleHelper { + + private static final float COLOR_SHIFT_AMOUNT = 60; + /** + * Pattern for filter some ignorable characters. + * p{Z} for any kind of whitespace or invisible separator. + * p{C} for any kind of punctuation character. + */ + private static final Pattern IGNORABLE_CHAR_PATTERN = Pattern.compile("[\\p{C}\\p{Z}]"); + private static final Pattern SPECIAL_CHAR_PATTERN = + Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); + + private Context mContext; + private int mAvatarSize; + private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint mTextPaint = new Paint(); + + /** + * Call this when the view is inflated to provide a context and initialize the helper + */ + public void init(Context context) { + mContext = context; + mAvatarSize = context.getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); + mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextPaint.setAntiAlias(true); + } + + /** + * A utility for animating CachingIconViews away when hidden. + */ + public void animateViewForceHidden(CachingIconView view, boolean forceHidden) { + boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden(); + if (forceHidden == nowForceHidden) { + // We are either already forceHidden or will be + return; + } + view.animate().cancel(); + view.setWillBeForceHidden(forceHidden); + view.animate() + .scaleX(forceHidden ? 0.5f : 1.0f) + .scaleY(forceHidden ? 0.5f : 1.0f) + .alpha(forceHidden ? 0.0f : 1.0f) + .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN) + .setDuration(160); + if (view.getVisibility() != View.VISIBLE) { + view.setForceHidden(forceHidden); + } else { + view.animate().withEndAction(() -> view.setForceHidden(forceHidden)); + } + view.animate().start(); + } + + /** + * This creates an avatar symbol for the given person or group + * + * @param name the name of the person or group + * @param symbol a pre-chosen symbol for the person or group. See + * {@link #findNamePrefix(CharSequence, String)} or + * {@link #findNameSplit(CharSequence)} + * @param layoutColor the background color of the layout + */ + @NonNull + public Icon createAvatarSymbol(@NonNull CharSequence name, @NonNull String symbol, + @ColorInt int layoutColor) { + if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) + || SPECIAL_CHAR_PATTERN.matcher(symbol).find()) { + Icon avatarIcon = Icon.createWithResource(mContext, R.drawable.messaging_user); + avatarIcon.setTint(findColor(name, layoutColor)); + return avatarIcon; + } else { + Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + float radius = mAvatarSize / 2.0f; + int color = findColor(name, layoutColor); + mPaint.setColor(color); + canvas.drawCircle(radius, radius, radius, mPaint); + boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f; + mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE); + mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f); + int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2)); + canvas.drawText(symbol, radius, yPos, mTextPaint); + return Icon.createWithBitmap(bitmap); + } + } + + private int findColor(@NonNull CharSequence senderName, int layoutColor) { + double luminance = ContrastColorUtil.calculateLuminance(layoutColor); + float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f; + + // we need to offset the range if the luminance is too close to the borders + shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0); + shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0); + return ContrastColorUtil.getShiftedColor(layoutColor, + (int) (shift * COLOR_SHIFT_AMOUNT)); + } + + /** + * Get the name with whitespace and punctuation characters removed + */ + private String getPureName(@NonNull CharSequence name) { + return IGNORABLE_CHAR_PATTERN.matcher(name).replaceAll("" /* replacement */); + } + + /** + * Gets a single character string prefix name for the person or group + * + * @param name the name of the person or group + * @param fallback the string to return if the name has no usable characters + */ + public String findNamePrefix(@NonNull CharSequence name, String fallback) { + String pureName = getPureName(name); + if (pureName.isEmpty()) { + return fallback; + } + try { + return new String(Character.toChars(pureName.codePointAt(0))); + } catch (RuntimeException ignore) { + return fallback; + } + } + + /** + * Find a 1 or 2 character prefix name for the person or group + */ + public String findNameSplit(@NonNull CharSequence name) { + String nameString = name instanceof String ? ((String) name) : name.toString(); + String[] split = nameString.trim().split("[ ]+"); + if (split.length > 1) { + String first = findNamePrefix(split[0], null); + String second = findNamePrefix(split[1], null); + if (first != null && second != null) { + return first + second; + } + } + return findNamePrefix(name, ""); + } +} diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index bb51c57c74b2..c64174b93c0d 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -43,6 +43,7 @@ #include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedUtfChars.h> #include "jni.h" +#include <dmabufinfo/dmabuf_sysfs_stats.h> #include <dmabufinfo/dmabufinfo.h> #include <meminfo/procmeminfo.h> #include <meminfo/sysmeminfo.h> @@ -805,6 +806,16 @@ static jlong android_os_Debug_getIonHeapsSizeKb(JNIEnv* env, jobject clazz) { return heapsSizeKb; } +static jlong android_os_Debug_getDmabufTotalExportedKb(JNIEnv* env, jobject clazz) { + jlong dmabufTotalSizeKb = -1; + uint64_t size; + + if (dmabufinfo::GetDmabufTotalExportedKb(&size)) { + dmabufTotalSizeKb = size; + } + return dmabufTotalSizeKb; +} + static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) { jlong poolsSizeKb = -1; uint64_t size; @@ -816,8 +827,19 @@ static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) { return poolsSizeKb; } -static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) { - jlong ionPss = 0; +static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject clazz) { + jlong poolsSizeKb = -1; + uint64_t size; + + if (meminfo::ReadDmabufHeapPoolsSizeKb(&size)) { + poolsSizeKb = size; + } + + return poolsSizeKb; +} + +static jlong android_os_Debug_getDmabufMappedSizeKb(JNIEnv* env, jobject clazz) { + jlong dmabufPss = 0; std::vector<dmabufinfo::DmaBuffer> dmabufs; std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir); @@ -841,10 +863,10 @@ static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) { } for (const dmabufinfo::DmaBuffer& buf : dmabufs) { - ionPss += buf.size() / 1024; + dmabufPss += buf.size() / 1024; } - return ionPss; + return dmabufPss; } static jlong android_os_Debug_getGpuTotalUsageKb(JNIEnv* env, jobject clazz) { @@ -922,10 +944,14 @@ static const JNINativeMethod gMethods[] = { (void*)android_os_Debug_getFreeZramKb }, { "getIonHeapsSizeKb", "()J", (void*)android_os_Debug_getIonHeapsSizeKb }, + { "getDmabufTotalExportedKb", "()J", + (void*)android_os_Debug_getDmabufTotalExportedKb }, { "getIonPoolsSizeKb", "()J", (void*)android_os_Debug_getIonPoolsSizeKb }, - { "getIonMappedSizeKb", "()J", - (void*)android_os_Debug_getIonMappedSizeKb }, + { "getDmabufMappedSizeKb", "()J", + (void*)android_os_Debug_getDmabufMappedSizeKb }, + { "getDmabufHeapPoolsSizeKb", "()J", + (void*)android_os_Debug_getDmabufHeapPoolsSizeKb }, { "getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb }, { "isVmapStack", "()Z", diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto index bb39ea810add..5c6116a09b77 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -114,4 +114,5 @@ message ApplicationInfoProto { optional bool native_heap_zero_init = 21; } optional Detail detail = 17; + repeated string overlay_paths = 18; } diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto index 6794c8cd8acb..0041f199b448 100644 --- a/core/proto/android/net/networkrequest.proto +++ b/core/proto/android/net/networkrequest.proto @@ -63,6 +63,9 @@ message NetworkRequestProto { // higher-scoring network will not go into the background immediately, // but will linger and go into the background after the linger timeout. TYPE_BACKGROUND_REQUEST = 5; + // Like TRACK_DEFAULT, but tracks the system default network, instead of + // the default network of the calling application. + TYPE_TRACK_SYSTEM_DEFAULT = 6; } // The type of the request. This is only used by the system and is always // NONE elsewhere. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 827bf7b70cbc..856657ad8b56 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2698,11 +2698,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|appop|installer|recents|appPredictor|pre23|development --> + <p>Protection level: signature|appop|installer|appPredictor|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|appop|installer|recents|appPredictor|pre23|development" /> + android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" /> <!-- @SystemApi @hide Allows an application to create windows using the type {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}, @@ -5447,6 +5447,11 @@ <permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" android:protectionLevel="signature" /> + <!-- Allows managing the Game Mode + @hide Used internally. --> + <permission android:name="android.permission.MANAGE_GAME_MODE" + android:protectionLevel="signature" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/color/btn_leanback_color.xml b/core/res/res/color/btn_leanback_color.xml new file mode 100644 index 000000000000..012df6069e9c --- /dev/null +++ b/core/res/res/color/btn_leanback_color.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_focused="true" + android:color="@color/btn_leanback_focused" /> + <item android:state_enabled="false" + android:color="@android:color/transparent" /> + <item android:color="@color/btn_leanback_unfocused" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color/btn_leanback_text_color.xml b/core/res/res/color/btn_leanback_text_color.xml new file mode 100644 index 000000000000..df0df948063a --- /dev/null +++ b/core/res/res/color/btn_leanback_text_color.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@color/btn_text_leanback_focused" android:state_focused="true"/> + <item android:color="@color/btn_text_leanback_unfocused"/> +</selector> diff --git a/core/res/res/drawable/btn_leanback.xml b/core/res/res/drawable/btn_leanback.xml new file mode 100644 index 000000000000..9a63986b69d4 --- /dev/null +++ b/core/res/res/drawable/btn_leanback.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> + <solid android:color="@color/btn_leanback_color"/> + <corners android:radius="@dimen/leanback_button_radius"/> +</shape> diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml index 1a574fe39e6e..ad680549aaa1 100644 --- a/core/res/res/drawable/btn_notification_emphasized.xml +++ b/core/res/res/drawable/btn_notification_emphasized.xml @@ -23,7 +23,7 @@ <ripple android:color="?attr/colorControlHighlight"> <item> <shape android:shape="rectangle"> - <corners android:radius="?attr/buttonCornerRadius" /> + <corners android:radius="@dimen/notification_action_button_radius" /> <padding android:left="@dimen/button_padding_horizontal_material" android:top="@dimen/button_padding_vertical_material" android:right="@dimen/button_padding_horizontal_material" diff --git a/core/res/res/drawable/ic_call_answer.xml b/core/res/res/drawable/ic_call_answer.xml new file mode 100644 index 000000000000..77c0ad1a585f --- /dev/null +++ b/core/res/res/drawable/ic_call_answer.xml @@ -0,0 +1,34 @@ +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="20" + android:viewportHeight="20" + android:tint="?android:attr/colorControlNormal" + android:autoMirrored="true"> + <path + android:fillColor="#FF000000" + android:pathData="M6.0168,3.3333L6.5751,5.575L4.6668,7.4916C3.8751,5.7166 3.6001,4.1916 + 3.4834,3.3333H6.0168ZM14.4251,13.375L16.6668,13.9416V16.5166C15.8084,16.4 14.2668,16.125 + 12.4834,15.325L14.4251,13.375ZM6.3418,1.6666H2.5668C2.0918,1.6666 1.7001,2.0666 + 1.7334,2.5416C2.4834,12.875 11.7668,18.275 17.5168,18.275C17.9668,18.275 18.3334,17.9 + 18.3334,17.4416V13.6166C18.3334,13.0416 17.9418,12.5416 + 17.3834,12.4083L14.5918,11.7083C14.2251,11.6166 13.7584,11.6833 + 13.4084,12.0333L10.9251,14.5166C8.6751,13.1833 6.7918,11.3 + 5.4668,9.0416L7.9168,6.5916C8.2251,6.2833 8.3501,5.8333 + 8.2418,5.4083L7.5584,2.6166C7.4168,2.0583 6.9168,1.6666 6.3418,1.6666Z"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_call_decline.xml b/core/res/res/drawable/ic_call_decline.xml new file mode 100644 index 000000000000..a5ee8f4e6e72 --- /dev/null +++ b/core/res/res/drawable/ic_call_decline.xml @@ -0,0 +1,36 @@ +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="20" + android:viewportHeight="20" + android:tint="?android:attr/colorControlNormal" + android:autoMirrored="true"> + <path + android:fillColor="#FF000000" + android:pathData="M5.2834,9.3083V10.7833L4.1084,11.4916L3.1334,10.5166C3.8084,10.0416 + 4.5251,9.6333 5.2834,9.3083ZM14.75,9.325C15.4917,9.65 16.2084,10.05 + 16.875,10.525L15.925,11.475L14.75,10.7666V9.325ZM9.975,6.6666C6.8584,6.6666 3.725,7.7333 + 1.1751,9.9416C0.8084,10.2583 0.9667,10.7166 1.1501,10.9L3.2917,13.0416C3.4917,13.2333 + 3.7417,13.3333 4.0001,13.3333C4.175,13.3333 4.35,13.2833 + 4.5084,13.1916L6.4667,12.0166C6.725,11.8583 6.95,11.5583 6.95,11.1666V8.3833C7.95,8.125 + 8.975,8 10.0084,8C11.0417,8 12.075,8.1333 13.0834,8.3916V11.1416C13.0834,11.4916 + 13.2667,11.8166 13.5667,11.9916L15.525,13.1666C15.6834,13.2583 15.8584,13.3083 + 16.0334,13.3083C16.2917,13.3083 16.5417,13.2083 + 16.7334,13.0166L18.85,10.9C19.1167,10.6333 19.1167,10.1916 18.825,9.9416C16.3334,7.7833 + 13.1667,6.6666 9.975,6.6666Z"/> +</vector> diff --git a/core/res/res/layout/alert_dialog_button_bar_leanback.xml b/core/res/res/layout/alert_dialog_button_bar_leanback.xml new file mode 100644 index 000000000000..ea94af662dcf --- /dev/null +++ b/core/res/res/layout/alert_dialog_button_bar_leanback.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/buttonPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scrollbarAlwaysDrawVerticalTrack="true" + android:scrollIndicators="top|bottom" + android:fillViewport="true" + style="?attr/buttonBarStyle"> + <com.android.internal.widget.ButtonBarLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layoutDirection="locale" + android:orientation="horizontal" + android:gravity="start"> + + <Button + android:id="@+id/button1" + style="?attr/buttonBarPositiveButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button + android:id="@+id/button2" + style="?attr/buttonBarNegativeButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Space + android:id="@+id/spacer" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1" + android:visibility="invisible" /> + + <Button + android:id="@+id/button3" + style="?attr/buttonBarNeutralButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + </com.android.internal.widget.ButtonBarLayout> +</ScrollView> diff --git a/core/res/res/layout/alert_dialog_leanback.xml b/core/res/res/layout/alert_dialog_leanback.xml index 848015c28f4a..091613fd9b56 100644 --- a/core/res/res/layout/alert_dialog_leanback.xml +++ b/core/res/res/layout/alert_dialog_leanback.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2014 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,108 +15,70 @@ limitations under the License. --> -<LinearLayout +<com.android.internal.widget.AlertDialogLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parentPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:background="@drawable/dialog_background_material" - android:translationZ="@dimen/floating_window_z" - android:layout_marginLeft="@dimen/leanback_alert_dialog_horizontal_margin" - android:layout_marginTop="@dimen/leanback_alert_dialog_vertical_margin" - android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin" - android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin"> + android:gravity="start|top" + android:orientation="vertical"> - <LinearLayout android:id="@+id/topPanel" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - <LinearLayout android:id="@+id/title_template" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:gravity="center_vertical|start" - android:paddingStart="16dip" - android:paddingEnd="16dip" - android:paddingTop="16dip"> - <ImageView android:id="@+id/icon" - android:layout_width="32dip" - android:layout_height="32dip" - android:layout_marginEnd="8dip" - android:scaleType="fitCenter" - android:src="@null" /> - <TextView android:id="@+id/alertTitle" - style="?attr/windowTitleStyle" - android:singleLine="true" - android:ellipsize="end" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAlignment="viewStart" /> - </LinearLayout> - <!-- If the client uses a customTitle, it will be added here. --> - </LinearLayout> + <include layout="@layout/alert_dialog_title_material" /> - <LinearLayout android:id="@+id/contentPanel" + <FrameLayout + android:id="@+id/contentPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" - android:orientation="vertical" - android:minHeight="64dp"> - <ScrollView android:id="@+id/scrollView" + android:minHeight="48dp"> + + <ScrollView + android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false"> - <TextView android:id="@+id/message" - style="?attr/textAppearanceMedium" + + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="16dip" - android:paddingEnd="16dip" - android:paddingTop="16dip" /> + android:orientation="vertical"> + + <Space + android:id="@+id/textSpacerNoTitle" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="@dimen/dialog_padding_top_material" /> + + <TextView + android:id="@+id/message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingEnd="?attr/dialogPreferredPadding" + android:paddingStart="?attr/dialogPreferredPadding" + style="@style/TextAppearance.Material.Subhead" /> + + <Space + android:id="@+id/textSpacerNoButtons" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="@dimen/dialog_padding_top_material" /> + </LinearLayout> </ScrollView> - </LinearLayout> + </FrameLayout> - <FrameLayout android:id="@+id/customPanel" + <FrameLayout + android:id="@+id/customPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" - android:minHeight="64dp"> - <FrameLayout android:id="@+android:id/custom" + android:minHeight="48dp"> + + <FrameLayout + android:id="@+id/custom" android:layout_width="match_parent" android:layout_height="wrap_content" /> </FrameLayout> - <LinearLayout android:id="@+id/buttonPanel" + <include android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="@dimen/alert_dialog_button_bar_height" - android:orientation="vertical" - android:gravity="end" - android:padding="16dip"> - <LinearLayout - style="?attr/buttonBarStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layoutDirection="locale"> - <Button android:id="@+id/button3" - style="?attr/buttonBarButtonStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" /> - <Button android:id="@+id/button2" - style="?attr/buttonBarButtonStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" /> - <Button android:id="@+id/button1" - style="?attr/buttonBarButtonStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" /> - </LinearLayout> - </LinearLayout> -</LinearLayout> + layout="@layout/alert_dialog_button_bar_leanback" /> +</com.android.internal.widget.AlertDialogLayout> diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml index a6b7b380eaa4..cd1f1ab88c96 100644 --- a/core/res/res/layout/notification_material_action_emphasized.xml +++ b/core/res/res/layout/notification_material_action_emphasized.xml @@ -22,6 +22,7 @@ android:layout_height="match_parent" android:layout_marginStart="12dp" android:layout_weight="1" + android:drawablePadding="6dp" android:gravity="center" android:textColor="@color/notification_default_color" android:singleLine="true" diff --git a/core/res/res/layout/notification_template_conversation_header.xml b/core/res/res/layout/notification_template_conversation_header.xml new file mode 100644 index 000000000000..b018676e68f0 --- /dev/null +++ b/core/res/res/layout/notification_template_conversation_header.xml @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/conversation_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingTop="16dp" + > + + <TextView + android:id="@+id/conversation_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + android:textSize="16sp" + android:singleLine="true" + android:layout_weight="1" + /> + + <TextView + android:id="@+id/app_name_divider" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" + android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" + android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" + android:text="@string/notification_header_divider_symbol" + android:layout_gravity="center" + android:paddingTop="1sp" + android:singleLine="true" + android:visibility="gone" + /> + + <!-- App Name --> + <com.android.internal.widget.ObservableTextView + android:id="@+id/app_name_text" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" + android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" + android:paddingTop="1sp" + android:singleLine="true" + android:visibility="gone" + /> + + <TextView + android:id="@+id/time_divider" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" + android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" + android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" + android:text="@string/notification_header_divider_symbol" + android:layout_gravity="center" + android:paddingTop="1sp" + android:singleLine="true" + android:visibility="gone" + /> + + <DateTimeView + android:id="@+id/time" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" + android:paddingTop="1sp" + android:showRelative="true" + android:singleLine="true" + android:visibility="gone" + /> + + <ViewStub + android:id="@+id/chronometer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" + android:layout="@layout/notification_template_part_chronometer" + android:visibility="gone" + /> + + <ImageView + android:id="@+id/verification_icon" + android:layout_width="@dimen/notification_badge_size" + android:layout_height="@dimen/notification_badge_size" + android:layout_gravity="center" + android:layout_marginStart="4dp" + android:contentDescription="@string/notification_alerted_content_description" + android:paddingTop="2dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_notifications_alerted" + android:visibility="gone" + /> + + <TextView + android:id="@+id/verification_text" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" + android:paddingTop="1sp" + android:showRelative="true" + android:singleLine="true" + android:visibility="gone" + /> + + <ImageButton + android:id="@+id/feedback" + android:layout_width="@dimen/notification_feedback_size" + android:layout_height="@dimen/notification_feedback_size" + android:layout_gravity="center" + android:layout_marginStart="@dimen/notification_header_separating_margin" + android:background="?android:selectableItemBackgroundBorderless" + android:contentDescription="@string/notification_feedback_indicator" + android:paddingTop="2dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_feedback_indicator" + android:visibility="gone" + /> + + <ImageView + android:id="@+id/profile_badge" + android:layout_width="@dimen/notification_badge_size" + android:layout_height="@dimen/notification_badge_size" + android:layout_gravity="center" + android:layout_marginStart="4dp" + android:paddingTop="2dp" + android:scaleType="fitCenter" + android:visibility="gone" + android:contentDescription="@string/notification_work_profile_content_description" + /> + + <ImageView + android:id="@+id/alerted_icon" + android:layout_width="@dimen/notification_alerted_size" + android:layout_height="@dimen/notification_alerted_size" + android:layout_gravity="center" + android:layout_marginStart="4dp" + android:contentDescription="@string/notification_alerted_content_description" + android:paddingTop="2dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_notifications_alerted" + android:visibility="gone" + /> +</LinearLayout> diff --git a/core/res/res/layout/notification_template_conversation_icon_container.xml b/core/res/res/layout/notification_template_conversation_icon_container.xml new file mode 100644 index 000000000000..e9ec7ce77deb --- /dev/null +++ b/core/res/res/layout/notification_template_conversation_icon_container.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/conversation_icon_container" + android:layout_width="@dimen/conversation_content_start" + android:layout_height="wrap_content" + android:gravity="start|top" + android:clipChildren="false" + android:clipToPadding="false" + android:paddingTop="12dp" + android:paddingBottom="12dp" + android:importantForAccessibility="no" + > + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_gravity="top|center_horizontal" + > + + <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right --> + <com.android.internal.widget.CachingIconView + android:id="@+id/conversation_icon" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:scaleType="centerCrop" + android:importantForAccessibility="no" + /> + + <ViewStub + android:layout="@layout/conversation_face_pile_layout" + android:layout_width="@dimen/conversation_avatar_size" + android:layout_height="@dimen/conversation_avatar_size" + android:id="@+id/conversation_face_pile" + /> + + <FrameLayout + android:id="@+id/conversation_icon_badge" + android:layout_width="@dimen/conversation_icon_size_badged" + android:layout_height="@dimen/conversation_icon_size_badged" + android:layout_marginLeft="@dimen/conversation_badge_side_margin" + android:layout_marginTop="@dimen/conversation_badge_side_margin" + android:clipChildren="false" + android:clipToPadding="false" + > + + <com.android.internal.widget.CachingIconView + android:id="@+id/conversation_icon_badge_bg" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:src="@drawable/conversation_badge_background" + android:forceHasOverlappingRendering="false" + android:scaleType="center" + /> + + <com.android.internal.widget.CachingIconView + android:id="@+id/icon" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="4dp" + android:layout_gravity="center" + android:forceHasOverlappingRendering="false" + /> + + <com.android.internal.widget.CachingIconView + android:id="@+id/conversation_icon_badge_ring" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:src="@drawable/conversation_badge_ring" + android:visibility="gone" + android:forceHasOverlappingRendering="false" + android:clipToPadding="false" + android:scaleType="center" + /> + </FrameLayout> + </FrameLayout> +</FrameLayout> diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml new file mode 100644 index 000000000000..471d874c59f5 --- /dev/null +++ b/core/res/res/layout/notification_template_material_call.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<com.android.internal.widget.CallLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:clipChildren="false" + android:tag="call" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts --> + <include layout="@layout/notification_template_conversation_icon_container" /> + + <LinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/notification_action_list_height" + android:orientation="vertical" + > + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="top" + android:orientation="horizontal" + > + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginStart="@dimen/conversation_content_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:orientation="vertical" + android:minHeight="68dp" + > + + <include + layout="@layout/notification_template_conversation_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + + <include layout="@layout/notification_template_text" /> + + <include + android:layout_width="match_parent" + android:layout_height="@dimen/notification_progress_bar_height" + android:layout_marginTop="@dimen/notification_progress_margin_top" + layout="@layout/notification_template_progress" + /> + </LinearLayout> + + <!-- TODO(b/179178086): remove padding from main column when this is visible --> + <com.android.internal.widget.NotificationExpandButton + android:id="@+id/expand_button" + android:layout_width="@dimen/notification_header_expand_icon_size" + android:layout_height="@dimen/notification_header_expand_icon_size" + android:layout_gravity="top|end" + android:contentDescription="@string/expand_button_content_description_collapsed" + android:paddingTop="@dimen/notification_expand_button_padding_top" + android:scaleType="center" + android:visibility="gone" + /> + + </LinearLayout> + + <include layout="@layout/notification_material_action_list" /> + + </LinearLayout> + +</com.android.internal.widget.CallLayout> diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index f9364d565f3b..f3aa54066c92 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -24,82 +24,7 @@ android:theme="@style/Theme.DeviceDefault.Notification" > - <FrameLayout - android:id="@+id/conversation_icon_container" - android:layout_width="@dimen/conversation_content_start" - android:layout_height="wrap_content" - android:gravity="start|top" - android:clipChildren="false" - android:clipToPadding="false" - android:paddingTop="12dp" - android:paddingBottom="12dp" - android:importantForAccessibility="no" - > - - <FrameLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:clipChildren="false" - android:clipToPadding="false" - android:layout_gravity="top|center_horizontal" - > - - <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right --> - <com.android.internal.widget.CachingIconView - android:id="@+id/conversation_icon" - android:layout_width="@dimen/conversation_avatar_size" - android:layout_height="@dimen/conversation_avatar_size" - android:scaleType="centerCrop" - android:importantForAccessibility="no" - /> - - <ViewStub - android:layout="@layout/conversation_face_pile_layout" - android:layout_width="@dimen/conversation_avatar_size" - android:layout_height="@dimen/conversation_avatar_size" - android:id="@+id/conversation_face_pile" - /> - - <FrameLayout - android:id="@+id/conversation_icon_badge" - android:layout_width="@dimen/conversation_icon_size_badged" - android:layout_height="@dimen/conversation_icon_size_badged" - android:layout_marginLeft="@dimen/conversation_badge_side_margin" - android:layout_marginTop="@dimen/conversation_badge_side_margin" - android:clipChildren="false" - android:clipToPadding="false" - > - <com.android.internal.widget.CachingIconView - android:id="@+id/conversation_icon_badge_bg" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:src="@drawable/conversation_badge_background" - android:forceHasOverlappingRendering="false" - android:scaleType="center" - /> - <com.android.internal.widget.CachingIconView - android:id="@+id/icon" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="4dp" - android:layout_gravity="center" - android:forceHasOverlappingRendering="false" - /> - <com.android.internal.widget.CachingIconView - android:id="@+id/conversation_icon_badge_ring" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:src="@drawable/conversation_badge_ring" - android:visibility="gone" - android:forceHasOverlappingRendering="false" - android:clipToPadding="false" - android:scaleType="center" - /> - </FrameLayout> - </FrameLayout> - </FrameLayout> + <include layout="@layout/notification_template_conversation_icon_container" /> <!-- Wraps entire "expandable" notification --> <com.android.internal.widget.RemeasuringLinearLayout @@ -132,161 +57,14 @@ <!-- Use layout_marginStart instead of paddingStart to work around strange measurement behavior on lower display densities. --> - <LinearLayout - android:id="@+id/conversation_header" + <include + layout="@layout/notification_template_conversation_header" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - android:paddingTop="16dp" android:layout_marginBottom="2dp" android:layout_marginStart="@dimen/conversation_content_start" - > - <TextView - android:id="@+id/conversation_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" - android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" - android:textSize="16sp" - android:singleLine="true" - android:layout_weight="1" - /> - - <TextView - android:id="@+id/app_name_divider" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" - android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" - android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" - android:text="@string/notification_header_divider_symbol" - android:layout_gravity="center" - android:paddingTop="1sp" - android:singleLine="true" - android:visibility="gone" /> - <!-- App Name --> - <com.android.internal.widget.ObservableTextView - android:id="@+id/app_name_text" - android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" - android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" - android:paddingTop="1sp" - android:singleLine="true" - android:visibility="gone" - /> - - <TextView - android:id="@+id/time_divider" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" - android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" - android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" - android:text="@string/notification_header_divider_symbol" - android:layout_gravity="center" - android:paddingTop="1sp" - android:singleLine="true" - android:visibility="gone" - /> - - <DateTimeView - android:id="@+id/time" - android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" - android:paddingTop="1sp" - android:showRelative="true" - android:singleLine="true" - android:visibility="gone" - /> - - <ImageButton - android:id="@+id/feedback" - android:layout_width="@dimen/notification_feedback_size" - android:layout_height="@dimen/notification_feedback_size" - android:layout_gravity="center" - android:layout_marginStart="@dimen/notification_header_separating_margin" - android:background="?android:selectableItemBackgroundBorderless" - android:contentDescription="@string/notification_feedback_indicator" - android:paddingTop="2dp" - android:scaleType="fitCenter" - android:src="@drawable/ic_feedback_indicator" - android:visibility="gone" - /> - - <ImageView - android:id="@+id/profile_badge" - android:layout_width="@dimen/notification_badge_size" - android:layout_height="@dimen/notification_badge_size" - android:layout_gravity="center" - android:layout_marginStart="4dp" - android:paddingTop="2dp" - android:scaleType="fitCenter" - android:visibility="gone" - android:contentDescription="@string/notification_work_profile_content_description" - /> - - <ImageView - android:id="@+id/alerted_icon" - android:layout_width="@dimen/notification_alerted_size" - android:layout_height="@dimen/notification_alerted_size" - android:layout_gravity="center" - android:layout_marginStart="4dp" - android:contentDescription="@string/notification_alerted_content_description" - android:paddingTop="2dp" - android:scaleType="fitCenter" - android:src="@drawable/ic_notifications_alerted" - android:visibility="gone" - /> - - <LinearLayout - android:id="@+id/app_ops" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:paddingTop="3dp" - android:layout_marginStart="2dp" - android:background="?android:selectableItemBackgroundBorderless" - android:orientation="horizontal" > - <ImageView - android:layout_marginStart="4dp" - android:id="@+id/camera" - android:layout_width="?attr/notificationHeaderIconSize" - android:layout_height="?attr/notificationHeaderIconSize" - android:src="@drawable/ic_camera" - android:visibility="gone" - android:focusable="false" - android:contentDescription="@string/notification_appops_camera_active" - /> - <ImageView - android:id="@+id/mic" - android:layout_width="?attr/notificationHeaderIconSize" - android:layout_height="?attr/notificationHeaderIconSize" - android:src="@drawable/ic_mic" - android:layout_marginStart="4dp" - android:visibility="gone" - android:focusable="false" - android:contentDescription="@string/notification_appops_microphone_active" - /> - <ImageView - android:id="@+id/overlay" - android:layout_width="?attr/notificationHeaderIconSize" - android:layout_height="?attr/notificationHeaderIconSize" - android:src="@drawable/ic_alert_window_layer" - android:layout_marginStart="4dp" - android:visibility="gone" - android:focusable="false" - android:contentDescription="@string/notification_appops_overlay_active" - /> - </LinearLayout> - </LinearLayout> - <!-- Messages --> <com.android.internal.widget.MessagingLinearLayout android:id="@+id/notification_messaging" diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml index 7cda03f8fc4f..7656dd50b2d4 100644 --- a/core/res/res/layout/notification_top_line_views.xml +++ b/core/res/res/layout/notification_top_line_views.xml @@ -26,7 +26,7 @@ android:layout_height="wrap_content" android:layout_marginEnd="@dimen/notification_header_separating_margin" android:singleLine="true" - android:textAppearance="?attr/notificationHeaderTextAppearance" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:visibility="?attr/notificationHeaderAppNameVisibility" /> @@ -34,7 +34,7 @@ android:id="@+id/header_text_secondary_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:layout_marginStart="@dimen/notification_header_separating_margin" android:layout_marginEnd="@dimen/notification_header_separating_margin" android:text="@string/notification_header_divider_symbol" @@ -45,7 +45,7 @@ android:id="@+id/header_text_secondary" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:layout_marginStart="@dimen/notification_header_separating_margin" android:layout_marginEnd="@dimen/notification_header_separating_margin" android:visibility="gone" @@ -56,7 +56,7 @@ android:id="@+id/header_text_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:layout_marginStart="@dimen/notification_header_separating_margin" android:layout_marginEnd="@dimen/notification_header_separating_margin" android:text="@string/notification_header_divider_symbol" @@ -67,7 +67,7 @@ android:id="@+id/header_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:layout_marginStart="@dimen/notification_header_separating_margin" android:layout_marginEnd="@dimen/notification_header_separating_margin" android:visibility="gone" @@ -78,7 +78,7 @@ android:id="@+id/time_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:layout_marginStart="@dimen/notification_header_separating_margin" android:layout_marginEnd="@dimen/notification_header_separating_margin" android:text="@string/notification_header_divider_symbol" diff --git a/core/res/res/values-television/styles_device_defaults.xml b/core/res/res/values-television/styles_device_defaults.xml new file mode 100644 index 000000000000..ef55fc68001f --- /dev/null +++ b/core/res/res/values-television/styles_device_defaults.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog" + parent="Widget.Leanback.Button.ButtonBar" /> +</resources> + diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index e567c3d7d486..586c99d76335 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3251,6 +3251,16 @@ a value of 'true' will not override any 'false' value in its parent chain nor will it prevent any 'false' in any of its children. --> <attr name="forceDarkAllowed" format="boolean" /> + + <!-- <p>Whether the View's Outline should be used to clip the contents of the View. + <p>Only a single non-rectangular clip can be applied on a View at any time. Circular + clips from a + {@link android.view.ViewAnimationUtils#createCircularReveal(View, int, int, float, + float)} circular reveal animation take priority over Outline clipping, and child + Outline clipping takes priority over Outline clipping done by a parent. + <p>Note that this flag will only be respected if the View's Outline returns true from + {@link android.graphics.Outline#canClip()}. --> + <attr name="clipToOutline" format="boolean" /> </declare-styleable> <!-- Attributes that can be assigned to a tag for a particular View. --> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 20a5d379cbcc..5546621b6ee8 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -153,6 +153,11 @@ <color name="notification_action_list_background_color">@null</color> + <!-- The color of the Decline and Hang Up actions on a CallStyle notification --> + <color name="call_notification_decline_color">#d93025</color> + <!-- The color of the Answer action on a CallStyle notification --> + <color name="call_notification_answer_color">#1e8e3e</color> + <!-- Keyguard colors --> <color name="keyguard_avatar_frame_color">#ffffffff</color> <color name="keyguard_avatar_frame_shadow_color">#80000000</color> diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index c8cccc45f634..f7234269f227 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -48,6 +48,8 @@ <color name="text_color_secondary_device_default_dark">@color/system_main_200</color> <color name="text_color_tertiary_device_default_light">@color/system_main_500</color> <color name="text_color_tertiary_device_default_dark">@color/system_main_400</color> + <color name="foreground_device_default_light">@color/text_color_primary_device_default_light</color> + <color name="foreground_device_default_dark">@color/text_color_primary_device_default_dark</color> <!-- Error color --> <color name="error_color_device_default_dark">@color/error_color_material_dark</color> diff --git a/core/res/res/values/colors_leanback.xml b/core/res/res/values/colors_leanback.xml index e52a861e2040..df0be03d6f8e 100644 --- a/core/res/res/values/colors_leanback.xml +++ b/core/res/res/values/colors_leanback.xml @@ -26,4 +26,9 @@ <color name="secondary_text_leanback_light">#99222222</color> <color name="primary_text_leanback_formwizard_default_dark">#ffeeeeee</color> + + <color name="btn_leanback_focused">#E8EAED</color> + <color name="btn_leanback_unfocused">#1AFFFFFF</color> + <color name="btn_text_leanback_focused">#0E0E0E</color> + <color name="btn_text_leanback_unfocused">#E8EAED</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8c5f454d204d..beae9353a10f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4712,4 +4712,10 @@ <!-- Whether to select voice/data/sms preference without user confirmation --> <bool name="config_voice_data_sms_auto_fallback">false</bool> + + <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. --> + <bool name="config_enableOneHandedKeyguard">false</bool> + + <!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot --> + <bool name="config_allow_pin_storage_for_unattended_reboot">true</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index cb16af5a16b0..debdab0b37ba 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -236,9 +236,20 @@ value is calculated in ConversationLayout#updateActionListPadding() --> <dimen name="notification_actions_padding_start">36dp</dimen> + <!-- The start padding to optionally use (e.g. if there's extra space) for CallStyle + notification actions. + this = conversation_content_start (80dp) - button inset (4dp) - action padding (12dp) --> + <dimen name="call_notification_collapsible_indent">64dp</dimen> + <!-- The size of icons for visual actions in the notification_material_action_list --> <dimen name="notification_actions_icon_size">48dp</dimen> + <!-- The size of icons for visual actions in the notification_material_action_list --> + <dimen name="notification_actions_icon_drawable_size">20dp</dimen> + + <!-- The corner radius if the emphasized action buttons in a notification --> + <dimen name="notification_action_button_radius">8dp</dimen> + <!-- Size of the stroke with for the emphasized notification button style --> <dimen name="emphasized_button_stroke_width">1dp</dimen> @@ -383,10 +394,6 @@ <dimen name="alert_dialog_button_bar_width">64dp</dimen> <!-- Dialog button bar height --> <dimen name="alert_dialog_button_bar_height">48dip</dimen> - <!-- Leanback dialog vertical margin --> - <dimen name="leanback_alert_dialog_vertical_margin">27dip</dimen> - <!-- Leanback dialog horizontal margin --> - <dimen name="leanback_alert_dialog_horizontal_margin">54dip</dimen> <!-- Default height of an action bar. --> <dimen name="action_bar_default_height">48dip</dimen> diff --git a/core/res/res/values/dimens_leanback.xml b/core/res/res/values/dimens_leanback.xml index c824a2a79f83..3ab21963ed77 100644 --- a/core/res/res/values/dimens_leanback.xml +++ b/core/res/res/values/dimens_leanback.xml @@ -83,4 +83,10 @@ <dimen name="leanback_setup_translation_backward_out_content_end_v4">@integer/leanback_setup_translation_content_cliff_v4</dimen> <integer name="leanback_setup_translation_backward_out_content_delay">0</integer> <integer name="leanback_setup_translation_backward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer> + + <!-- Button dimens --> + <dimen name="leanback_button_height">42dp</dimen> + <dimen name="leanback_button_radius">55dp</dimen> + <dimen name="leanback_button_padding_horizontal">22dp</dimen> + <dimen name="leanback_button_padding_vertical">11dp</dimen> </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index a4c7293e48b6..ab4e0f3b608e 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -102,6 +102,10 @@ <item type="id" name="selection_end_handle" /> <item type="id" name="insertion_handle" /> <item type="id" name="floating_toolbar_menu_item_image_button" /> + <item type="id" name="camera" /> + <item type="id" name="mic" /> + <item type="id" name="overlay" /> + <item type="id" name="app_ops" /> <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. --> <item type="id" name="accessibilityActionShowOnScreen" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 97ec0f4fa71e..d3f3ebd7c3d9 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3062,6 +3062,7 @@ <!-- @hide @SystemApi --> <public name="hotwordDetectionService" /> <public name="previewLayout" /> + <public name="clipToOutline" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 9b5f67091a2a..af5e406979ad 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5039,6 +5039,18 @@ <!-- Tempalate for Notification.MessagingStyle to join a conversation name with the name of the sender of a message, to make a notification title [CHAR LIMIT=NONE] --> <string name="notification_messaging_title_template"><xliff:g id="conversation_title" example="Tasty Treat Team">%1$s</xliff:g>: <xliff:g id="sender_name" example="Adrian Baker">%2$s</xliff:g></string> + <!-- Action text to be displayed for the "answer" action of an incoming call [CHAR LIMIT=13] --> + <string name="call_notification_answer_action">Answer</string> + <!-- Action text to be displayed for the "decline" action of an incoming call [CHAR LIMIT=13] --> + <string name="call_notification_decline_action">Decline</string> + <!-- Action text to be displayed for the "hang up" action of an ongoing call [CHAR LIMIT=13] --> + <string name="call_notification_hang_up_action">Hang Up</string> + <!-- Default notification text to be displayed in incoming call notifications [CHAR LIMIT=40] --> + <string name="call_notification_incoming_text">Incoming call</string> + <!-- Default notification text to be displayed in ongoing call notifications [CHAR LIMIT=40] --> + <string name="call_notification_ongoing_text">Ongoing call</string> + <!-- Default notification text to be displayed in screening call notifications [CHAR LIMIT=40] --> + <string name="call_notification_screening_text">Screening an incoming call</string> <!-- Label describing the number of selected items [CHAR LIMIT=48] --> <plurals name="selected_count"> diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml index aaeaadd7418d..7eaf36d5366a 100644 --- a/core/res/res/values/styles_leanback.xml +++ b/core/res/res/values/styles_leanback.xml @@ -16,11 +16,34 @@ <resources> <style name="AlertDialog.Leanback" parent="AlertDialog.Material"> <item name="buttonPanelSideLayout">@android:layout/alert_dialog_leanback_button_panel_side</item> + <item name="layout">@android:layout/alert_dialog_leanback</item> </style> <style name="AlertDialog.Leanback.Light"> </style> + <style name="Widget.Leanback.Button" parent="Widget.Material.Button"> + <item name="android:background">@drawable/btn_leanback</item> + <item name="android:textColor">@color/btn_leanback_text_color</item> + <item name="android:layout_height">@dimen/leanback_button_height</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:textAllCaps">false</item> + <item name="android:paddingHorizontal">@dimen/leanback_button_padding_horizontal</item> + <item name="android:paddingVertical">@dimen/leanback_button_padding_vertical</item> + </style> + + <style name="Widget.Leanback.Button.ButtonBar" parent="Widget.Leanback.Button"> + <item name="android:layout_marginStart">10dp</item> + </style> + + <style name="Widget.Leanback.Button.ButtonBarGravityStart" parent="Widget.Leanback.Button"> + <item name="android:layout_marginEnd">10dp</item> + </style> + + <style name="Widget.Leanback.ButtonBar" parent="Widget.Material.ButtonBar"> + <item name="android:padding">?android:attr/dialogPreferredPadding</item> + </style> + <style name="Widget.Leanback.TimePicker" parent="Widget.Material.TimePicker"> <item name="timePickerMode">spinner</item> </style> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c41b78e4a680..4109d4c9f6f9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2647,6 +2647,7 @@ <java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" /> <java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" /> <java-symbol type="attr" name="colorProgressBackgroundNormal" /> + <java-symbol type="bool" name="config_allow_pin_storage_for_unattended_reboot" /> <java-symbol type="layout" name="simple_account_item" /> <java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" /> @@ -3088,8 +3089,26 @@ <!-- TV Remote Service package --> <java-symbol type="string" name="config_tvRemoteServicePackage" /> + + <!-- Notifications: MessagingStyle --> <java-symbol type="string" name="notification_messaging_title_template" /> + <!-- Notifications: CallStyle --> + <java-symbol type="layout" name="notification_template_material_call" /> + <java-symbol type="string" name="call_notification_answer_action" /> + <java-symbol type="string" name="call_notification_decline_action" /> + <java-symbol type="string" name="call_notification_hang_up_action" /> + <java-symbol type="string" name="call_notification_incoming_text" /> + <java-symbol type="string" name="call_notification_ongoing_text" /> + <java-symbol type="string" name="call_notification_screening_text" /> + <java-symbol type="color" name="call_notification_decline_color"/> + <java-symbol type="color" name="call_notification_answer_color"/> + <java-symbol type="dimen" name="call_notification_collapsible_indent"/> + <java-symbol type="drawable" name="ic_call_answer" /> + <java-symbol type="drawable" name="ic_call_decline" /> + <java-symbol type="id" name="verification_icon" /> + <java-symbol type="id" name="verification_text" /> + <!-- Notification handler / dashboard package --> <java-symbol type="string" name="config_notificationHandlerPackage" /> @@ -3415,6 +3434,7 @@ <java-symbol type="dimen" name="notification_media_image_max_width"/> <java-symbol type="dimen" name="notification_media_image_max_height"/> <java-symbol type="dimen" name="notification_right_icon_size"/> + <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/> <java-symbol type="dimen" name="notification_custom_view_max_image_height"/> <java-symbol type="dimen" name="notification_custom_view_max_image_width"/> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index ee17f6f7ce21..ce4ee87e52a0 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -222,6 +222,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> </style> <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" /> @@ -236,6 +238,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -267,6 +271,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -300,6 +306,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -332,6 +340,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -381,6 +391,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -405,6 +417,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -435,6 +449,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -466,6 +482,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -513,6 +531,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -545,6 +565,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -575,6 +597,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -607,6 +631,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -638,6 +664,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -669,6 +697,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -700,6 +730,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -731,6 +763,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -766,6 +800,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Text styles --> <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item> @@ -798,6 +834,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -827,6 +865,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1012,6 +1052,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <item name="colorPopupBackground">?attr/colorBackgroundFloating</item> <item name="panelColorBackground">?attr/colorBackgroundFloating</item> </style> @@ -1027,6 +1069,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1057,6 +1101,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1088,6 +1134,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1121,6 +1169,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1153,6 +1203,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1204,6 +1256,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Progress bar attributes --> <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item> @@ -1227,6 +1281,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1260,6 +1316,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1294,6 +1352,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1329,6 +1389,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. --> @@ -1346,6 +1408,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> </style> <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller @@ -1362,6 +1426,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1397,6 +1463,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1430,6 +1498,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1462,6 +1532,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1493,6 +1565,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1524,6 +1598,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1553,6 +1629,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1672,6 +1750,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> + <item name="colorForeground">@color/foreground_device_default_dark</item> + <item name="colorForegroundInverse">@color/foreground_device_default_light</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1702,6 +1782,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> @@ -1742,6 +1824,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> @@ -1775,6 +1859,8 @@ easier. <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> + <item name="colorForeground">@color/foreground_device_default_light</item> + <item name="colorForegroundInverse">@color/foreground_device_default_dark</item> <!-- Dialog attributes --> <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml index 9dca9128e362..efe582612982 100644 --- a/core/res/res/values/themes_leanback.xml +++ b/core/res/res/values/themes_leanback.xml @@ -22,6 +22,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Light.Dialog" parent="Theme.Material.Light.BaseDialog"> @@ -32,6 +34,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog"> @@ -42,6 +46,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert"> @@ -52,6 +58,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert"> @@ -62,6 +70,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert"> @@ -72,6 +82,8 @@ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item> <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item> <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item> + <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item> + <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item> </style> <style name="Theme.Leanback.Dialog.AppError" parent="Theme.Leanback.Dialog"> diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java index cfcfcc8cf044..ece37f8054cf 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java @@ -201,6 +201,18 @@ public class BundleUtilTest { assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2)); } + @Test + public void testDeepHashCode_differentKeys() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + Bundle b = new Bundle(); + b.putString("key" + i, "value"); + inputs[i] = b; + } + assertThat(BundleUtil.deepHashCode(inputs[0])) + .isNotEqualTo(BundleUtil.deepHashCode(inputs[1])); + } + private static Bundle createThoroughBundle() { Bundle toy1 = new Bundle(); toy1.putString("a", "a"); diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index 45adf833de97..46dbe0fd658a 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -90,12 +90,12 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testMultipleCallsWithIdenticalParametersCacheReference() { Resources resources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources); Resources newResources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(newResources); assertSame(resources, newResources); @@ -104,14 +104,14 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() { Resources resources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources); Configuration overrideConfig = new Configuration(); overrideConfig.smallestScreenWidthDp = 200; Resources newResources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, overrideConfig, + null, APP_ONE_RES_DIR, null, null, null, null, null, overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(newResources); assertNotSame(resources, newResources); @@ -120,12 +120,12 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testAddingASplitCreatesANewImpl() { Resources resources1 = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); Resources resources2 = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null, + null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources2); @@ -137,12 +137,12 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testUpdateConfigurationUpdatesAllAssetManagers() { Resources resources1 = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); Resources resources2 = mResourcesManager.getResources( - null, APP_TWO_RES_DIR, null, null, null, null, null, + null, APP_TWO_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources2); @@ -150,7 +150,7 @@ public class ResourcesManagerTest extends TestCase { final Configuration overrideConfig = new Configuration(); overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE; Resources resources3 = mResourcesManager.getResources( - activity, APP_ONE_RES_DIR, null, null, null, null, + activity, APP_ONE_RES_DIR, null, null, null, null, null, overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources3); @@ -183,13 +183,13 @@ public class ResourcesManagerTest extends TestCase { public void testTwoActivitiesWithIdenticalParametersShareImpl() { Binder activity1 = new Binder(); Resources resources1 = mResourcesManager.getResources( - activity1, APP_ONE_RES_DIR, null, null, null, null, null, + activity1, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); Binder activity2 = new Binder(); Resources resources2 = mResourcesManager.getResources( - activity2, APP_ONE_RES_DIR, null, null, null, null, null, + activity2, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); @@ -204,7 +204,7 @@ public class ResourcesManagerTest extends TestCase { public void testThemesGetUpdatedWithNewImpl() { Binder activity1 = new Binder(); Resources resources1 = mResourcesManager.createBaseTokenResources( - activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, + activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); @@ -237,15 +237,15 @@ public class ResourcesManagerTest extends TestCase { Configuration config1 = new Configuration(); config1.densityDpi = 280; Resources resources1 = mResourcesManager.createBaseTokenResources( - activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); + activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, + config1, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); // Create a Resources based on the Activity. Configuration config2 = new Configuration(); config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES; Resources resources2 = mResourcesManager.getResources( - activity1, APP_ONE_RES_DIR, null, null, null, null, config2, + activity1, APP_ONE_RES_DIR, null, null, null, null, null, config2, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources2); @@ -286,8 +286,8 @@ public class ResourcesManagerTest extends TestCase { final Configuration overrideConfig = new Configuration(); overrideConfig.densityDpi = originalOverrideDensity; final Resources resources = mResourcesManager.createBaseTokenResources( - token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* overlayDirs */, - null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig, + token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* legacyOverlayDirs */, + null /* overlayDirs */,null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */, null /* loaders */); @@ -315,12 +315,12 @@ public class ResourcesManagerTest extends TestCase { // Create a base token resources that are based on the default display. Resources activityResources = mResourcesManager.createBaseTokenResources( - activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, + activity, APP_ONE_RES_DIR, null, null, null,null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); // Create another resources that explicitly override the display of the base token above // and set it to DEFAULT_DISPLAY. Resources defaultDisplayResources = mResourcesManager.getResources( - activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, + activity, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels, diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java index 64906bb27ff0..e16d44854516 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java @@ -100,7 +100,7 @@ public class ResolverListControllerTest { final List<UsageStats> slices = new ArrayList<>(); slices.add(packageStats); ParceledListSlice<UsageStats> stats = new ParceledListSlice<>(slices); - when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString())) + when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString(), anyInt())) .thenReturn(stats); Answer<Void> answer = new Answer<Void>() { @Override diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 143e07a1c8a6..0fac4f79686a 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -45,6 +45,7 @@ import org.junit.runners.Suite; BluetoothPowerCalculatorTest.class, BstatsCpuTimesValidationTest.class, CameraPowerCalculatorTest.class, + CpuPowerCalculatorTest.class, FlashlightPowerCalculatorTest.class, GnssPowerCalculatorTest.class, IdlePowerCalculatorTest.class, @@ -65,6 +66,7 @@ import org.junit.runners.Suite; ScreenPowerCalculatorTest.class, SensorPowerCalculatorTest.class, SystemServicePowerCalculatorTest.class, + UserPowerCalculatorTest.class, VideoPowerCalculatorTest.class, com.android.internal.power.MeasuredEnergyStatsTest.class diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 2c71287fac4a..0ddc4c0035ce 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -28,6 +28,7 @@ import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.SystemBatteryConsumer; import android.os.UidBatteryConsumer; +import android.os.UserBatteryConsumer; import android.util.SparseArray; import androidx.test.InstrumentationRegistry; @@ -76,6 +77,26 @@ public class BatteryUsageStatsRule implements TestRule { return this; } + public BatteryUsageStatsRule setNumCpuClusters(int number) { + when(mPowerProfile.getNumCpuClusters()).thenReturn(number); + return this; + } + + public BatteryUsageStatsRule setNumSpeedStepsInCpuCluster(int cluster, int speeds) { + when(mPowerProfile.getNumSpeedStepsInCpuCluster(cluster)).thenReturn(speeds); + return this; + } + + public BatteryUsageStatsRule setAveragePowerForCpuCluster(int cluster, double value) { + when(mPowerProfile.getAveragePowerForCpuCluster(cluster)).thenReturn(value); + return this; + } + + public BatteryUsageStatsRule setAveragePowerForCpuCore(int cluster, int step, double value) { + when(mPowerProfile.getAveragePowerForCpuCore(cluster, step)).thenReturn(value); + return this; + } + public void setNetworkStats(NetworkStats networkStats) { mBatteryStats.setNetworkStats(networkStats); } @@ -113,15 +134,21 @@ public class BatteryUsageStatsRule implements TestRule { mMockClocks.uptime = uptimeUs; } - void apply(PowerCalculator calculator) { + void apply(PowerCalculator... calculators) { + apply(BatteryUsageStatsQuery.DEFAULT, calculators); + } + + void apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) { BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0); SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats(); for (int i = 0; i < uidStats.size(); i++) { builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); } - calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime, - BatteryUsageStatsQuery.DEFAULT, null); + for (PowerCalculator calculator : calculators) { + calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime, + query); + } mBatteryUsageStats = builder.build(); } @@ -144,4 +171,13 @@ public class BatteryUsageStatsRule implements TestRule { } return null; } + + public UserBatteryConsumer getUserBatteryConsumer(int userId) { + for (UserBatteryConsumer ubc : mBatteryUsageStats.getUserBatteryConsumers()) { + if (ubc.getUserId() == userId) { + return ubc; + } + } + return null; + } } diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java new file mode 100644 index 000000000000..9cf0d375ff51 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.os; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class CpuPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 272; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720) + .setNumCpuClusters(2) + .setNumSpeedStepsInCpuCluster(0, 2) + .setNumSpeedStepsInCpuCluster(1, 2) + .setAveragePowerForCpuCluster(0, 360) + .setAveragePowerForCpuCluster(1, 480) + .setAveragePowerForCpuCore(0, 0, 300) + .setAveragePowerForCpuCore(0, 1, 400) + .setAveragePowerForCpuCore(1, 0, 500) + .setAveragePowerForCpuCore(1, 1, 600); + + private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{ + mock(KernelCpuSpeedReader.class), + mock(KernelCpuSpeedReader.class), + }; + + @Mock + private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader; + @Mock + private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mStatsRule.getBatteryStats() + .setUserInfoProvider(mMockUserInfoProvider) + .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders) + .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) + .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader) + .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader) + .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader) + .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader); + } + + @Test + public void testTimerBasedModel() { + when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); + + when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000}); + when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000}); + + when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false); + + // User/System CPU time + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0); + // User/system time in microseconds + callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000}); + callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000}); + return null; + }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any()); + + // Active CPU time + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0); + callback.onUidCpuTime(APP_UID1, 1111L); + callback.onUidCpuTime(APP_UID2, 3333L); + return null; + }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any()); + + // Per-cluster CPU time + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0); + callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222}); + callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444}); + return null; + }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any()); + + mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true); + + mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234); + mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345); + + CpuPowerCalculator calculator = + new CpuPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1); + assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + .isEqualTo(3333); + assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) + .isWithin(PRECISION).of(1.092233); + assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); + + UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); + assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + .isEqualTo(7777); + assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) + .isWithin(PRECISION).of(2.672322); + assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java new file mode 100644 index 000000000000..6fa1d3bae0f7 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.BatteryUsageStatsQuery; +import android.os.Process; +import android.os.UidBatteryConsumer; +import android.os.UserBatteryConsumer; +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class UserPowerCalculatorTest { + public static final int USER1 = 0; + public static final int USER2 = 1625; + + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 272; + private static final int APP_UID3 = Process.FIRST_APPLICATION_UID + 314; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + + @Test + public void testAllUsers() { + prepareUidBatteryConsumers(); + + UserPowerCalculator calculator = new UserPowerCalculator(); + + mStatsRule.apply(BatteryUsageStatsQuery.DEFAULT, calculator, new FakeAudioPowerCalculator(), + new FakeVideoPowerCalculator()); + + assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull(); + + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000); + + assertThat(mStatsRule.getUserBatteryConsumer(USER2)).isNull(); + + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(5555); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(9999); + } + + @Test + public void testSpecificUser() { + prepareUidBatteryConsumers(); + + UserPowerCalculator calculator = new UserPowerCalculator(); + + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().addUser(UserHandle.of(USER1)).build(), + calculator, new FakeAudioPowerCalculator(), new FakeVideoPowerCalculator()); + + assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull(); + + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID2))).isNull(); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(7070); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3)) + .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(11110); + + UserBatteryConsumer user2 = mStatsRule.getUserBatteryConsumer(USER2); + assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)) + .isEqualTo(15308); + assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)) + .isEqualTo(24196); + + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID1))).isNull(); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))).isNull(); + assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID3))).isNull(); + } + + private void prepareUidBatteryConsumers() { + prepareUidBatteryConsumer(USER1, APP_UID1, 1000, 2000, 3000, 4000); + prepareUidBatteryConsumer(USER2, APP_UID2, 2222, 3333, 4444, 5555); + prepareUidBatteryConsumer(USER1, APP_UID3, 3030, 4040, 5050, 6060); + prepareUidBatteryConsumer(USER2, APP_UID3, 4321, 5432, 6543, 7654); + } + + private void prepareUidBatteryConsumer(int userId, int uid, long audioDuration1Ms, + long audioDuration2Ms, long videoDuration1Ms, long videoDuration2Ms) { + BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(UserHandle.getUid(userId, uid)); + + // Use "audio" and "video" to fake some power consumption. Could be any other type of usage. + uidStats.noteAudioTurnedOnLocked(0); + uidStats.noteAudioTurnedOffLocked(audioDuration1Ms); + uidStats.noteAudioTurnedOnLocked(1000000); + uidStats.noteAudioTurnedOffLocked(1000000 + audioDuration2Ms); + + uidStats.noteVideoTurnedOnLocked(0); + uidStats.noteVideoTurnedOffLocked(videoDuration1Ms); + uidStats.noteVideoTurnedOnLocked(2000000); + uidStats.noteVideoTurnedOffLocked(2000000 + videoDuration2Ms); + } + + private static class FakeAudioPowerCalculator extends PowerCalculator { + @Override + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + long durationMs = u.getAudioTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO, durationMs / 1000); + } + } + + private static class FakeVideoPowerCalculator extends PowerCalculator { + @Override + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + long durationMs = u.getVideoTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO, durationMs / 1000); + } + } +} diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index cb4dd9e8cacd..b70fa0e693c2 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -46,7 +46,6 @@ import java.io.File; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Optional; import java.util.concurrent.Executor; import java.util.stream.Stream; @@ -1148,24 +1147,14 @@ public class HardwareRenderer { // Default to SRGB if the display doesn't support wide color .orElse(Dataspace.SRGB); - float maxRefreshRate = - (float) Arrays.stream(display.getSupportedModes()) - .mapToDouble(Mode::getRefreshRate) - .max() - .orElseGet(() -> { - Log.i(LOG_TAG, "Failed to find the maximum display refresh rate"); - // Assume that the max refresh rate is 60hz if we can't find one. - return 60.0; - }); // Grab the physical screen dimensions from the active display mode // Strictly speaking the screen resolution may not always be constant - it is for // sizing the font cache for the underlying rendering thread. Since it's a // heuristic we don't need to be always 100% correct. Mode activeMode = display.getMode(); nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(), - display.getRefreshRate(), maxRefreshRate, - wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(), - display.getPresentationDeadlineNanos()); + display.getRefreshRate(), wideColorDataspace.mNativeDataspace, + display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos()); // Defensively clear out the context mContext = null; @@ -1324,6 +1313,5 @@ public class HardwareRenderer { private static native void nSetDisplayDensityDpi(int densityDpi); private static native void nInitDisplayInfo(int width, int height, float refreshRate, - float maxRefreshRate, int wideColorDataspace, long appVsyncOffsetNanos, - long presentationDeadlineNanos); + int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos); } diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java index 372add9b7ecb..d188b6525579 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -190,7 +190,7 @@ public class KeyStoreSecurityLevel { keyDescriptor.blob = wrappedKey; keyDescriptor.domain = wrappedKeyDescriptor.domain; - return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor, + return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor, wrappingKeyDescriptor, maskingKey, args.toArray(new KeyParameter[args.size()]), authenticatorSpecs)); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index 16bf5469296f..087151711138 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -434,14 +434,16 @@ public class AndroidKeyStoreProvider extends Provider { @NonNull public static java.security.KeyStore getKeyStoreForUid(int uid) throws KeyStoreException, NoSuchProviderException { - String providerName = PROVIDER_NAME; + final java.security.KeyStore.LoadStoreParameter loadParameter; if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) { - providerName = "AndroidKeyStoreLegacy"; + loadParameter = new android.security.keystore2.AndroidKeyStoreLoadStoreParameter( + KeyProperties.legacyUidToNamespace(uid)); + } else { + loadParameter = new AndroidKeyStoreLoadStoreParameter(uid); } - java.security.KeyStore result = - java.security.KeyStore.getInstance(providerName); + java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME); try { - result.load(new AndroidKeyStoreLoadStoreParameter(uid)); + result.load(loadParameter); } catch (NoSuchAlgorithmException | CertificateException | IOException e) { throw new KeyStoreException( "Failed to load AndroidKeyStore KeyStore for UID " + uid, e); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java index 8475ad9fd57b..0f777495a3fe 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java @@ -164,6 +164,9 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC List<KeyParameter> parameters = new ArrayList<>(); parameters.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN + )); + parameters.add(KeyStore2ParameterUtils.makeEnum( KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC )); parameters.add(KeyStore2ParameterUtils.makeEnum( diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java index 32650aeda1b1..5619585d9c3c 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java @@ -21,7 +21,6 @@ import android.security.KeyStoreSecurityLevel; import android.system.keystore2.Authorization; import android.system.keystore2.Domain; import android.system.keystore2.KeyDescriptor; -import android.util.Log; import java.security.Key; @@ -127,15 +126,6 @@ public class AndroidKeyStoreKey implements Key { return false; } - // If the key ids are equal and the class matches all the other fields cannot differ - // unless we have a bug. - if (!mAlgorithm.equals(other.mAlgorithm) - || !mAuthorizations.equals(other.mAuthorizations) - || !mDescriptor.equals(other.mDescriptor)) { - Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata" - + "differs."); - return false; - } return true; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 8c8acc418a0e..39607aeb3852 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -866,7 +866,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { try { response = mKeyStore.getKeyEntry(wrappingkey); } catch (android.security.KeyStoreException e) { - throw new KeyStoreException("Failed to load wrapping key.", e); + throw new KeyStoreException("Failed to import wrapped key. Keystore error code: " + + e.getErrorCode(), e); } KeyDescriptor wrappedKey = makeKeyDescriptor(alias); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 40fdb97b0094..0ee1f0642352 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -398,6 +398,14 @@ public class Bubble implements BubbleViewProvider { } } + @Override + public void setExpandedContentAlpha(float alpha) { + if (mExpandedView != null) { + mExpandedView.setAlpha(alpha); + mExpandedView.setTaskViewAlpha(alpha); + } + } + /** * Set visibility of bubble in the expanded state. * @@ -407,7 +415,7 @@ public class Bubble implements BubbleViewProvider { * and setting {@code false} actually means rendering the expanded view in transparent. */ @Override - public void setContentVisibility(boolean visibility) { + public void setTaskViewVisibility(boolean visibility) { if (mExpandedView != null) { mExpandedView.setContentVisibility(visibility); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 29458ef70e2d..21004280c952 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -85,6 +85,19 @@ public class BubbleExpandedView extends LinearLayout { private boolean mImeVisible; private boolean mNeedsNewHeight; + /** + * Whether we want the TaskView's content to be visible (alpha = 1f). If + * {@link #mIsAlphaAnimating} is true, this may not reflect the TaskView's actual alpha value + * until the animation ends. + */ + private boolean mIsContentVisible = false; + + /** + * Whether we're animating the TaskView's alpha value. If so, we will hold off on applying alpha + * changes from {@link #setContentVisibility} until the animation ends. + */ + private boolean mIsAlphaAnimating = false; + private int mMinHeight; private int mOverflowHeight; private int mSettingsIconHeight; @@ -465,6 +478,29 @@ public class BubbleExpandedView extends LinearLayout { } /** + * Whether we are currently animating the TaskView's alpha value. If this is set to true, calls + * to {@link #setContentVisibility} will not be applied until this is set to false again. + */ + void setAlphaAnimating(boolean animating) { + mIsAlphaAnimating = animating; + + // If we're done animating, apply the correct + if (!animating) { + setContentVisibility(mIsContentVisible); + } + } + + /** + * Sets the alpha of the underlying TaskView, since changing the expanded view's alpha does not + * affect the TaskView since it uses a Surface. + */ + void setTaskViewAlpha(float alpha) { + if (mTaskView != null) { + mTaskView.setAlpha(alpha); + } + } + + /** * Set visibility of contents in the expanded state. * * @param visibility {@code true} if the contents should be visible on the screen. @@ -477,16 +513,19 @@ public class BubbleExpandedView extends LinearLayout { Log.d(TAG, "setContentVisibility: visibility=" + visibility + " bubble=" + getBubbleKey()); } + mIsContentVisible = visibility; + final float alpha = visibility ? 1f : 0f; mPointerView.setAlpha(alpha); - if (mTaskView != null) { + if (mTaskView != null && !mIsAlphaAnimating) { mTaskView.setAlpha(alpha); } } + @Nullable - View getTaskView() { + TaskView getTaskView() { return mTaskView; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index 16cd3cf3686c..51d63cff385a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -167,8 +167,12 @@ class BubbleOverflow( return dotPath } - override fun setContentVisibility(visible: Boolean) { - expandedView?.setContentVisibility(visible) + override fun setExpandedContentAlpha(alpha: Float) { + expandedView?.alpha = alpha + } + + override fun setTaskViewVisibility(visible: Boolean) { + // Overflow does not have a TaskView. } override fun getIconView(): BadgedImageView? { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index d54be0e62527..a3edc20e242a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -43,7 +43,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.Bundle; -import android.os.Handler; import android.provider.Settings; import android.util.Log; import android.view.Choreographer; @@ -123,6 +122,10 @@ public class BubbleStackView extends FrameLayout @VisibleForTesting static final int FLYOUT_HIDE_AFTER = 5000; + private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f; + + private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150; + /** * How long to wait to animate the stack temporarily invisible after a drag/flyout hide * animation ends, if we are in fact temporarily invisible. @@ -142,7 +145,7 @@ public class BubbleStackView extends FrameLayout private final PhysicsAnimator.SpringConfig mTranslateSpringConfig = new PhysicsAnimator.SpringConfig( - SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY); + SpringForce.STIFFNESS_VERY_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY); /** * Handler to use for all delayed animations - this way, we can easily cancel them before @@ -211,6 +214,9 @@ public class BubbleStackView extends FrameLayout /** Container for the animating-out SurfaceView. */ private FrameLayout mAnimatingOutSurfaceContainer; + /** Animator for animating the alpha value of the animating out SurfaceView. */ + private final ValueAnimator mAnimatingOutSurfaceAlphaAnimator = ValueAnimator.ofFloat(0f, 1f); + /** * Buffer containing a screenshot of the animating-out bubble. This is drawn into the * SurfaceView during animations. @@ -261,6 +267,12 @@ public class BubbleStackView extends FrameLayout /** Whether we're in the middle of dragging the stack around by touch. */ private boolean mIsDraggingStack = false; + /** Whether the expanded view has been hidden, because we are dragging out a bubble. */ + private boolean mExpandedViewHidden = false; + + /** Animator for animating the expanded view's alpha (including the TaskView inside it). */ + private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f); + /** * The pointer index of the ACTION_DOWN event we received prior to an ACTION_UP. We'll ignore * touches from other pointer indices. @@ -614,6 +626,12 @@ public class BubbleStackView extends FrameLayout // Show the dismiss target, if we haven't already. mDismissView.show(); + if (mIsExpanded && mExpandedBubble != null && v.equals(mExpandedBubble.getIconView())) { + // Hide the expanded view if we're dragging out the expanded bubble, and we haven't + // already hidden it. + hideExpandedViewIfNeeded(); + } + // First, see if the magnetized object consumes the event - if so, we shouldn't move the // bubble since it's stuck to the target. if (!passEventToMagnetizedObject(ev)) { @@ -645,6 +663,9 @@ public class BubbleStackView extends FrameLayout if (!passEventToMagnetizedObject(ev)) { if (mBubbleData.isExpanded()) { mExpandedAnimationController.snapBubbleBack(v, velX, velY); + + // Re-show the expanded view if we hid it. + showExpandedViewIfNeeded(); } else { // Fling the stack to the edge, and save whether or not it's going to end up on // the left side of the screen. @@ -937,6 +958,46 @@ public class BubbleStackView extends FrameLayout animate() .setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED) .setDuration(FADE_IN_DURATION); + + mExpandedViewAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION); + mExpandedViewAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED); + mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + // We need to be Z ordered on top in order for alpha animations to work. + mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true); + mExpandedBubble.getExpandedView().setAlphaAnimating(true); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false); + mExpandedBubble.getExpandedView().setAlphaAnimating(false); + } + } + }); + mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> { + if (mExpandedBubble != null) { + mExpandedBubble.setExpandedContentAlpha((float) valueAnimator.getAnimatedValue()); + } + }); + + mAnimatingOutSurfaceAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION); + mAnimatingOutSurfaceAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED); + mAnimatingOutSurfaceAlphaAnimator.addUpdateListener(valueAnimator -> { + if (!mExpandedViewHidden) { + mAnimatingOutSurfaceView.setAlpha((float) valueAnimator.getAnimatedValue()); + } + }); + mAnimatingOutSurfaceAlphaAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + releaseAnimatingOutBubbleBuffer(); + } + }); } /** @@ -1539,7 +1600,8 @@ public class BubbleStackView extends FrameLayout // If we're expanded, screenshot the currently expanded bubble (before expanding the newly // selected bubble) so we can animate it out. - if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null + && !mExpandedViewHidden) { if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { // Before screenshotting, have the real ActivityView show on top of other surfaces // so that the screenshot doesn't flicker on top of it. @@ -1575,7 +1637,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setAlpha(0.0f); mSurfaceSynchronizer.syncSurfaceAndRun(() -> { if (previouslySelected != null) { - previouslySelected.setContentVisibility(false); + previouslySelected.setTaskViewVisibility(false); } updateExpandedBubble(); @@ -1656,6 +1718,58 @@ public class BubbleStackView extends FrameLayout requestUpdate(); } + /** Animate the expanded view hidden. This is done while we're dragging out a bubble. */ + private void hideExpandedViewIfNeeded() { + if (mExpandedViewHidden + || mExpandedBubble == null + || mExpandedBubble.getExpandedView() == null) { + return; + } + + mExpandedViewHidden = true; + + // Scale down. + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) + .spring(AnimatableScaleMatrix.SCALE_X, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor( + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT), + mScaleOutSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_Y, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor( + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT), + mScaleOutSpringConfig) + .addUpdateListener((target, values) -> + mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix)) + .start(); + + // Animate alpha from 1f to 0f. + mExpandedViewAlphaAnimator.reverse(); + } + + /** + * Animate the expanded view visible again. This is done when we're done dragging out a bubble. + */ + private void showExpandedViewIfNeeded() { + if (!mExpandedViewHidden) { + return; + } + + mExpandedViewHidden = false; + + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) + .spring(AnimatableScaleMatrix.SCALE_X, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleOutSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_Y, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleOutSpringConfig) + .addUpdateListener((target, values) -> + mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix)) + .start(); + + mExpandedViewAlphaAnimator.start(); + } + private void animateExpansion() { cancelDelayedExpandCollapseSwitchAnimations(); final boolean showVertically = mPositioner.showBubblesVertically(); @@ -1714,7 +1828,7 @@ public class BubbleStackView extends FrameLayout // Should not happen since we lay out before expanding, but just in case... if (getWidth() > 0) { startDelay = (long) - (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION + (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 1.2f + (distanceAnimated / getWidth()) * 30); } @@ -1728,20 +1842,29 @@ public class BubbleStackView extends FrameLayout pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; } mExpandedViewContainerMatrix.setScale( - 0f, 0f, + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, pivotX, pivotY); } else { mExpandedViewContainerMatrix.setScale( - 0f, 0f, - bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY()); + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, + bubbleWillBeAt + mBubbleSize / 2f, + getExpandedViewY()); } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); - if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { - mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false); + if (mExpandedBubble.getExpandedView() != null) { + mExpandedBubble.setExpandedContentAlpha(0f); + + // We'll be starting the alpha animation after a slight delay, so set this flag early + // here. + mExpandedBubble.getExpandedView().setAlphaAnimating(true); } mDelayedAnimationExecutor.executeDelayed(() -> { + mExpandedViewAlphaAnimator.start(); + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) .spring(AnimatableScaleMatrix.SCALE_X, @@ -1796,22 +1919,15 @@ public class BubbleStackView extends FrameLayout // since we're about to animate collapsed. mExpandedAnimationController.notifyPreparingToCollapse(); - final long startDelay = - (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f); - mDelayedAnimationExecutor.executeDelayed(() -> { - mExpandedAnimationController.collapseBackToStack( - mStackAnimationController.getStackPositionAlongNearestHorizontalEdge() - /* collapseTo */, - () -> mBubbleContainer.setActiveController(mStackAnimationController)); - }, startDelay); + mExpandedAnimationController.collapseBackToStack( + mStackAnimationController.getStackPositionAlongNearestHorizontalEdge() + /* collapseTo */, + () -> mBubbleContainer.setActiveController(mStackAnimationController)); if (mTaskbarScrim.getVisibility() == VISIBLE) { mTaskbarScrim.animate().alpha(0f).start(); } - // We want to visually collapse into this bubble during the animation. - final View expandingFromBubble = mExpandedBubble.getIconView(); - int index; if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) { index = mBubbleData.getBubbles().size(); @@ -1840,31 +1956,25 @@ public class BubbleStackView extends FrameLayout getExpandedViewY()); } + mExpandedViewAlphaAnimator.reverse(); + + // When the animation completes, we should no longer be showing the content. + if (mExpandedBubble.getExpandedView() != null) { + mExpandedBubble.getExpandedView().setContentVisibility(false); + } + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) - .spring(AnimatableScaleMatrix.SCALE_X, 0f, mScaleOutSpringConfig) - .spring(AnimatableScaleMatrix.SCALE_Y, 0f, mScaleOutSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_X, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor( + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT), + mScaleOutSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_Y, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor( + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT), + mScaleOutSpringConfig) .addUpdateListener((target, values) -> { - if (expandingFromBubble != null) { - // Follow the bubble as it translates! - if (showVertically) { - mExpandedViewContainerMatrix.postTranslate( - 0f, expandingFromBubble.getTranslationY() - - expandingFromBubbleAt); - } else { - mExpandedViewContainerMatrix.postTranslate( - expandingFromBubble.getTranslationX() - - expandingFromBubbleAt, 0f); - } - } - mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); - - // Hide early so we don't have a tiny little expanded view still visible at the - // end of the scale animation. - if (mExpandedViewContainerMatrix.getScaleX() < 0.05f) { - mExpandedViewContainer.setVisibility(View.INVISIBLE); - } }) .withEndActions(() -> { final BubbleViewProvider previouslySelected = mExpandedBubble; @@ -1882,7 +1992,7 @@ public class BubbleStackView extends FrameLayout updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */); afterExpandedViewAnimation(); if (previouslySelected != null) { - previouslySelected.setContentVisibility(false); + previouslySelected.setTaskViewVisibility(false); } if (mPositioner.showingInTaskbar()) { @@ -1903,22 +2013,21 @@ public class BubbleStackView extends FrameLayout // The surface contains a screenshot of the animating out bubble, so we just need to animate // it out (and then release the GraphicBuffer). PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel(); - PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer) - .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig) - .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig) - .withEndActions(this::releaseAnimatingOutBubbleBuffer); + + mAnimatingOutSurfaceAlphaAnimator.reverse(); + mExpandedViewAlphaAnimator.start(); if (mPositioner.showBubblesVertically()) { float translationX = mStackAnimationController.isStackOnLeftSide() ? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2 : mAnimatingOutSurfaceContainer.getTranslationX(); - animator.spring(DynamicAnimation.TRANSLATION_X, - translationX, - mTranslateSpringConfig) + PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer) + .spring(DynamicAnimation.TRANSLATION_X, translationX, mTranslateSpringConfig) .start(); } else { - animator.spring(DynamicAnimation.TRANSLATION_Y, - mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2, + PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer) + .spring(DynamicAnimation.TRANSLATION_Y, + mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize, mTranslateSpringConfig) .start(); } @@ -1947,7 +2056,8 @@ public class BubbleStackView extends FrameLayout pivotX, pivotY); } else { mExpandedViewContainerMatrix.setScale( - 0f, 0f, + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, expandingFromBubbleDestination + mBubbleSize / 2f, getExpandedViewY()); } @@ -1972,10 +2082,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); }) .withEndActions(() -> { - if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { - mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false); - } - + mExpandedViewHidden = false; mIsBubbleSwitchAnimating = false; }) .start(); @@ -2489,6 +2596,7 @@ public class BubbleStackView extends FrameLayout && mExpandedBubble.getExpandedView() != null) { BubbleExpandedView bev = mExpandedBubble.getExpandedView(); bev.setContentVisibility(false); + bev.setAlphaAnimating(!mIsExpansionAnimating); mExpandedViewContainerMatrix.setScaleX(0f); mExpandedViewContainerMatrix.setScaleY(0f); mExpandedViewContainerMatrix.setTranslate(0f, 0f); @@ -2586,7 +2694,14 @@ public class BubbleStackView extends FrameLayout mAnimatingOutBubbleBuffer.getHardwareBuffer(), mAnimatingOutBubbleBuffer.getColorSpace()); - mSurfaceSynchronizer.syncSurfaceAndRun(() -> post(() -> onComplete.accept(true))); + mAnimatingOutSurfaceView.setAlpha(1f); + mExpandedViewContainer.setVisibility(View.GONE); + + mSurfaceSynchronizer.syncSurfaceAndRun(() -> { + post(() -> { + onComplete.accept(true); + }); + }); }); } @@ -2618,7 +2733,9 @@ public class BubbleStackView extends FrameLayout } } mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0); - mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); + if (mIsExpansionAnimating) { + mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); + } if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { mExpandedViewContainer.setTranslationY(getExpandedViewY()); mExpandedViewContainer.setTranslationX(0f); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java index ec900be13658..da4259c42558 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java @@ -29,7 +29,16 @@ import androidx.annotation.Nullable; public interface BubbleViewProvider { @Nullable BubbleExpandedView getExpandedView(); - void setContentVisibility(boolean visible); + /** + * Sets the alpha of the expanded view content. This will be applied to both the expanded view + * container itself (the manage button, etc.) as well as the TaskView within it. + */ + void setExpandedContentAlpha(float alpha); + + /** + * Sets whether the contents of the bubble's TaskView should be visible. + */ + void setTaskViewVisibility(boolean visible); @Nullable View getIconView(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 728f60d93497..87f0c25c93df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -39,6 +39,7 @@ import android.view.IWindow; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; import android.view.WindowManager; import android.view.WindowlessWindowManager; @@ -55,6 +56,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { private final ParentContainerCallbacks mParentContainerCallbacks; private Context mContext; private SurfaceControlViewHost mViewHost; + private SurfaceControl mLeash; private boolean mResizingSplits; private final String mWindowName; @@ -88,7 +90,15 @@ public final class SplitWindowManager extends WindowlessWindowManager { @Override protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - mParentContainerCallbacks.attachToParentSurface(b); + // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName(TAG) + .setHidden(false) + .setCallsite("SplitWindowManager#attachToParentSurface"); + mParentContainerCallbacks.attachToParentSurface(builder); + mLeash = builder.build(); + b.setParent(mLeash); } /** Inflates {@link DividerView} on to the root surface. */ @@ -118,9 +128,15 @@ public final class SplitWindowManager extends WindowlessWindowManager { * hierarchy. */ void release() { - if (mViewHost == null) return; - mViewHost.release(); - mViewHost = null; + if (mViewHost != null){ + mViewHost.release(); + mViewHost = null; + } + + if (mLeash != null) { + new SurfaceControl.Transaction().remove(mLeash).apply(); + mLeash = null; + } } void setResizingSplits(boolean resizing) { @@ -139,6 +155,6 @@ public final class SplitWindowManager extends WindowlessWindowManager { */ @Nullable SurfaceControl getSurfaceControl() { - return mViewHost == null ? null : getSurfaceControl(mViewHost.getWindowToken()); + return mLeash; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index 177646b22ea3..7ca569349633 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -106,6 +106,8 @@ public interface SplitScreen extends DragAndDropPolicy.Starter { /** Removes the split-screen stages. */ void exitSplitScreen(); + /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */ + void exitSplitScreenOnHide(boolean exitSplitScreenOnHide); /** Gets the stage bounds. */ void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index bbad36dcc046..b0167afa2e4e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -126,6 +126,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter { mStageCoordinator.exitSplitScreen(); } + public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { + mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide); + } + public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds); } @@ -292,6 +296,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter { } @Override + public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { + mMainExecutor.execute(() -> { + SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide); + }); + } + + @Override public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { try { mMainExecutor.executeBlocking(() -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 176852b148fa..e44c820a656a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -79,6 +79,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, private DisplayAreaInfo mDisplayAreaInfo; private final Context mContext; private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>(); + private boolean mExitSplitScreenOnHide = true; StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) { @@ -113,7 +114,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, boolean moveToSideStage(ActivityManager.RunningTaskInfo task, @SplitScreen.StagePosition int sideStagePosition) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStagePosition = sideStagePosition; + setSideStagePosition(sideStagePosition); mMainStage.activate(getMainStageBounds(), wct); mSideStage.addTask(task, getSideStageBounds(), wct); mTaskOrganizer.applyTransaction(wct); @@ -144,14 +145,18 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) { + if (mSideStagePosition == sideStagePosition) return; + mSideStagePosition = sideStagePosition; if (mSideStageListener.mVisible) { onStageVisibilityChanged(mSideStageListener); } + + sendOnStagePositionChanged(); } void setSideStageVisibility(boolean visible) { - if (!mSideStageListener.mVisible == visible) return; + if (mSideStageListener.mVisible == visible) return; final WindowContainerTransaction wct = new WindowContainerTransaction(); mSideStage.setVisibility(visible, wct); @@ -162,6 +167,10 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, exitSplitScreen(null /* childrenToTop */); } + void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { + mExitSplitScreenOnHide = exitSplitScreenOnHide; + } + private void exitSplitScreen(StageTaskListener childrenToTop) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mSideStage.removeAllTasks(wct, childrenToTop == mSideStage); @@ -202,6 +211,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mListeners.remove(listener); } + private void sendOnStagePositionChanged() { + for (int i = mListeners.size() - 1; i >= 0; --i) { + final SplitScreen.SplitScreenListener l = mListeners.get(i); + l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition()); + l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition()); + } + } + private void onStageChildTaskStatusChanged( StageListenerImpl stageListener, int taskId, boolean present) { @@ -251,7 +268,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } } - if (!mainStageVisible && !sideStageVisible) { + if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) { // Exit split-screen if both stage are not visible. // TODO: This is only a temporary request from UX and is likely to be removed soon... exitSplitScreen(); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt new file mode 100644 index 000000000000..1869d833fbc4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:JvmName("Utils") +package com.android.wm.shell.flicker + +import android.app.ActivityTaskManager +import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT +import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED + +fun removeAllTasksButHome() { + val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf( + ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, + ACTIVITY_TYPE_UNDEFINED) + val atm = ActivityTaskManager.getService() + atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME) +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt index 7ec22bb9db1c..cac46fe676b3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt @@ -32,13 +32,12 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( /** * Opens the IME and wait for it to be displayed * - * @param device UIDevice instance to interact with the device * @param wmHelper Helper used to wait for WindowManager states */ @JvmOverloads - open fun openIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) { + open fun openIME(wmHelper: WindowManagerStateHelper? = null) { if (!isTelevision) { - val editText = device.wait( + val editText = uiDevice.wait( Until.findObject(By.res(getPackage(), "plain_text_input")), FIND_TIMEOUT) @@ -47,7 +46,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( "was left in an unknown state (e.g. in split screen)" } editText.click() - waitAndAssertIMEShown(device, wmHelper) + waitAndAssertIMEShown(uiDevice, wmHelper) } else { // If we do the same thing as above - editText.click() - on TV, that's going to force TV // into the touch mode. We really don't want that. @@ -69,16 +68,15 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( /** * Opens the IME and wait for it to be gone * - * @param device UIDevice instance to interact with the device * @param wmHelper Helper used to wait for WindowManager states */ @JvmOverloads - open fun closeIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) { + open fun closeIME(wmHelper: WindowManagerStateHelper? = null) { if (!isTelevision) { - device.pressBack() + uiDevice.pressBack() // Using only the AccessibilityInfo it is not possible to identify if the IME is active if (wmHelper == null) { - device.waitForIdle() + uiDevice.waitForIdle() } else { require(wmHelper.waitImeWindowGone()) { "IME did did not close" } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt index b90e865de691..111362a93495 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt @@ -17,17 +17,20 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation +import android.graphics.Point import android.media.session.MediaController import android.media.session.MediaSessionManager import android.os.SystemClock import androidx.test.uiautomator.By import androidx.test.uiautomator.BySelector +import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE import com.android.server.wm.flicker.helpers.closePipWindow -import com.android.server.wm.flicker.helpers.hasPipWindow +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild +import com.android.wm.shell.flicker.pip.waitPipWindowGone +import com.android.wm.shell.flicker.pip.waitPipWindowShown import com.android.wm.shell.flicker.testapp.Components -import org.junit.Assert.fail class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( instrumentation, @@ -55,6 +58,17 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( } } + /** {@inheritDoc} */ + override fun launchViaIntent( + wmHelper: WindowManagerStateHelper, + expectedWindowName: String, + action: String?, + stringExtras: Map<String, String> + ) { + super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras) + wmHelper.waitPipWindowShown() + } + private fun focusOnObject(selector: BySelector): Boolean { // We expect all the focusable UI elements to be arranged in a way so that it is possible // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top" @@ -69,16 +83,12 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( return false } - fun clickEnterPipButton() { + @JvmOverloads + fun clickEnterPipButton(wmHelper: WindowManagerStateHelper? = null) { clickObject(ENTER_PIP_BUTTON_ID) - // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs - if (!isTelevision) { - uiDevice.hasPipWindow() - } else { - // Simply wait for 3 seconds - SystemClock.sleep(3_000) - } + // Wait on WMHelper or simply wait for 3 seconds + wmHelper?.waitPipWindowShown() ?: SystemClock.sleep(3_000) } fun clickStartMediaSessionButton() { @@ -97,16 +107,75 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( fun stopMedia() = mediaController?.transportControls?.stop() ?: error("No active media session found") + @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead", + ReplaceWith("closePipWindow(wmHelper)")) fun closePipWindow() { if (isTelevision) { uiDevice.closeTvPipWindow() } else { uiDevice.closePipWindow() } + } + + /** + * Expands the pip window and dismisses it by clicking on the X button. + * + * Note, currently the View coordinates reported by the accessibility are relative to + * the window, so the correct coordinates need to be calculated + * + * For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the + * dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in + * Point(716, 66), instead of Point(970, 1403) + * + * See b/179337864 + */ + fun closePipWindow(wmHelper: WindowManagerStateHelper) { + if (isTelevision) { + uiDevice.closeTvPipWindow() + } else { + expandPipWindow(wmHelper) + val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss")) + requireNotNull(exitPipObject) { "PIP window dismiss button not found" } + val coordinatesInWindow = exitPipObject.visibleBounds + val windowOffset = wmHelper.getWindowRegion(component).bounds + val newCoordinates = Point(windowOffset.left + coordinatesInWindow.centerX(), + windowOffset.top + coordinatesInWindow.centerY()) + uiDevice.click(newCoordinates.x, newCoordinates.y) + } + + // Wait for animation to complete. + wmHelper.waitPipWindowGone() + wmHelper.waitForHomeActivityVisible() + } + + /** + * Click once on the PIP window to expand it + */ + fun expandPipWindow(wmHelper: WindowManagerStateHelper) { + val windowRegion = wmHelper.getWindowRegion(component) + require(!windowRegion.isEmpty) { + "Unable to find a PIP window in the current state" + } + val windowRect = windowRegion.bounds + uiDevice.click(windowRect.centerX(), windowRect.centerY()) + // Ensure WindowManagerService wait until all animations have completed + wmHelper.waitForAppTransitionIdle() + mInstrumentation.uiAutomation.syncInputTransactions() + } - if (!waitUntilClosed()) { - fail("Couldn't close Pip") + /** + * Double click on the PIP window to reopen to app + */ + fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) { + val windowRegion = wmHelper.getWindowRegion(component) + require(!windowRegion.isEmpty) { + "Unable to find a PIP window in the current state" } + val windowRect = windowRegion.bounds + uiDevice.click(windowRect.centerX(), windowRect.centerY()) + uiDevice.click(windowRect.centerX(), windowRect.centerY()) + wmHelper.waitPipWindowGone() + wmHelper.waitForAppTransitionIdle() } companion object { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt index 2015f4941cea..bc42d5ed04ce 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt @@ -16,11 +16,6 @@ package com.android.wm.shell.flicker.pip -import android.app.ActivityTaskManager -import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT -import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS -import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD -import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED import android.os.SystemClock import com.android.wm.shell.flicker.NonRotationTestBase @@ -29,14 +24,6 @@ abstract class AppTestBase( rotation: Int ) : NonRotationTestBase(rotationName, rotation) { companion object { - fun removeAllTasksButHome() { - val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf( - ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, - ACTIVITY_TYPE_UNDEFINED) - val atm = ActivityTaskManager.getService() - atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME) - } - fun waitForAnimationComplete() { // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. // Consider to introduce WindowManagerStateHelper to access Activity state. diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt index 5a3d18d9feef..a14b46ef7a3d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -16,21 +16,21 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Presubmit +import android.os.Bundle import android.view.Surface import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.dsl.runFlicker +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.wm.shell.flicker.helpers.PipAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,82 +39,67 @@ import org.junit.runners.Parameterized * Test Pip launch and exit. * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterExitPipTest( - rotationName: String, - rotation: Int -) : AppTestBase(rotationName, rotation) { - private val pipApp = PipAppHelper(instrumentation) - private val testApp = FixedAppHelper(instrumentation) - - @Test - fun testDisplayMetricsPinUnpin() { - runFlicker(instrumentation) { - withTestName { "testDisplayMetricsPinUnpin" } - setup { - test { - removeAllTasksButHome() - device.wakeUpAndGoToHomeScreen() - pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true")) - testApp.launchViaIntent() - waitForAnimationComplete() - } - } - transitions { - // This will bring PipApp to fullscreen - pipApp.launchViaIntent() - waitForAnimationComplete() - } - teardown { - test { - removeAllTasksButHome() - } - } - assertions { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - windowManagerTrace { - all("pipApp must remain inside visible bounds") { - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) - } - all("Initially shows both app windows then pipApp hides testApp") { - showsAppWindow(testApp.defaultWindowName) - .showsAppWindowOnTop(pipApp.defaultWindowName) - .then() - .hidesAppWindow(testApp.defaultWindowName) + testSpec: FlickerTestRunnerFactory.TestSpec +) : FlickerTestRunner(testSpec) { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<Array<Any>> { + val testApp = FixedAppHelper(instrumentation) + val baseConfig = getTransitionLaunch(eachRun = true) + val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> + setup { + eachRun { + testApp.launchViaIntent(wmHelper) } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() } - layersTrace { - all("Initially shows both app layers then pipApp hides testApp") { - showsLayer(testApp.defaultWindowName) - .showsLayer(pipApp.defaultWindowName) - .then() - .hidesLayer(testApp.defaultWindowName) - } - start("testApp covers the fullscreen, pipApp remains inside display") { - hasVisibleRegion(testApp.defaultWindowName, displayBounds) - coversAtMostRegion(displayBounds, pipApp.defaultWindowName) - } - end("pipApp covers the fullscreen") { - hasVisibleRegion(pipApp.defaultWindowName, displayBounds) + transitions { + // This will bring PipApp to fullscreen + pipApp.launchViaIntent(wmHelper) + } + assertions { + val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation) + presubmit { + windowManagerTrace { + all("pipApp must remain inside visible bounds") { + coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + } + all("Initially shows both app windows then pipApp hides testApp") { + showsAppWindow(testApp.defaultWindowName) + .showsAppWindowOnTop(pipApp.defaultWindowName) + .then() + .hidesAppWindow(testApp.defaultWindowName) + } + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + layersTrace { + all("Initially shows both app layers then pipApp hides testApp") { + showsLayer(testApp.defaultWindowName) + .showsLayer(pipApp.defaultWindowName) + .then() + .hidesLayer(testApp.defaultWindowName) + } + start("testApp covers the fullscreen, pipApp remains inside display") { + hasVisibleRegion(testApp.defaultWindowName, displayBounds) + coversAtMostRegion(displayBounds, pipApp.defaultWindowName) + } + end("pipApp covers the fullscreen") { + hasVisibleRegion(pipApp.defaultWindowName, displayBounds) + } + navBarLayerIsAlwaysVisible() + statusBarLayerIsAlwaysVisible() + } } - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() } } - } - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig, + testSpec, supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index af62eb9ae40d..99a40daa027f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -16,18 +16,13 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.closePipWindow -import com.android.server.wm.flicker.helpers.expandPipWindow -import com.android.server.wm.flicker.helpers.hasPipWindow -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible @@ -35,9 +30,7 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -50,80 +43,58 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 152738416) class EnterPipTest( testSpec: FlickerTestRunnerFactory.TestSpec ) : FlickerTestRunner(testSpec) { - companion object { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = PipAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0)) { configuration -> - withTestName { buildTestTag("enterPip", testApp, configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - device.pressHome() - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - if (device.hasPipWindow()) { - device.closePipWindow() - } - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - test { - if (device.hasPipWindow()) { - device.closePipWindow() - } - } - } - transitions { - testApp.clickEnterPipButton() - device.expandPipWindow() - } - assertions { + val baseConfig = getTransitionLaunch( + eachRun = true, stringExtras = emptyMap()) + val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> + transitions { + pipApp.clickEnterPipButton() + pipApp.expandPipWindow(wmHelper) + } + assertions { + presubmit { windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() all("pipWindowBecomesVisible") { - this.showsAppWindow(testApp.`package`) - .then() - .showsAppWindow(PIP_WINDOW_TITLE) + this.showsAppWindow(pipApp.defaultWindowName) } } layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0, - enabled = false) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) statusBarLayerRotatesScales(configuration.startRotation, Surface.ROTATION_0) } layersTrace { all("pipLayerBecomesVisible") { - this.showsLayer(testApp.launcherName) - .then() - .showsLayer(PIP_WINDOW_TITLE) + this.showsLayer(pipApp.launcherName) } } } + + flaky { + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) + navBarLayerRotatesAndScales(configuration.startRotation, + Surface.ROTATION_0, bugId = 140855415) + } + } } + } + + return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig, + testSpec, supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt new file mode 100644 index 000000000000..eaaa2f6390be --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT +import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP +import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip with orientation changes. + * To run this test: `atest WMShellFlickerTests:PipOrientationTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class EnterPipToOtherOrientationTest( + testSpec: FlickerTestRunnerFactory.TestSpec +) : FlickerTestRunner(testSpec) { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { + private val testApp = FixedAppHelper(instrumentation) + + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, + supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) { configuration -> + setupAndTeardown(this, configuration) + + setup { + eachRun { + // Launch a portrait only app on the fullscreen stack + testApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())) + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) + } + } + teardown { + eachRun { + pipApp.exit() + testApp.exit() + } + } + transitions { + // Enter PiP, and assert that the PiP is within bounds now that the device is back + // in portrait + broadcastActionTrigger.doAction(ACTION_ENTER_PIP) + wmHelper.waitPipWindowShown() + wmHelper.waitForAppTransitionIdle() + } + assertions { + val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) + val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + + presubmit { + windowManagerTrace { + all("pipApp window is always on top") { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + start("pipApp window hides testApp") { + isInvisible(testApp.defaultWindowName) + } + end("testApp windows is shown") { + isVisible(testApp.defaultWindowName) + } + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + + layersTrace { + start("pipApp layer hides testApp") { + hasVisibleRegion(pipApp.defaultWindowName, startingBounds) + isInvisible(testApp.defaultWindowName) + } + } + } + + flaky { + layersTrace { + end("testApp layer covers fullscreen") { + hasVisibleRegion(testApp.defaultWindowName, endingBounds) + } + navBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt new file mode 100644 index 000000000000..707d28d9c4c0 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.app.WindowConfiguration +import android.content.ComponentName +import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject +import com.android.server.wm.traces.common.windowmanager.WindowManagerState +import com.android.server.wm.traces.parser.toWindowName +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper +import com.google.common.truth.Truth + +inline val WindowManagerState.pinnedWindows + get() = visibleWindows + .filter { it.windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED } + +/** + * Checks if the state has any window in PIP mode + */ +fun WindowManagerState.hasPipWindow(): Boolean = pinnedWindows.isNotEmpty() + +/** + * Checks that an activity [activity] is in PIP mode + */ +fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean { + val windowName = activity.toWindowName() + return pinnedWindows.any { it.title == windowName } +} + +/** + * Asserts that an activity [activity] exists and is in PIP mode + */ +fun WindowManagerStateSubject.isInPipMode( + activity: ComponentName +): WindowManagerStateSubject = apply { + val windowName = activity.toWindowName() + hasWindow(windowName) + val pinnedWindows = wmState.pinnedWindows + .map { it.title } + Truth.assertWithMessage("Window not in PIP mode") + .that(pinnedWindows) + .contains(windowName) +} + +/** + * Waits until the state has a window in PIP mode, i.e., with + * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED + */ +fun WindowManagerStateHelper.waitPipWindowShown(): Boolean = + waitFor("PIP window shown") { + val result = it.wmState.hasPipWindow() + result + } + +/** + * Waits until the state doesn't have a window in PIP mode, i.e., with + * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED + */ +fun WindowManagerStateHelper.waitPipWindowGone(): Boolean = + waitFor("PIP window gone") { + val result = !it.wmState.hasPipWindow() + result + } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index c21b594246b9..7576e24ace19 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -16,21 +16,19 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Presubmit +import android.os.Bundle import android.view.Surface import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.dsl.runWithFlicker import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.closePipWindow -import com.android.server.wm.flicker.helpers.hasPipWindow -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.IME_WINDOW_NAME import com.android.wm.shell.flicker.helpers.ImeAppHelper -import com.android.wm.shell.flicker.testapp.Components import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,114 +37,61 @@ import org.junit.runners.Parameterized * Test Pip launch. * To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipKeyboardTest( - rotationName: String, - rotation: Int -) : PipTestBase(rotationName, rotation) { - private val keyboardApp = ImeAppHelper(instrumentation) - private val keyboardComponent = Components.ImeActivity.COMPONENT - private val helper = WindowManagerStateHelper() +class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { + private const val TAG_IME_VISIBLE = "imeIsVisible" - private val keyboardScenario: FlickerBuilder - get() = FlickerBuilder(instrumentation).apply { - repeat { TEST_REPETITIONS } - // disable layer tracing - withLayerTracing { null } - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.pressHome() - // launch our target pip app - testApp.launchViaIntent(wmHelper) - this.setRotation(rotation) - testApp.clickEnterPipButton() - // open an app with an input field and a keyboard - // UiAutomator doesn't support to launch the multiple Activities in a task. - // So use launchActivity() for the Keyboard Activity. - keyboardApp.launchViaIntent() - helper.waitForAppTransitionIdle() - helper.waitForFullScreenApp(keyboardComponent) - } - } - teardown { - test { - keyboardApp.exit() - - if (device.hasPipWindow()) { - device.closePipWindow() + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val imeApp = ImeAppHelper(instrumentation) + val baseConfig = getTransitionLaunch(eachRun = false) + val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> + setup { + test { + imeApp.launchViaIntent(wmHelper) + setRotation(configuration.startRotation) } - testApp.exit() - this.setRotation(Surface.ROTATION_0) } - } - } - - /** Ensure the pip window remains visible throughout any keyboard interactions. */ - @Test - fun pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose() { - val testTag = "pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose" - runWithFlicker(keyboardScenario) { - withTestName { testTag } - transitions { - // open the soft keyboard - keyboardApp.openIME(device, wmHelper) - helper.waitImeWindowShown() - - // then close it again - keyboardApp.closeIME(device, wmHelper) - helper.waitImeWindowGone() - } - assertions { - windowManagerTrace { - all("PiP window must remain inside visible bounds") { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - coversAtMostRegion(testApp.defaultWindowName, displayBounds) + teardown { + test { + imeApp.exit() + setRotation(Surface.ROTATION_0) } } - } - } - } + transitions { + // open the soft keyboard + imeApp.openIME(wmHelper) + createTag(TAG_IME_VISIBLE) - /** Ensure the pip window does not obscure the keyboard. */ - @Test - fun pipWindow_doesNotObscure_keyboard() { - val testTag = "pipWindow_doesNotObscure_keyboard" - runWithFlicker(keyboardScenario) { - withTestName { testTag } - transitions { - // open the soft keyboard - keyboardApp.openIME(device, wmHelper) - helper.waitImeWindowShown() - } - teardown { - eachRun { - // close the keyboard - keyboardApp.closeIME(device, wmHelper) - helper.waitImeWindowGone() + // then close it again + imeApp.closeIME(wmHelper) } - } - assertions { - windowManagerTrace { - end("imeWindowAboveApp") { - isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName) + assertions { + presubmit { + windowManagerTrace { + // Ensure the pip window remains visible throughout + // any keyboard interactions + all("pipInVisibleBounds") { + val displayBounds = WindowUtils.getDisplayBounds( + configuration.startRotation) + coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + } + // Ensure that the pip window does not obscure the keyboard + tag(TAG_IME_VISIBLE) { + isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName) + } + } } } } - } - } - - companion object { - private const val TEST_REPETITIONS = 5 - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, + baseConfig, testSpec, supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt index e5790962c025..f10bd7f1e45a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt @@ -32,6 +32,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.removeAllTasksButHome import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt deleted file mode 100644 index 5e0760ceeda7..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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.wm.shell.flicker.pip - -import android.content.Intent -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.dsl.runFlicker -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP -import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION -import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP -import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION -import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION -import org.junit.Assert.assertEquals -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test Pip with orientation changes. - * To run this test: `atest WMShellFlickerTests:PipOrientationTest` - */ -@Presubmit -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipOrientationTest( - rotationName: String, - rotation: Int -) : AppTestBase(rotationName, rotation) { - // Helper class to process test actions by broadcast. - private inner class BroadcastActionTrigger { - private fun createIntentWithAction(broadcastAction: String): Intent { - return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND) - } - fun doAction(broadcastAction: String) { - instrumentation.getContext().sendBroadcast(createIntentWithAction(broadcastAction)) - } - fun requestOrientationForPip(orientation: Int) { - instrumentation.getContext() - .sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION) - .putExtra(EXTRA_PIP_ORIENTATION, orientation.toString())) - } - } - private val broadcastActionTrigger = BroadcastActionTrigger() - - // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE - private val ORIENTATION_LANDSCAPE = 0 - // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - private val ORIENTATION_PORTRAIT = 1 - - private val testApp = FixedAppHelper(instrumentation) - private val pipApp = PipAppHelper(instrumentation) - - @Test - fun testEnterPipToOtherOrientation() { - runFlicker(instrumentation) { - withTestName { "testEnterPipToOtherOrientation" } - setup { - test { - removeAllTasksButHome() - device.wakeUpAndGoToHomeScreen() - // Launch a portrait only app on the fullscreen stack - testApp.launchViaIntent(stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())) - waitForAnimationComplete() - // Launch the PiP activity fixed as landscape - pipApp.launchViaIntent(stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) - waitForAnimationComplete() - } - } - transitions { - // Enter PiP, and assert that the PiP is within bounds now that the device is back - // in portrait - broadcastActionTrigger.doAction(ACTION_ENTER_PIP) - waitForAnimationComplete() - } - teardown { - test { - removeAllTasksButHome() - } - } - assertions { - windowManagerTrace { - all("pipApp window is always on top") { - showsAppWindowOnTop(pipApp.defaultWindowName) - } - start("pipApp window hides testApp") { - isInvisible(testApp.defaultWindowName) - } - end("testApp windows is shown") { - isVisible(testApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) - start("pipApp layer hides testApp") { - hasVisibleRegion(pipApp.defaultWindowName, startingBounds) - isInvisible(testApp.defaultWindowName) - } - end("testApp layer covers fullscreen", enabled = false) { - hasVisibleRegion(testApp.defaultWindowName, endingBounds) - } - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - } - } - } - } - - @Test - fun testSetRequestedOrientationWhilePinned() { - runFlicker(instrumentation) { - withTestName { "testSetRequestedOrientationWhilePinned" } - setup { - test { - removeAllTasksButHome() - device.wakeUpAndGoToHomeScreen() - // Launch the PiP activity fixed as landscape - pipApp.launchViaIntent(stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), - EXTRA_ENTER_PIP to "true")) - waitForAnimationComplete() - assertEquals(Surface.ROTATION_0, device.displayRotation) - } - } - transitions { - // Request that the orientation is set to landscape - broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) - - // Launch the activity back into fullscreen and ensure that it is now in landscape - pipApp.launchViaIntent() - waitForAnimationComplete() - assertEquals(Surface.ROTATION_90, device.displayRotation) - } - teardown { - test { - removeAllTasksButHome() - } - } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) - val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - windowManagerTrace { - start("PIP window must remain inside display") { - coversAtMostRegion(pipApp.defaultWindowName, startingBounds) - } - end("pipApp shows on top") { - showsAppWindowOnTop(pipApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - start("PIP layer must remain inside display") { - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) - } - end("pipApp layer covers fullscreen") { - hasVisibleRegion(pipApp.defaultWindowName, endingBounds) - } - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - } - } - } - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index a00c5f463a50..adab5e81b32d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -16,21 +16,18 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Presubmit +import android.os.Bundle import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.wm.shell.flicker.helpers.PipAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -38,7 +35,6 @@ import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -48,80 +44,77 @@ import org.junit.runners.Parameterized * Test Pip Stack in bounds after rotations. * To run this test: `atest WMShellFlickerTests:PipRotationTest` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class PipRotationTest( testSpec: FlickerTestRunnerFactory.TestSpec ) : FlickerTestRunner(testSpec) { - companion object { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = FixedAppHelper(instrumentation) - val pipApp = PipAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)) { - configuration -> - withTestName { buildTestTag("PipRotationTest", testApp, configuration) } - repeat { configuration.repetitions } - setup { - test { - AppTestBase.removeAllTasksButHome() - device.wakeUpAndGoToHomeScreen() - pipApp.launchViaIntent(stringExtras = mapOf( - EXTRA_ENTER_PIP to "true")) - testApp.launchViaIntent() - AppTestBase.waitForAnimationComplete() - } - eachRun { - setRotation(configuration.startRotation) - } - } - transitions { - setRotation(configuration.endRotation) + val fixedApp = FixedAppHelper(instrumentation) + val baseConfig = getTransitionLaunch(eachRun = false) + val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> + setup { + test { + fixedApp.launchViaIntent(wmHelper) + } + eachRun { + setRotation(configuration.startRotation) + } + } + transitions { + setRotation(configuration.endRotation) + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) + } + } + assertions { + val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation) + val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation) + + presubmit { + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() } - teardown { - eachRun { - setRotation(Surface.ROTATION_0) - } - test { - AppTestBase.removeAllTasksButHome() - } + + layersTrace { + noUncoveredRegions(configuration.startRotation, + configuration.endRotation, allStates = false) } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false) - navBarLayerRotatesAndScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) - statusBarLayerRotatesScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) + } + + flaky { + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + navBarLayerRotatesAndScales(configuration.startRotation, + configuration.endRotation, bugId = 140855415) + statusBarLayerRotatesScales(configuration.startRotation, + configuration.endRotation, bugId = 140855415) + + start("appLayerRotates_StartingBounds", bugId = 140855415) { + hasVisibleRegion(fixedApp.defaultWindowName, startingBounds) + coversAtMostRegion(startingBounds, pipApp.defaultWindowName) } - layersTrace { - val startingBounds = WindowUtils.getDisplayBounds( - configuration.startRotation) - val endingBounds = WindowUtils.getDisplayBounds( - configuration.endRotation) - start("appLayerRotates_StartingBounds", bugId = 140855415) { - hasVisibleRegion(testApp.defaultWindowName, startingBounds) - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) - } - end("appLayerRotates_EndingBounds", bugId = 140855415) { - hasVisibleRegion(testApp.defaultWindowName, endingBounds) - coversAtMostRegion(endingBounds, pipApp.defaultWindowName) - } + end("appLayerRotates_EndingBounds", bugId = 140855415) { + hasVisibleRegion(fixedApp.defaultWindowName, endingBounds) + coversAtMostRegion(endingBounds, pipApp.defaultWindowName) } } } + } + } + + return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation, + baseConfig, testSpec, + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), + repetitions = 5) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt index 3e7eb134e627..4b826ffd646d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt @@ -16,29 +16,23 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.closePipWindow -import com.android.server.wm.flicker.helpers.expandPipWindow -import com.android.server.wm.flicker.helpers.hasPipWindow import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -51,48 +45,30 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 152738416) class PipToAppTest( testSpec: FlickerTestRunnerFactory.TestSpec ) : FlickerTestRunner(testSpec) { - companion object { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = PipAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0)) { configuration -> - withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.pressHome() - testApp.launchViaIntent(wmHelper) - } - eachRun { - this.setRotation(configuration.startRotation) - testApp.clickEnterPipButton() - device.hasPipWindow() - } - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - if (device.hasPipWindow()) { - device.closePipWindow() - } - testApp.exit() - } + val baseConfig = getTransitionLaunch(eachRun = true) + val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> + setup { + eachRun { + this.setRotation(configuration.startRotation) } - transitions { - device.expandPipWindow() - device.waitForIdle() + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) } - assertions { + } + transitions { + pipApp.expandPipWindowToApp(wmHelper) + } + assertions { + presubmit { windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() @@ -100,34 +76,42 @@ class PipToAppTest( all("appReplacesPipWindow") { this.showsAppWindow(PIP_WINDOW_TITLE) .then() - .showsAppWindowOnTop(testApp.launcherName) + .showsAppWindowOnTop(pipApp.launcherName) } } layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0, - enabled = false) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) statusBarLayerRotatesScales(configuration.startRotation, Surface.ROTATION_0) all("appReplacesPipLayer") { this.showsLayer(PIP_WINDOW_TITLE) .then() - .showsLayer(testApp.launcherName) + .showsLayer(pipApp.launcherName) } } + } + + flaky { + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) + navBarLayerRotatesAndScales(configuration.startRotation, + Surface.ROTATION_0, bugId = 140855415) + } eventLog { focusChanges( - "NexusLauncherActivity", testApp.launcherName, + "NexusLauncherActivity", pipApp.launcherName, "NexusLauncherActivity", bugId = 151179149) } } } + } + + return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig, + testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt index 5d3bc1388686..62e82212b1d1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt @@ -16,28 +16,23 @@ package com.android.wm.shell.flicker.pip +import android.os.Bundle import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.closePipWindow -import com.android.server.wm.flicker.helpers.hasPipWindow import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -50,50 +45,30 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 152738416) class PipToHomeTest( testSpec: FlickerTestRunnerFactory.TestSpec ) : FlickerTestRunner(testSpec) { - companion object { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = PipAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0)) { configuration -> - withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.pressHome() - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - testApp.clickEnterPipButton() - device.hasPipWindow() - } - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - if (device.hasPipWindow()) { - device.closePipWindow() - } - } - test { - if (device.hasPipWindow()) { - device.closePipWindow() - } - testApp.exit() - } + val baseConfig = getTransitionLaunch(eachRun = true) + val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> + setup { + eachRun { + this.setRotation(configuration.startRotation) } - transitions { - testApp.closePipWindow() + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) } - assertions { + } + transitions { + pipApp.closePipWindow(wmHelper) + } + assertions { + presubmit { windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() @@ -106,12 +81,7 @@ class PipToHomeTest( } layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0, - enabled = false) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) statusBarLayerRotatesScales(configuration.startRotation, Surface.ROTATION_0) @@ -121,13 +91,28 @@ class PipToHomeTest( .hidesLayer(PIP_WINDOW_TITLE) } } + } + postsubmit { + layersTrace { + navBarLayerIsAlwaysVisible() + noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) + navBarLayerRotatesAndScales(configuration.startRotation, + Surface.ROTATION_0) + } + } + + flaky { eventLog { - focusChanges(testApp.launcherName, "NexusLauncherActivity", + focusChanges(pipApp.launcherName, "NexusLauncherActivity", bugId = 151179149) } } } + } + + return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig, + testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt new file mode 100644 index 000000000000..eb7bae160577 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.app.Instrumentation +import android.content.Intent +import android.os.Bundle +import android.view.Surface +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.closePipWindow +import com.android.server.wm.flicker.helpers.hasPipWindow +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions +import com.android.wm.shell.flicker.helpers.PipAppHelper +import com.android.wm.shell.flicker.removeAllTasksButHome +import com.android.wm.shell.flicker.testapp.Components + +abstract class PipTransitionBase(protected val instrumentation: Instrumentation) { + // Helper class to process test actions by broadcast. + protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) { + private fun createIntentWithAction(broadcastAction: String): Intent { + return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + } + + fun doAction(broadcastAction: String) { + instrumentation.context + .sendBroadcast(createIntentWithAction(broadcastAction)) + } + + fun requestOrientationForPip(orientation: Int) { + instrumentation.context.sendBroadcast( + createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION) + .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString()) + ) + } + + companion object { + // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + @JvmStatic + val ORIENTATION_LANDSCAPE = 0 + + // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + @JvmStatic + val ORIENTATION_PORTRAIT = 1 + } + } + + protected val pipApp = PipAppHelper(instrumentation) + protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) + + /** + * Gets a configuration that handles basic setup and teardown of pip tests + */ + protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit + get() = { configuration -> + withTestName { buildTestTag(configuration) } + repeat { configuration.repetitions } + setup { + test { + removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + } + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) + } + test { + removeAllTasksButHome() + + if (device.hasPipWindow()) { + device.closePipWindow() + } + pipApp.exit() + } + } + } + + /** + * Gets a configuration that handles basic setup and teardown of pip tests and that + * launches the Pip app for test + * + * @param eachRun If the pip app should be launched in each run (otherwise only 1x per test) + * @param stringExtras Arguments to pass to the PIP launch intent + */ + @JvmOverloads + fun getTransitionLaunch( + eachRun: Boolean, + stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true") + ): FlickerBuilder.(Bundle) -> Unit { + return { configuration -> + setupAndTeardown(this, configuration) + + setup { + test { + removeAllTasksButHome() + if (!eachRun) { + pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras) + wmHelper.waitPipWindowShown() + } + } + eachRun { + if (eachRun) { + pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras) + wmHelper.waitPipWindowShown() + } + } + } + teardown { + eachRun { + if (eachRun) { + pipApp.exit() + } + } + test { + if (!eachRun) { + pipApp.exit() + } + removeAllTasksButHome() + } + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt new file mode 100644 index 000000000000..c01bc94151e9 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -0,0 +1,115 @@ +/* + * 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.wm.shell.flicker.pip + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION +import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP +import org.junit.Assert.assertEquals +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip with orientation changes. + * To run this test: `atest WMShellFlickerTests:PipOrientationTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class SetRequestedOrientationWhilePinnedTest( + testSpec: FlickerTestRunnerFactory.TestSpec +) : FlickerTestRunner(testSpec) { + companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, + supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 1) { configuration -> + setupAndTeardown(this, configuration) + + setup { + eachRun { + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), + EXTRA_ENTER_PIP to "true")) + } + } + teardown { + eachRun { + pipApp.exit() + } + } + transitions { + // Request that the orientation is set to landscape + broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) + + // Launch the activity back into fullscreen and + // ensure that it is now in landscape + pipApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(pipApp.component) + wmHelper.waitForRotation(Surface.ROTATION_90) + assertEquals(Surface.ROTATION_90, device.displayRotation) + } + assertions { + val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) + presubmit { + windowManagerTrace { + start("PIP window must remain inside display") { + coversAtMostRegion(pipApp.defaultWindowName, startingBounds) + } + end("pipApp shows on top") { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + layersTrace { + start("PIP layer must remain inside display") { + coversAtMostRegion(startingBounds, pipApp.defaultWindowName) + } + end("pipApp layer covers fullscreen") { + hasVisibleRegion(pipApp.defaultWindowName, endingBounds) + } + } + } + + flaky { + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 27be62269959..d5fee3f667a9 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -35,7 +35,6 @@ class DeviceInfo { public: static DeviceInfo* get(); - static float getMaxRefreshRate() { return get()->mMaxRefreshRate; } static int32_t getWidth() { return get()->mWidth; } static int32_t getHeight() { return get()->mHeight; } // Gets the density in density-independent pixels @@ -45,7 +44,6 @@ public: static int64_t getAppOffset() { return get()->mAppVsyncOffsetNanos; } // Sets the density in density-independent pixels static void setDensity(float density) { sDensity.store(density); } - static void setMaxRefreshRate(float refreshRate) { get()->mMaxRefreshRate = refreshRate; } static void setWidth(int32_t width) { get()->mWidth = width; } static void setHeight(int32_t height) { get()->mHeight = height; } static void setRefreshRate(float refreshRate) { @@ -91,7 +89,6 @@ private: SkColorType mWideColorType = SkColorType::kN32_SkColorType; int mDisplaysSize = 0; int mPhysicalDisplayIndex = -1; - float mMaxRefreshRate = 60.0; int32_t mWidth = 1080; int32_t mHeight = 1920; int64_t mVsyncPeriod = 16666666; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index ca2ada9e8141..b14ade97ca5f 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -54,7 +54,7 @@ SkBlendMode Layer::getMode() const { } static inline SkScalar isIntegerAligned(SkScalar x) { - return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON; + return MathUtils::isZero(roundf(x) - x); } // Disable filtering when there is no scaling in screen coordinates and the corners have the same diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index e798f2a2bc69..971a53a8b2dc 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -79,7 +79,6 @@ bool Properties::debuggingEnabled = false; bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; -int Properties::defaultRenderAhead = -1; float Properties::defaultSdrWhitePoint = 200.f; bool Properties::load() { @@ -129,10 +128,6 @@ bool Properties::load() { runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false); - defaultRenderAhead = std::max( - -1, - std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(-1)))); - return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 1639143ef87c..dcb79babad24 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -162,8 +162,6 @@ enum DebugLevel { */ #define PROPERTY_QEMU_KERNEL "ro.kernel.qemu" -#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead" - /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// @@ -247,8 +245,6 @@ public: static int contextPriority; - static int defaultRenderAhead; - static float defaultSdrWhitePoint; private: diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index a146b64e29cc..4966bfa1c1e9 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -603,14 +603,12 @@ static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass, static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth, jint physicalHeight, jfloat refreshRate, - jfloat maxRefreshRate, jint wideColorDataspace, jlong appVsyncOffsetNanos, jlong presentationDeadlineNanos) { DeviceInfo::setWidth(physicalWidth); DeviceInfo::setHeight(physicalHeight); DeviceInfo::setRefreshRate(refreshRate); - DeviceInfo::setMaxRefreshRate(maxRefreshRate); DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace)); DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos); DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos); @@ -735,7 +733,7 @@ static const JNINativeMethod gMethods[] = { {"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark}, {"nSetDisplayDensityDpi", "(I)V", (void*)android_view_ThreadedRenderer_setDisplayDensityDpi}, - {"nInitDisplayInfo", "(IIFFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo}, + {"nInitDisplayInfo", "(IIFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo}, {"preload", "()V", (void*)android_view_ThreadedRenderer_preload}, }; diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 3392dac02493..c8471a9b8feb 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -219,7 +219,7 @@ static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { } // Critical Native -static jlong Font_getReleaseNativeFontFunc() { +static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) { return reinterpret_cast<jlong>(releaseFont); } diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index 80964794efb2..b68213549938 100644 --- a/libs/hwui/jni/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -101,13 +101,13 @@ static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) { } // CriticalNative -static jint FontFamily_getFontSize(jlong familyPtr) { +static jint FontFamily_getFontSize(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) { FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); return family->family->getNumFonts(); } // CriticalNative -static jlong FontFamily_getFont(jlong familyPtr, jint index) { +static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint index) { FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); std::shared_ptr<minikin::Font> font = family->family->getFontRef(index); return reinterpret_cast<jlong>(new FontWrapper(std::move(font))); diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index 34df5ddbb210..471a7f7af3b1 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -34,7 +34,7 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { } static inline SkScalar isIntegerAligned(SkScalar x) { - return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON; + return MathUtils::isZero(roundf(x) - x); } // Disable filtering when there is no scaling in screen coordinates and the corners have the same diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 3b8caeb3aab1..7cfccb56382c 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -52,7 +52,7 @@ void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) { RenderNodeDrawable* childNode = mChildren[drawIndex]; SkASSERT(childNode); const float casterZ = childNode->getNodeProperties().getZ(); - if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z + if (casterZ >= -MathUtils::NON_ZERO_EPSILON) { // draw only children with negative Z return; } SkAutoCanvasRestore acr(canvas, true); @@ -86,7 +86,7 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) { const size_t endIndex = zChildren.size(); while (drawIndex < endIndex // draw only children with positive Z - && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) + && zChildren[drawIndex]->getNodeProperties().getZ() <= MathUtils::NON_ZERO_EPSILON) drawIndex++; size_t shadowIndex = drawIndex; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 85924c5e8939..d998e5031984 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -101,7 +101,8 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { return; } - mGrContext->flushAndSubmit(); + // flush and submit all work to the gpu and wait for it to finish + mGrContext->flushAndSubmit(/*syncCpu=*/true); switch (mode) { case TrimMemoryMode::Complete: @@ -119,11 +120,6 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); break; } - - // We must sync the cpu to make sure deletions of resources still queued up on the GPU actually - // happen. - mGrContext->flush({}); - mGrContext->submit(true); } void CacheManager::trimStaleResources() { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 37a6ee71c4a6..65afcc3a2558 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -108,7 +108,6 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); mProfiler.setDensity(DeviceInfo::getDensity()); - setRenderAheadDepth(Properties::defaultRenderAhead); } CanvasContext::~CanvasContext() { @@ -157,24 +156,17 @@ static void setBufferCount(ANativeWindow* window) { void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { ATRACE_CALL(); - if (mFixedRenderAhead) { - mRenderAheadCapacity = mRenderAheadDepth; - } else { - if (DeviceInfo::get()->getMaxRefreshRate() > 66.6f) { - mRenderAheadCapacity = 1; - } else { - mRenderAheadCapacity = 0; - } - } - if (window) { + int extraBuffers = 0; + native_window_get_extra_buffer_count(window, &extraBuffers); + mNativeSurface = std::make_unique<ReliableSurface>(window); mNativeSurface->init(); if (enableTimeout) { // TODO: Fix error handling & re-shorten timeout ANativeWindow_setDequeueTimeout(window, 4000_ms); } - mNativeSurface->setExtraBufferCount(mRenderAheadCapacity); + mNativeSurface->setExtraBufferCount(extraBuffers); } else { mNativeSurface = nullptr; } @@ -441,24 +433,6 @@ void CanvasContext::notifyFramePending() { mRenderThread.pushBackFrameCallback(this); } -void CanvasContext::setPresentTime() { - int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO; - int renderAhead = 0; - const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos(); - if (mFixedRenderAhead) { - renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity); - } else if (frameIntervalNanos < 15_ms) { - renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity)); - } - - if (renderAhead) { - presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + - (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() + - (frameIntervalNanos / 2); - } - native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime); -} - void CanvasContext::draw() { SkRect dirty; mDamageAccumulator.finish(&dirty); @@ -478,8 +452,6 @@ void CanvasContext::draw() { mCurrentFrameInfo->markIssueDrawCommandsStart(); Frame frame = mRenderPipeline->getFrame(); - setPresentTime(); - SkRect windowDirty = computeDirtyRect(frame, &dirty); bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, @@ -765,19 +737,6 @@ bool CanvasContext::surfaceRequiresRedraw() { return width != mLastFrameWidth || height != mLastFrameHeight; } -void CanvasContext::setRenderAheadDepth(int renderAhead) { - if (renderAhead > 2 || renderAhead < -1 || mNativeSurface) { - return; - } - if (renderAhead == -1) { - mFixedRenderAhead = false; - mRenderAheadDepth = 0; - } else { - mFixedRenderAhead = true; - mRenderAheadDepth = static_cast<uint32_t>(renderAhead); - } -} - SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { // can't rely on prior content of window if viewport size changes diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index cc4eb3285bbe..b31883b9ae94 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -193,9 +193,6 @@ public: return mUseForceDark; } - // Must be called before setSurface - void setRenderAheadDepth(int renderAhead); - SkISize getNextFrameSize() const; private: @@ -211,7 +208,6 @@ private: bool isSwapChainStuffed(); bool surfaceRequiresRedraw(); - void setPresentTime(); void setupPipelineSurface(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); @@ -232,9 +228,6 @@ private: // painted onto its surface. bool mIsDirty = false; SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; - bool mFixedRenderAhead = false; - uint32_t mRenderAheadDepth = 0; - uint32_t mRenderAheadCapacity = 0; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index b51f6dcfc66f..0ade8dde12eb 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -295,11 +295,6 @@ void RenderProxy::setForceDark(bool enable) { mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); }); } -void RenderProxy::setRenderAheadDepth(int renderAhead) { - mRenderThread.queue().post( - [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); }); -} - int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 33dabc9895b1..a4adb16a930e 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -123,23 +123,6 @@ public: void removeFrameMetricsObserver(FrameMetricsObserver* observer); void setForceDark(bool enable); - /** - * Sets a render-ahead depth on the backing renderer. This will increase latency by - * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>. - * In return the renderer will be less susceptible to jitter, resulting in a smoother animation. - * - * Not recommended to use in response to anything touch driven, but for canned animations - * where latency is not a concern careful use may be beneficial. - * - * Note that when increasing this there will be a frame gap of N frames where N is - * renderAhead - <current renderAhead>. When decreasing this if there are any pending - * frames they will retain their prior renderAhead value, so it will take a few frames - * for the decrease to flush through. - * - * @param renderAhead How far to render ahead, must be in the range [0..2] - */ - void setRenderAheadDepth(int renderAhead); - static int copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, SkBitmap* bitmap); static void prepareToDraw(Bitmap& bitmap); diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 74a039b3d090..91022cfe734b 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -38,7 +38,6 @@ public: int count = 0; int reportFrametimeWeight = 0; bool renderOffscreen = true; - int renderAhead = 0; }; template <class T> diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index eda5d2266dcf..8c7d2612f39b 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -153,11 +153,6 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, proxy->resetProfileInfo(); proxy->fence(); - if (opts.renderAhead) { - usleep(33000); - } - proxy->setRenderAheadDepth(opts.renderAhead); - ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 88d33c315a09..174a14080eff 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -69,7 +69,6 @@ OPTIONS: are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk - --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2. )"); } @@ -171,7 +170,6 @@ enum { Onscreen, Offscreen, Renderer, - RenderAhead, }; } @@ -187,7 +185,6 @@ static const struct option LONG_OPTIONS[] = { {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, - {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead}, {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; @@ -286,16 +283,6 @@ void parseOptions(int argc, char* argv[]) { gOpts.renderOffscreen = true; break; - case LongOpts::RenderAhead: - if (!optarg) { - error = true; - } - gOpts.renderAhead = atoi(optarg); - if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) { - error = true; - } - break; - case 'h': printHelp(); exit(EXIT_SUCCESS); diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 62bf39ca8a7a..1d3f9d701eed 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -22,11 +22,11 @@ namespace android { namespace uirenderer { -#define NON_ZERO_EPSILON (0.001f) -#define ALPHA_EPSILON (0.001f) - class MathUtils { public: + static constexpr float NON_ZERO_EPSILON = 0.001f; + static constexpr float ALPHA_EPSILON = 0.001f; + /** * Check for floats that are close enough to zero. */ diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index d40de76431e4..205c1f4b4057 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -354,7 +354,7 @@ public final class AudioDeviceInfo { /** * @hide - * @return the internal device tyoe + * @return the internal device type */ public int getInternalType() { return mPort.type(); diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index fd71670ee404..606cd5718d98 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -322,7 +322,8 @@ public class DeviceDiscoveryService extends Service { void onDeviceSelected(String callingPackage, String deviceAddress) { mServiceCallback.complete(new Association( - getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false)); + getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false, + System.currentTimeMillis())); } void onCancel() { diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index a74613970ce7..fbe15bbaa19c 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -21,6 +21,7 @@ import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.LISTEN; import static android.net.NetworkRequest.Type.REQUEST; import static android.net.NetworkRequest.Type.TRACK_DEFAULT; +import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; import static android.net.QosCallback.QosCallbackRegistrationException; import android.annotation.CallbackExecutor; @@ -3721,7 +3722,8 @@ public class ConnectivityManager { printStackTrace(); checkCallbackNotNull(callback); Preconditions.checkArgument( - reqType == TRACK_DEFAULT || need != null, "null NetworkCapabilities"); + reqType == TRACK_DEFAULT || reqType == TRACK_SYSTEM_DEFAULT || need != null, + "null NetworkCapabilities"); final NetworkRequest request; final String callingPackageName = mContext.getOpPackageName(); try { @@ -4192,8 +4194,9 @@ public class ConnectivityManager { } /** - * Registers to receive notifications about changes in the system default network. The callbacks - * will continue to be called until either the application exits or + * Registers to receive notifications about changes in the application's default network. This + * may be a physical network or a virtual network, such as a VPN that applies to the + * application. The callbacks will continue to be called until either the application exits or * {@link #unregisterNetworkCallback(NetworkCallback)} is called. * * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the @@ -4206,7 +4209,7 @@ public class ConnectivityManager { * {@link #unregisterNetworkCallback(NetworkCallback)}. * * @param networkCallback The {@link NetworkCallback} that the system will call as the - * system default network changes. + * application's default network changes. * The callback is invoked on the default internal Handler. * @throws RuntimeException if the app already has too many callbacks registered. */ @@ -4216,8 +4219,9 @@ public class ConnectivityManager { } /** - * Registers to receive notifications about changes in the system default network. The callbacks - * will continue to be called until either the application exits or + * Registers to receive notifications about changes in the application's default network. This + * may be a physical network or a virtual network, such as a VPN that applies to the + * application. The callbacks will continue to be called until either the application exits or * {@link #unregisterNetworkCallback(NetworkCallback)} is called. * * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the @@ -4230,26 +4234,60 @@ public class ConnectivityManager { * {@link #unregisterNetworkCallback(NetworkCallback)}. * * @param networkCallback The {@link NetworkCallback} that the system will call as the - * system default network changes. + * application's default network changes. * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. * @throws RuntimeException if the app already has too many callbacks registered. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, @NonNull Handler handler) { - // This works because if the NetworkCapabilities are null, - // ConnectivityService takes them from the default request. - // - // Since the capabilities are exactly the same as the default request's - // capabilities, this request is guaranteed, at all times, to be - // satisfied by the same network, if any, that satisfies the default - // request, i.e., the system default network. CallbackHandler cbHandler = new CallbackHandler(handler); sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0, TRACK_DEFAULT, TYPE_NONE, cbHandler); } /** + * Registers to receive notifications about changes in the system default network. The callbacks + * will continue to be called until either the application exits or + * {@link #unregisterNetworkCallback(NetworkCallback)} is called. + * + * This method should not be used to determine networking state seen by applications, because in + * many cases, most or even all application traffic may not use the default network directly, + * and traffic from different applications may go on different networks by default. As an + * example, if a VPN is connected, traffic from all applications might be sent through the VPN + * and not onto the system default network. Applications or system components desiring to do + * determine network state as seen by applications should use other methods such as + * {@link #registerDefaultNetworkCallback(NetworkCallback, Handler)}. + * + * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the + * number of outstanding requests to 100 per app (identified by their UID), shared with + * all variants of this method, of {@link #requestNetwork} as well as + * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}. + * Requesting a network with this method will count toward this limit. If this limit is + * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources, + * make sure to unregister the callbacks with + * {@link #unregisterNetworkCallback(NetworkCallback)}. + * + * @param networkCallback The {@link NetworkCallback} that the system will call as the + * system default network changes. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @throws RuntimeException if the app already has too many callbacks registered. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @SuppressLint({"ExecutorRegistration", "PairedRegistration"}) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, + @NonNull Handler handler) { + CallbackHandler cbHandler = new CallbackHandler(handler); + sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0, + TRACK_SYSTEM_DEFAULT, TYPE_NONE, cbHandler); + } + + /** * Requests bandwidth update for a given {@link Network} and returns whether the update request * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying * network connection for updated bandwidth information. The caller will be notified via diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index 55b2c3c9e11f..9d67f0b84367 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -762,12 +762,14 @@ public final class NetworkCapabilities implements Parcelable { final int originalSignalStrength = mSignalStrength; final int originalOwnerUid = getOwnerUid(); final int[] originalAdministratorUids = getAdministratorUids(); + final TransportInfo originalTransportInfo = getTransportInfo(); clearAll(); mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS) | (1 << TRANSPORT_TEST); mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES; mNetworkSpecifier = originalSpecifier; mSignalStrength = originalSignalStrength; + mTransportInfo = originalTransportInfo; // Only retain the owner and administrator UIDs if they match the app registering the remote // caller that registered the network. diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index 6540397d6200..b4a651c0607e 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -104,17 +104,14 @@ public class NetworkRequest implements Parcelable { * callbacks about the single, highest scoring current network * (if any) that matches the specified NetworkCapabilities, or * - * - TRACK_DEFAULT, a hybrid of the two designed such that the - * framework will issue callbacks for the single, highest scoring - * current network (if any) that matches the capabilities of the - * default Internet request (mDefaultRequest), but which cannot cause - * the framework to either create or retain the existence of any - * specific network. Note that from the point of view of the request - * matching code, TRACK_DEFAULT is identical to REQUEST: its special - * behaviour is not due to different semantics, but to the fact that - * the system will only ever create a TRACK_DEFAULT with capabilities - * that are identical to the default request's capabilities, thus - * causing it to share fate in every way with the default request. + * - TRACK_DEFAULT, which causes the framework to issue callbacks for + * the single, highest scoring current network (if any) that will + * be chosen for an app, but which cannot cause the framework to + * either create or retain the existence of any specific network. + * + * - TRACK_SYSTEM_DEFAULT, which causes the framework to send callbacks + * for the network (if any) that satisfies the default Internet + * request. * * - BACKGROUND_REQUEST, like REQUEST but does not cause any networks * to retain the NET_CAPABILITY_FOREGROUND capability. A network with @@ -137,6 +134,7 @@ public class NetworkRequest implements Parcelable { TRACK_DEFAULT, REQUEST, BACKGROUND_REQUEST, + TRACK_SYSTEM_DEFAULT, }; /** @@ -601,6 +599,8 @@ public class NetworkRequest implements Parcelable { return NetworkRequestProto.TYPE_REQUEST; case BACKGROUND_REQUEST: return NetworkRequestProto.TYPE_BACKGROUND_REQUEST; + case TRACK_SYSTEM_DEFAULT: + return NetworkRequestProto.TYPE_TRACK_SYSTEM_DEFAULT; default: return NetworkRequestProto.TYPE_UNKNOWN; } diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java index 1812509ba6d2..1e30283a9e6c 100644 --- a/packages/Connectivity/framework/src/android/net/VpnManager.java +++ b/packages/Connectivity/framework/src/android/net/VpnManager.java @@ -55,13 +55,29 @@ import java.security.GeneralSecurityException; public class VpnManager { /** Type representing a lack of VPN @hide */ public static final int TYPE_VPN_NONE = -1; - /** VPN service type code @hide */ + + /** + * A VPN created by an app using the {@link VpnService} API. + * @hide + */ public static final int TYPE_VPN_SERVICE = 1; - /** Platform VPN type code @hide */ + + /** + * A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}. + * @hide + */ public static final int TYPE_VPN_PLATFORM = 2; + /** + * An IPsec VPN created by the built-in LegacyVpnRunner. + * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead. + * @hide + */ + @Deprecated + public static final int TYPE_VPN_LEGACY = 3; + /** @hide */ - @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM}) + @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY}) @Retention(RetentionPolicy.SOURCE) public @interface VpnType {} diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 84dacfdaa2e4..d10ff402aa3a 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -251,5 +251,5 @@ <integer name="def_accessibility_magnification_capabilities">3</integer> <!-- Default for Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW --> - <bool name="def_enable_non_resizable_multi_window">false</bool> + <bool name="def_enable_non_resizable_multi_window">true</bool> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index c7790fd3ace7..a1fd7eefaf9a 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -297,6 +297,24 @@ public class SettingsProvider extends ContentProvider { Settings.System.getCloneFromParentOnValueSettings(sSystemCloneFromParentOnDependency); } + private static final Set<String> sAllSecureSettings = new ArraySet<>(); + private static final Set<String> sReadableSecureSettings = new ArraySet<>(); + static { + Settings.Secure.getPublicSettings(sAllSecureSettings, sReadableSecureSettings); + } + + private static final Set<String> sAllSystemSettings = new ArraySet<>(); + private static final Set<String> sReadableSystemSettings = new ArraySet<>(); + static { + Settings.System.getPublicSettings(sAllSystemSettings, sReadableSystemSettings); + } + + private static final Set<String> sAllGlobalSettings = new ArraySet<>(); + private static final Set<String> sReadableGlobalSettings = new ArraySet<>(); + static { + Settings.Global.getPublicSettings(sAllGlobalSettings, sReadableGlobalSettings); + } + private final Object mLock = new Object(); @GuardedBy("mLock") @@ -1919,6 +1937,7 @@ public class SettingsProvider extends ContentProvider { if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) { return; } + checkReadableAnnotation(settingsType, settingName); ApplicationInfo ai = getCallingApplicationInfoOrThrow(); if (!ai.isInstantApp()) { return; @@ -1932,6 +1951,41 @@ public class SettingsProvider extends ContentProvider { } } + /** + * Check if the target settings key is readable. Reject if the caller app is trying to access a + * settings key defined in the Settings.Secure, Settings.System or Settings.Global and is not + * annotated as @Readable. + * Notice that a key string that is not defined in any of the Settings.* classes will still be + * regarded as readable. + */ + private void checkReadableAnnotation(int settingsType, String settingName) { + final Set<String> allFields; + final Set<String> readableFields; + switch (settingsType) { + case SETTINGS_TYPE_GLOBAL: + allFields = sAllGlobalSettings; + readableFields = sReadableGlobalSettings; + break; + case SETTINGS_TYPE_SYSTEM: + allFields = sAllSystemSettings; + readableFields = sReadableSystemSettings; + break; + case SETTINGS_TYPE_SECURE: + allFields = sAllSecureSettings; + readableFields = sReadableSecureSettings; + break; + default: + throw new IllegalArgumentException("Invalid settings type: " + settingsType); + } + + if (allFields.contains(settingName) && !readableFields.contains(settingName)) { + throw new SecurityException( + "Settings key: <" + settingName + "> is not readable. From S+, new public " + + "settings keys need to be annotated with @Readable unless they are " + + "annotated with @hide."); + } + } + private ApplicationInfo getCallingApplicationInfoOrThrow() { // We always use the callingUid for this lookup. This means that if hypothetically an // app was installed in user A with cross user and in user B as an Instant App diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 35a2195accab..7a9e7c7e73f6 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -76,7 +76,6 @@ android:textSize="100dp" android:includeFontPadding="false" android:fontFamily="@font/clock" - android:lineSpacingMultiplier=".7" android:typeface="monospace" android:elegantTextHeight="false" dozeWeight="200" @@ -97,7 +96,6 @@ android:gravity="center_horizontal" android:textSize="@dimen/large_clock_text_size" android:includeFontPadding="false" - android:lineSpacingMultiplier=".7" android:fontFamily="@font/clock" android:typeface="monospace" android:elegantTextHeight="false" diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml index 827cf4a9d3b6..109442d7ed79 100644 --- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml +++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml @@ -16,8 +16,6 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="#242424" /> <!-- 14% of white --> - <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding" - android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" /> + <solid android:color="#FFFFFF" /> <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 862076b650b9..187ae58c2f2c 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -43,14 +43,17 @@ android:accessibilityLiveRegion="polite"/> <com.android.systemui.statusbar.phone.KeyguardIndicationTextView - android:id="@+id/keyguard_indication_enterprise_disclosure" - android:layout_width="match_parent" + android:id="@+id/keyguard_indication_text_bottom" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" + android:minHeight="48dp" + android:layout_gravity="center_horizontal" + android:layout_centerHorizontal="true" android:paddingStart="@dimen/keyguard_indication_text_padding" android:paddingEnd="@dimen/keyguard_indication_text_padding" android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" - android:alpha=".54" + android:alpha=".8" android:visibility="gone"/> </LinearLayout> diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml index 3c306322d21f..bad582669079 100644 --- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml @@ -22,19 +22,14 @@ android:layout_height="match_parent" android:layout_width="wrap_content" android:layout_gravity="center_vertical|end" - android:focusable="true" > + android:focusable="true" + android:minWidth="48dp" > - <FrameLayout - android:id="@+id/background" + <LinearLayout + android:id="@+id/icons_container" android:layout_height="@dimen/ongoing_appops_chip_height" android:layout_width="wrap_content" - android:minWidth="48dp" - android:layout_gravity="center_vertical"> - <LinearLayout - android:id="@+id/icons_container" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:gravity="center_vertical" - /> - </FrameLayout> + android:gravity="center_vertical" + android:layout_gravity="center" + /> </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index afa98b56a13c..79471e6dacd2 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -163,10 +163,14 @@ <!-- heads up elevation that is added if the view is pinned --> <dimen name="heads_up_pinned_elevation">16dp</dimen> - <!-- Height of a messaging notifications with actions at least. Not that this is an upper bound + <!-- Height of a messaging notifications with actions at least. Note that this is an upper bound and the notification won't use this much, but is measured with wrap_content --> <dimen name="notification_messaging_actions_min_height">196dp</dimen> + <!-- Height of a call notification. Note that this is an upper bound + and the notification won't use this much, but is measured with wrap_content --> + <dimen name="call_notification_full_height">172dp</dimen> + <!-- a threshold in dp per second that is considered fast scrolling --> <dimen name="scroll_fast_threshold">1500dp</dimen> @@ -199,6 +203,9 @@ <!-- The amount the content shifts upwards when transforming into the shelf --> <dimen name="shelf_transform_content_shift">32dp</dimen> + <!-- The y translation for keyguard indication text animation for rotating text in/out --> + <dimen name="keyguard_indication_y_translation">24dp</dimen> + <!-- The padding on the bottom of the notifications on the keyguard --> <dimen name="keyguard_indication_bottom_padding">12sp</dimen> @@ -685,6 +692,11 @@ <!-- The amount to shift the clocks during a small/large transition --> <dimen name="keyguard_clock_switch_y_shift">10dp</dimen> + <!-- Default line spacing multiplier between hours and minutes of the keyguard clock --> + <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item> + <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock --> + <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item> + <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item> <!-- The minimum amount the user needs to swipe to go to the camera / phone. --> @@ -1157,7 +1169,7 @@ <dimen name="logout_button_layout_height">32dp</dimen> <dimen name="logout_button_padding_horizontal">16dp</dimen> <dimen name="logout_button_margin_bottom">12dp</dimen> - <dimen name="logout_button_corner_radius">2dp</dimen> + <dimen name="logout_button_corner_radius">4dp</dimen> <!-- Blur radius on status bar window and power menu --> <dimen name="min_window_blur_radius">1px</dimen> @@ -1167,17 +1179,13 @@ <dimen name="display_cutout_margin_consumption">0px</dimen> <!-- Height of the Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_height">32dp</dimen> - <!-- Padding between background of Ongoing App Ops chip and content --> - <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen> + <dimen name="ongoing_appops_chip_height">24dp</dimen> <!-- Side padding between background of Ongoing App Ops chip and content --> <dimen name="ongoing_appops_chip_side_padding">8dp</dimen> - <!-- Margin between icons of Ongoing App Ops chip when QQS--> - <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen> - <!-- Margin between icons of Ongoing App Ops chip when QS--> - <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen> + <!-- Margin between icons of Ongoing App Ops chip --> + <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen> <!-- Icon size of Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen> + <dimen name="ongoing_appops_chip_icon_size">16dp</dimen> <!-- Radius of Ongoing App Ops chip corners --> <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 589a39c16bcb..abcf4e802ab9 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2253,6 +2253,12 @@ <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string> + <!-- Accessibility announcement after a tile has been added [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_added">Tile added</string> + + <!-- Accessibility announcement after a tile has been added [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_tile_removed">Tile removed</string> + <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] --> <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index e38cf237d36b..5c943f63a9a1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -245,9 +245,11 @@ interface ISystemUiProxy { void setSideStageVisibility(in boolean visible) = 36; /** Removes the split-screen stages. */ void exitSplitScreen() = 37; - void startTask(in int taskId, in int stage, in int position, in Bundle options) = 38; + /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */ + void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38; + void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39; void startShortcut(in String packageName, in String shortcutId, in int stage, in int position, - in Bundle options, in UserHandle user) = 39; + in Bundle options, in UserHandle user) = 40; void startIntent( - in PendingIntent intent, in int stage, in int position, in Bundle options) = 40; + in PendingIntent intent, in int stage, in int position, in Bundle options) = 41; } diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index 59e81cf96bb2..0a117c17a354 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -16,37 +16,72 @@ package com.android.keyguard; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.graphics.Color; import android.graphics.Paint; +import android.icu.text.NumberFormat; import android.util.MathUtils; import com.android.settingslib.Utils; +import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.util.ViewController; +import java.util.Locale; +import java.util.Objects; import java.util.TimeZone; /** - * Controls the color of a GradientTextClock. + * Controller for an AnimatableClockView. */ public class AnimatableClockController extends ViewController<AnimatableClockView> { + private static final int FORMAT_NUMBER = 1234567890; private final StatusBarStateController mStatusBarStateController; - private final int[] mDozingColors = new int[] {Color.WHITE, Color.WHITE}; - private int[] mLockScreenColors = new int[2]; + private final BroadcastDispatcher mBroadcastDispatcher; + private final int mDozingColor = Color.WHITE; + private int mLockScreenColor; private boolean mIsDozing; + private Locale mLocale; + + private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my")); + private final String mBurmeseNumerals; + private final float mBurmeseLineSpacing; + private final float mDefaultLineSpacing; public AnimatableClockController( AnimatableClockView view, - StatusBarStateController statusBarStateController) { + StatusBarStateController statusBarStateController, + BroadcastDispatcher broadcastDispatcher) { super(view); mStatusBarStateController = statusBarStateController; mIsDozing = mStatusBarStateController.isDozing(); + mBroadcastDispatcher = broadcastDispatcher; + + mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER); + mBurmeseLineSpacing = getContext().getResources().getFloat( + R.dimen.keyguard_clock_line_spacing_scale_burmese); + mDefaultLineSpacing = getContext().getResources().getFloat( + R.dimen.keyguard_clock_line_spacing_scale); } + private BroadcastReceiver mLocaleBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateLocale(); + } + }; + @Override protected void onViewAttached() { + updateLocale(); + mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver, + new IntentFilter(Intent.ACTION_LOCALE_CHANGED)); mStatusBarStateController.addCallback(mStatusBarStateListener); mIsDozing = mStatusBarStateController.isDozing(); refreshTime(); @@ -55,6 +90,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie @Override protected void onViewDetached() { + mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver); mStatusBarStateController.removeCallback(mStatusBarStateListener); } @@ -84,11 +120,23 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie mView.refreshFormat(); } + private void updateLocale() { + Locale currLocale = Locale.getDefault(); + if (!Objects.equals(currLocale, mLocale)) { + mLocale = currLocale; + NumberFormat nf = NumberFormat.getInstance(mLocale); + if (nf.format(FORMAT_NUMBER).equals(mBurmeseNumerals)) { + mView.setLineSpacingScale(mBurmeseLineSpacing); + } else { + mView.setLineSpacingScale(mDefaultLineSpacing); + } + } + } + private void initColors() { - mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(), + mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(), com.android.systemui.R.attr.wallpaperTextColor); - mLockScreenColors[1] = mLockScreenColors[0]; // same color - mView.setColors(mDozingColors, mLockScreenColors); + mView.setColors(mDozingColor, mLockScreenColor); mView.animateDoze(mIsDozing, false); } diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java index ca99563986b4..64b3d7356d3c 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java @@ -44,12 +44,13 @@ public class AnimatableClockView extends TextView { private final Calendar mTime = Calendar.getInstance(); - private CharSequence mFormat; - private CharSequence mDescFormat; - private int[] mDozingColors; - private int[] mLockScreenColors; private final int mDozingWeight; private final int mLockScreenWeight; + private CharSequence mFormat; + private CharSequence mDescFormat; + private int mDozingColor; + private int mLockScreenColor; + private float mLineSpacingScale = 1f; private TextAnimator mTextAnimator = null; private Runnable mOnTextAnimatorInitialized; @@ -111,8 +112,7 @@ public class AnimatableClockView extends TextView { () -> { invalidate(); return Unit.INSTANCE; - }, - 2 /* number of lines (each can have a unique Paint) */); + }); if (mOnTextAnimatorInitialized != null) { mOnTextAnimatorInitialized.run(); mOnTextAnimatorInitialized = null; @@ -127,15 +127,20 @@ public class AnimatableClockView extends TextView { mTextAnimator.draw(canvas); } - void setColors(int[] dozingColors, int[] lockScreenColors) { - mDozingColors = dozingColors; - mLockScreenColors = lockScreenColors; + void setLineSpacingScale(float scale) { + mLineSpacingScale = scale; + setLineSpacing(0, mLineSpacingScale); + } + + void setColors(int dozingColor, int lockScreenColor) { + mDozingColor = dozingColor; + mLockScreenColor = lockScreenColor; } void animateDoze(boolean isDozing, boolean animate) { setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */, -1, - isDozing ? mDozingColors : mLockScreenColors, + isDozing ? mDozingColor : mLockScreenColor, animate); } @@ -152,15 +157,15 @@ public class AnimatableClockView extends TextView { private void setTextStyle( @IntRange(from = 0, to = 1000) int weight, @FloatRange(from = 0) float textSize, - int[] colors, + int color, boolean animate) { if (mTextAnimator != null) { - mTextAnimator.setTextStyle(weight, textSize, colors, animate, ANIM_DURATION, null); + mTextAnimator.setTextStyle(weight, textSize, color, animate, ANIM_DURATION, null); } else { // when the text animator is set, update its start values mOnTextAnimatorInitialized = () -> mTextAnimator.setTextStyle( - weight, textSize, colors, false, ANIM_DURATION, null); + weight, textSize, color, false, ANIM_DURATION, null); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index e0de180e657c..e375877ed6cf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -28,6 +28,7 @@ import android.widget.FrameLayout; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ClockPlugin; @@ -56,6 +57,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final ClockManager mClockManager; private final KeyguardSliceViewController mKeyguardSliceViewController; private final NotificationIconAreaController mNotificationIconAreaController; + private final BroadcastDispatcher mBroadcastDispatcher; /** * Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL. @@ -101,7 +103,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS SysuiColorExtractor colorExtractor, ClockManager clockManager, KeyguardSliceViewController keyguardSliceViewController, NotificationIconAreaController notificationIconAreaController, - ContentResolver contentResolver) { + ContentResolver contentResolver, + BroadcastDispatcher broadcastDispatcher) { super(keyguardClockSwitch); mResources = resources; mStatusBarStateController = statusBarStateController; @@ -109,6 +112,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mClockManager = clockManager; mKeyguardSliceViewController = keyguardSliceViewController; mNotificationIconAreaController = notificationIconAreaController; + mBroadcastDispatcher = broadcastDispatcher; mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24); } @@ -231,12 +235,14 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mNewLockScreenClockViewController = new AnimatableClockController( mView.findViewById(R.id.animatable_clock_view), - mStatusBarStateController); + mStatusBarStateController, + mBroadcastDispatcher); mNewLockScreenClockViewController.init(); mNewLockScreenLargeClockViewController = new AnimatableClockController( mView.findViewById(R.id.animatable_clock_view_large), - mStatusBarStateController); + mStatusBarStateController, + mBroadcastDispatcher); mNewLockScreenLargeClockViewController.init(); } } else { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 97aa26fb7f68..fea152abe36a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -56,8 +56,10 @@ public class KeyguardStatusView extends GridLayout { private final IActivityManager mIActivityManager; private TextView mLogoutView; + private boolean mCanShowLogout = true; // by default, try to show the logout button here private KeyguardClockSwitch mClockView; private TextView mOwnerInfo; + private boolean mCanShowOwnerInfo = true; // by default, try to show the owner information here private KeyguardSliceView mKeyguardSlice; private View mNotificationIcons; private Runnable mPendingMarqueeStart; @@ -114,6 +116,25 @@ public class KeyguardStatusView extends GridLayout { if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled); } + void setCanShowOwnerInfo(boolean canShowOwnerInfo) { + mCanShowOwnerInfo = canShowOwnerInfo; + mOwnerInfo = findViewById(R.id.owner_info); + if (mOwnerInfo != null) { + if (mCanShowOwnerInfo) { + mOwnerInfo.setVisibility(VISIBLE); + updateOwnerInfo(); + } else { + mOwnerInfo.setVisibility(GONE); + mOwnerInfo = null; + } + } + } + + void setCanShowLogout(boolean canShowLogout) { + mCanShowLogout = canShowLogout; + updateLogoutView(); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -128,7 +149,10 @@ public class KeyguardStatusView extends GridLayout { if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) { mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext)); } - mOwnerInfo = findViewById(R.id.owner_info); + if (mCanShowOwnerInfo) { + mOwnerInfo = findViewById(R.id.owner_info); + } + mKeyguardSlice = findViewById(R.id.keyguard_status_area); mTextColor = mClockView.getCurrentTextColor(); @@ -189,7 +213,7 @@ public class KeyguardStatusView extends GridLayout { if (mLogoutView == null) { return; } - mLogoutView.setVisibility(shouldShowLogout() ? VISIBLE : GONE); + mLogoutView.setVisibility(mCanShowLogout && shouldShowLogout() ? VISIBLE : GONE); // Logout button will stay in language of user 0 if we don't set that manually. mLogoutView.setText(mContext.getResources().getString( com.android.internal.R.string.global_action_logout)); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 973b49384c09..a5f364d30d7d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -134,7 +134,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** - * Get the height of the logout button. + * Get the height of the owner information view. */ public int getOwnerInfoHeight() { return mView.getOwnerInfoHeight(); @@ -335,9 +335,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV // of the top of the view mKeyguardSliceViewController.updateTopMargin( mKeyguardClockSwitchController.getClockTextTopPadding()); + mView.setCanShowOwnerInfo(false); + mView.setCanShowLogout(false); } else { // reset margin mKeyguardSliceViewController.updateTopMargin(0); + mView.setCanShowOwnerInfo(true); + mView.setCanShowLogout(false); } updateAodIcons(); } diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt index f2d36d19afcf..5735a4fc0172 100644 --- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt +++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt @@ -54,11 +54,10 @@ private const val DEFAULT_ANIMATION_DURATION: Long = 300 */ class TextAnimator( layout: Layout, - private val invalidateCallback: () -> Unit, - private val numLines: Int = 1 + private val invalidateCallback: () -> Unit ) { // Following two members are for mutable for testing purposes. - internal var textInterpolator: TextInterpolator = TextInterpolator(layout, numLines) + internal var textInterpolator: TextInterpolator = TextInterpolator(layout) internal var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION addUpdateListener { @@ -99,7 +98,7 @@ class TextAnimator( fun setTextStyle( weight: Int = -1, textSize: Float = -1f, - colors: IntArray? = null, + color: Int? = null, animate: Boolean = true, duration: Long = -1L, interpolator: TimeInterpolator? = null @@ -110,21 +109,13 @@ class TextAnimator( } if (textSize >= 0) { - for (targetPaint in textInterpolator.targetPaint) - targetPaint.textSize = textSize + textInterpolator.targetPaint.textSize = textSize } if (weight >= 0) { - for (targetPaint in textInterpolator.targetPaint) - targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight" + textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight" } - if (colors != null) { - require(colors.size == textInterpolator.targetPaint.size) { - "colors size (${colors.size}) must be the same size as" + - " targetPaints size (${textInterpolator.targetPaint.size})," + - " which was initialized as numLines ($numLines)" - } - for ((index, targetPaint) in textInterpolator.targetPaint.withIndex()) - targetPaint.color = colors[index] + if (color != null) { + textInterpolator.targetPaint.color = color } textInterpolator.onTargetPaintModified() diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt index 0d41a2f56618..5d5797cbbb3d 100644 --- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt +++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt @@ -30,8 +30,7 @@ import java.lang.Math.max * Provide text style linear interpolation for plain text. */ class TextInterpolator( - layout: Layout, - lines: Int = 1 + layout: Layout ) { /** @@ -40,11 +39,9 @@ class TextInterpolator( * Once you modified the style parameters, you have to call reshapeText to recalculate base text * layout. * - * @return an array list of paint objects representing one paint per line of text. If this - * list has a smaller size than the number of lines, all extra lines will use the Paint an - * index 0. + * @return a paint object */ - val basePaint = createDefaultPaint(layout.paint, lines) + val basePaint = TextPaint(layout.paint) /** * Returns target paint used for interpolation. @@ -52,18 +49,9 @@ class TextInterpolator( * Once you modified the style parameters, you have to call reshapeText to recalculate target * text layout. * - * @return an array list of paint objects representing one paint per line of text. If this - * list has a smaller size than the number of lines, all extra lines will use the Paint an - * index 0. + * @return a paint object */ - val targetPaint = createDefaultPaint(layout.paint, lines) - - private fun createDefaultPaint(paint: TextPaint, lines: Int): ArrayList<TextPaint> { - val paintList = ArrayList<TextPaint>() - for (i in 0 until lines) - paintList.add(TextPaint(paint)) - return paintList - } + val targetPaint = TextPaint(layout.paint) /** * A class represents a single font run. @@ -102,7 +90,7 @@ class TextInterpolator( private val fontInterpolator = FontInterpolator() // Recycling object for glyph drawing. Will be extended for the longest font run if needed. - private val tmpDrawPaints = ArrayList<TextPaint>() + private val tmpDrawPaint = TextPaint() private var tmpPositionArray = FloatArray(20) /** @@ -216,10 +204,10 @@ class TextInterpolator( if (progress == 0f) { return } else if (progress == 1f) { - updatePaint(basePaint, targetPaint) + basePaint.set(targetPaint) } else { - lerp(basePaint, targetPaint, progress, tmpDrawPaints) - updatePaint(basePaint, tmpDrawPaints) + lerp(basePaint, targetPaint, progress, tmpDrawPaint) + basePaint.set(tmpDrawPaint) } lines.forEach { line -> @@ -237,21 +225,13 @@ class TextInterpolator( progress = 0f } - companion object { - fun updatePaint(toUpdate: ArrayList<TextPaint>, newValues: ArrayList<TextPaint>) { - toUpdate.clear() - for (paint in newValues) - toUpdate.add(TextPaint(paint)) - } - } - /** * Draws interpolated text at the given progress. * * @param canvas a canvas. */ fun draw(canvas: Canvas) { - lerp(basePaint, targetPaint, progress, tmpDrawPaints) + lerp(basePaint, targetPaint, progress, tmpDrawPaint) lines.forEachIndexed { lineNo, line -> line.runs.forEach { run -> canvas.save() @@ -261,10 +241,7 @@ class TextInterpolator( canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat()) run.fontRuns.forEach { fontRun -> - if (lineNo >= tmpDrawPaints.size) - drawFontRun(canvas, run, fontRun, tmpDrawPaints[0]) - else - drawFontRun(canvas, run, fontRun, tmpDrawPaints[lineNo]) + drawFontRun(canvas, run, fontRun, tmpDrawPaint) } } finally { canvas.restore() @@ -430,27 +407,19 @@ class TextInterpolator( } // Linear interpolate the paint. - private fun lerp( - from: ArrayList<TextPaint>, - to: ArrayList<TextPaint>, - progress: Float, - out: ArrayList<TextPaint> - ) { - out.clear() + private fun lerp(from: Paint, to: Paint, progress: Float, out: Paint) { + out.set(from) + // Currently only font size & colors are interpolated. // TODO(172943390): Add other interpolation or support custom interpolator. - for (index in from.indices) { - val paint = TextPaint(from[index]) - paint.textSize = MathUtils.lerp(from[index].textSize, to[index].textSize, progress) - paint.color = ColorUtils.blendARGB(from[index].color, to[index].color, progress) - out.add(paint) - } + out.textSize = MathUtils.lerp(from.textSize, to.textSize, progress) + out.color = ColorUtils.blendARGB(from.color, to.color, progress) } // Shape the text and stores the result to out argument. private fun shapeText( layout: Layout, - paints: ArrayList<TextPaint> + paint: TextPaint ): List<List<PositionedGlyphs>> { val out = mutableListOf<List<PositionedGlyphs>>() for (lineNo in 0 until layout.lineCount) { // Shape all lines. @@ -458,7 +427,7 @@ class TextInterpolator( val count = layout.getLineEnd(lineNo) - lineStart val runs = mutableListOf<PositionedGlyphs>() TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic, - paints[lineNo]) { _, _, glyphs, _ -> + paint) { _, _, glyphs, _ -> runs.add(glyphs) } out.add(runs) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt index 429f67fdc706..d06568a7caf9 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt @@ -24,6 +24,9 @@ import android.service.controls.Control */ interface ControlActionCoordinator { + // Handle actions launched from GlobalActionsDialog or ControlDialog + var startedFromGlobalActions: Boolean + /** * Close any dialogs which may have been open */ diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index 7cd7e18965c2..247f25e1ccea 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -30,6 +30,7 @@ import android.service.controls.actions.CommandAction import android.service.controls.actions.FloatAction import android.util.Log import android.view.HapticFeedbackConstants +import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.globalactions.GlobalActionsComponent @@ -37,6 +38,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.DelayableExecutor import com.android.wm.shell.TaskViewFactory +import dagger.Lazy import java.util.Optional import javax.inject.Inject @@ -48,13 +50,17 @@ class ControlActionCoordinatorImpl @Inject constructor( private val activityStarter: ActivityStarter, private val keyguardStateController: KeyguardStateController, private val globalActionsComponent: GlobalActionsComponent, - private val taskViewFactory: Optional<TaskViewFactory> + private val taskViewFactory: Optional<TaskViewFactory>, + private val broadcastDispatcher: BroadcastDispatcher, + private val lazyUiController: Lazy<ControlsUiController> ) : ControlActionCoordinator { private var dialog: Dialog? = null private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator private var pendingAction: Action? = null private var actionsInProgress = mutableSetOf<String>() + override var startedFromGlobalActions: Boolean = true + companion object { private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L } @@ -131,8 +137,8 @@ class ControlActionCoordinatorImpl @Inject constructor( private fun bouncerOrRun(action: Action) { if (keyguardStateController.isShowing()) { - var closeGlobalActions = !keyguardStateController.isUnlocked() - if (closeGlobalActions) { + var closeDialog = !keyguardStateController.isUnlocked() + if (closeDialog) { context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) // pending actions will only run after the control state has been refreshed @@ -141,8 +147,12 @@ class ControlActionCoordinatorImpl @Inject constructor( activityStarter.dismissKeyguardThenExecute({ Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action") - if (closeGlobalActions) { - globalActionsComponent.handleShowGlobalActionsMenu() + if (closeDialog) { + if (startedFromGlobalActions) { + globalActionsComponent.handleShowGlobalActionsMenu() + } else { + ControlsDialog(context, broadcastDispatcher).show(lazyUiController.get()) + } } else { action.invoke() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt index 8e878cf76ad9..f533cfb47076 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt @@ -66,7 +66,7 @@ class ControlsDialog( val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls) vg.alpha = 0f - controller.show(vg, { /* do nothing */ }) + controller.show(vg, { /* do nothing */ }, false /* startedFromGlobalActions */) vg.animate() .alpha(1f) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt index 4e4c82cabaa0..944887741721 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt @@ -29,7 +29,7 @@ interface ControlsUiController { public const val EXTRA_ANIMATE = "extra_animate" } - fun show(parent: ViewGroup, dismissGlobalActions: Runnable) + fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean) fun hide() /** diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 2b529f9a6cde..762362cde095 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -102,7 +102,7 @@ class ControlsUiControllerImpl @Inject constructor ( private lateinit var lastItems: List<SelectionItem> private var popup: ListPopupWindow? = null private var hidden = true - private lateinit var dismissGlobalActions: Runnable + private lateinit var onDismiss: Runnable private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow) private var retainCache = false @@ -145,13 +145,19 @@ class ControlsUiControllerImpl @Inject constructor ( } } - override fun show(parent: ViewGroup, dismissGlobalActions: Runnable) { + override fun show( + parent: ViewGroup, + onDismiss: Runnable, + startedFromGlobalActions: Boolean + ) { Log.d(ControlsUiController.TAG, "show()") this.parent = parent - this.dismissGlobalActions = dismissGlobalActions + this.onDismiss = onDismiss hidden = false retainCache = false + controlActionCoordinator.startedFromGlobalActions = startedFromGlobalActions + allStructures = controlsController.get().getFavorites() selectedStructure = loadPreference(allStructures) @@ -187,7 +193,7 @@ class ControlsUiControllerImpl @Inject constructor ( controlViewsById.clear() controlsById.clear() - show(parent, dismissGlobalActions) + show(parent, onDismiss, controlActionCoordinator.startedFromGlobalActions) val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f) showAnim.setInterpolator(DecelerateInterpolator(1.0f)) showAnim.setDuration(FADE_IN_MILLIS) @@ -260,7 +266,7 @@ class ControlsUiControllerImpl @Inject constructor ( private fun startActivity(context: Context, intent: Intent) { // Force animations when transitioning from a dialog to an activity intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true) - dismissGlobalActions.run() + onDismiss.run() activityStarter.dismissKeyguardThenExecute({ shadeController.collapsePanel(false) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index ec4a91c24464..2b362b94d1f5 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -20,6 +20,7 @@ import android.app.Activity; import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.keyguard.WorkLockActivity; +import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.screenrecord.ScreenRecordDialog; import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity; @@ -92,4 +93,10 @@ public abstract class DefaultActivityBinder { @IntoMap @ClassKey(TvNotificationPanelActivity.class) public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity); + + /** Inject into PeopleSpaceActivity. */ + @Binds + @IntoMap + @ClassKey(PeopleSpaceActivity.class) + public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index ad4c44767995..8af45a5c0ef1 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -2229,7 +2229,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private void showControls(ControlsUiController controller) { mControlsUiController = controller; - mControlsUiController.show(mControlsView, this::dismissForControlsActivity); + mControlsUiController.show(mControlsView, this::dismissForControlsActivity, + true /* startedFromGlobalActions */); } private boolean isWalletViewAvailable() { @@ -2457,7 +2458,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, return WindowInsets.CONSUMED; }); if (mControlsUiController != null) { - mControlsUiController.show(mControlsView, this::dismissForControlsActivity); + mControlsUiController.show(mControlsView, this::dismissForControlsActivity, + true /* startedFromGlobalActions */); } mBackgroundDrawable.setAlpha(0); @@ -2632,7 +2634,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, initializeLayout(); mGlobalActionsLayout.updateList(); if (mControlsUiController != null) { - mControlsUiController.show(mControlsView, this::dismissForControlsActivity); + mControlsUiController.show(mControlsView, this::dismissForControlsActivity, + true /* startedFromGlobalActions */); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java new file mode 100644 index 000000000000..3a06f7aeb6bf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.keyguard; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.view.View; + +/** + * Data class containing display information (message, icon, styling) for indication to show at + * the bottom of the keyguard. + * + * See {@link com.android.systemui.statusbar.phone.KeyguardBottomAreaView}. + */ +public class KeyguardIndication { + @NonNull + private final CharSequence mMessage; + @NonNull + private final ColorStateList mTextColor; + @Nullable + private final Drawable mIcon; + @Nullable + private final View.OnClickListener mOnClickListener; + @Nullable + private final Drawable mBackground; + + private KeyguardIndication( + CharSequence message, + ColorStateList textColor, + Drawable icon, + View.OnClickListener onClickListener, + Drawable background) { + mMessage = message; + mTextColor = textColor; + mIcon = icon; + mOnClickListener = onClickListener; + mBackground = background; + } + + /** + * Message to display + */ + public @NonNull CharSequence getMessage() { + return mMessage; + } + + /** + * TextColor to display the message. + */ + public @NonNull ColorStateList getTextColor() { + return mTextColor; + } + + /** + * Icon to display. + */ + public @Nullable Drawable getIcon() { + return mIcon; + } + + /** + * Click listener for messsage. + */ + public @Nullable View.OnClickListener getClickListener() { + return mOnClickListener; + } + + /** + * Background for textView. + */ + public @Nullable Drawable getBackground() { + return mBackground; + } + + /** + * KeyguardIndication Builder + */ + public static class Builder { + private CharSequence mMessage; + private Drawable mIcon; + private View.OnClickListener mOnClickListener; + private ColorStateList mTextColor; + private Drawable mBackground; + + public Builder() { } + + /** + * Required field. Message to display. + */ + public Builder setMessage(@NonNull CharSequence message) { + this.mMessage = message; + return this; + } + + /** + * Required field. Text color to use to display the message. + */ + public Builder setTextColor(@NonNull ColorStateList textColor) { + this.mTextColor = textColor; + return this; + } + + /** + * Optional. Icon to show next to the text. Icon location changes based on language + * display direction. For LTR, icon shows to the left of the message. For RTL, icon shows + * to the right of the message. + */ + public Builder setIcon(Drawable icon) { + this.mIcon = icon; + return this; + } + + /** + * Optional. Set a click listener on the message. + */ + public Builder setClickListener(View.OnClickListener onClickListener) { + this.mOnClickListener = onClickListener; + return this; + } + + /** + * Optional. Set a custom background on the TextView. + */ + public Builder setBackground(Drawable background) { + this.mBackground = background; + return this; + } + + /** + * Build the KeyguardIndication. + */ + public KeyguardIndication build() { + if (mMessage == null) throw new IllegalStateException("message must be set"); + if (mTextColor == null) throw new IllegalStateException("text color must be set"); + return new KeyguardIndication( + mMessage, mTextColor, mIcon, mOnClickListener, mBackground); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java new file mode 100644 index 000000000000..8c04143abc54 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.keyguard; + +import android.annotation.Nullable; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.text.TextUtils; +import android.view.View; + +import androidx.annotation.IntDef; + +import com.android.settingslib.Utils; +import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; +import com.android.systemui.util.ViewController; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Rotates through messages to show on the keyguard bottom area on the lock screen + * NOTE: This controller should not be used on AoD to avoid waking up the AP too often. + */ +public class KeyguardIndicationRotateTextViewController extends + ViewController<KeyguardIndicationTextView> implements Dumpable { + public static String TAG = "KgIndicationRotatingCtrl"; + private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds + + private final StatusBarStateController mStatusBarStateController; + private final float mMaxAlpha; + private final ColorStateList mInitialTextColorState; + + // Stores @IndicationType => KeyguardIndication messages + private final Map<Integer, KeyguardIndication> mIndicationMessages = new HashMap<>(); + + // Executor that will show the next message after a delay + private final DelayableExecutor mExecutor; + @Nullable private ShowNextIndication mShowNextIndicationRunnable; + + // List of indication types to show. The next indication to show is always at index 0 + private final List<Integer> mIndicationQueue = new LinkedList<>(); + private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE; + + private boolean mIsDozing; + + public KeyguardIndicationRotateTextViewController( + KeyguardIndicationTextView view, + @Main DelayableExecutor executor, + StatusBarStateController statusBarStateController, + int lockScreenMode + ) { + super(view); + mMaxAlpha = view.getAlpha(); + mExecutor = executor; + mInitialTextColorState = mView != null + ? mView.getTextColors() : ColorStateList.valueOf(Color.WHITE); + mStatusBarStateController = statusBarStateController; + mView.setLockScreenMode(lockScreenMode); + init(); + } + + @Override + protected void onViewAttached() { + mStatusBarStateController.addCallback(mStatusBarStateListener); + } + + @Override + protected void onViewDetached() { + mStatusBarStateController.removeCallback(mStatusBarStateListener); + cancelScheduledIndication(); + } + + /** + * Update the indication type with the given String. + * @param type of indication + * @param newIndication message to associate with this indication type + * @param showImmediately if true: shows this indication message immediately. Else, the text + * associated with this type is updated and will show when its turn in + * the IndicationQueue comes around. + */ + public void updateIndication(@IndicationType int type, KeyguardIndication newIndication, + boolean showImmediately) { + final boolean hasPreviousIndication = mIndicationMessages.get(type) != null; + final boolean hasNewIndication = newIndication != null + && !TextUtils.isEmpty(newIndication.getMessage()); + if (!hasNewIndication) { + mIndicationMessages.remove(type); + mIndicationQueue.removeIf(x -> x == type); + } else { + if (!hasPreviousIndication) { + mIndicationQueue.add(type); + } + + mIndicationMessages.put(type, newIndication); + } + + if (mIsDozing) { + return; + } + + final boolean showNow = showImmediately + || mCurrIndicationType == INDICATION_TYPE_NONE + || mCurrIndicationType == type; + if (hasNewIndication) { + if (showNow) { + showIndication(type); + } else if (!isNextIndicationScheduled()) { + scheduleShowNextIndication(); + } + return; + } + + if (mCurrIndicationType == type + && !hasNewIndication + && showImmediately) { + if (mShowNextIndicationRunnable != null) { + mShowNextIndicationRunnable.runImmediately(); + } else { + showIndication(INDICATION_TYPE_NONE); + } + } + } + + /** + * Stop showing the following indication type. + * + * If the current indication is of this type, immediately stops showing the message. + */ + public void hideIndication(@IndicationType int type) { + updateIndication(type, null, true); + } + + /** + * Show a transient message. + * Transient messages: + * - show immediately + * - will continue to be in the rotation of messages shown until hideTransient is called. + * - can be presented with an "error" color if isError is true + */ + public void showTransient(CharSequence newIndication, boolean isError) { + updateIndication(INDICATION_TYPE_TRANSIENT, + new KeyguardIndication.Builder() + .setMessage(newIndication) + .setTextColor(isError + ? Utils.getColorError(getContext()) + : mInitialTextColorState) + .build(), + /* showImmediately */true); + } + + /** + * Hide a transient message immediately. + */ + public void hideTransient() { + hideIndication(INDICATION_TYPE_TRANSIENT); + } + + /** + * @return true if there are available indications to show + */ + public boolean hasIndications() { + return mIndicationMessages.keySet().size() > 0; + } + + /** + * Immediately show the passed indication type and schedule the next indication to show. + * Will re-add this indication to be re-shown after all other indications have been + * rotated through. + */ + private void showIndication(@IndicationType int type) { + cancelScheduledIndication(); + + mCurrIndicationType = type; + mIndicationQueue.removeIf(x -> x == type); + if (mCurrIndicationType == INDICATION_TYPE_NONE) { + mView.setVisibility(View.GONE); + } else { + mView.setVisibility(View.VISIBLE); + mIndicationQueue.add(type); // re-add to show later + } + + // pass the style update to be run right before our new indication is shown: + mView.switchIndication(mIndicationMessages.get(type)); + + // only schedule next indication if there's more than just this indication in the queue + if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) { + scheduleShowNextIndication(); + } + } + + protected boolean isNextIndicationScheduled() { + return mShowNextIndicationRunnable != null; + } + + private void scheduleShowNextIndication() { + cancelScheduledIndication(); + mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH); + } + + private void cancelScheduledIndication() { + if (mShowNextIndicationRunnable != null) { + mShowNextIndicationRunnable.cancelDelayedExecution(); + mShowNextIndicationRunnable = null; + } + } + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onDozeAmountChanged(float linear, float eased) { + mView.setAlpha((1 - linear) * mMaxAlpha); + } + + @Override + public void onDozingChanged(boolean isDozing) { + if (isDozing == mIsDozing) return; + mIsDozing = isDozing; + if (mIsDozing) { + showIndication(INDICATION_TYPE_NONE); + } else if (mIndicationQueue.size() > 0) { + showIndication(mIndicationQueue.remove(0)); + } + } + }; + + /** + * Shows the next indication in the IndicationQueue after an optional delay. + * This wrapper has the ability to cancel itself (remove runnable from DelayableExecutor) or + * immediately run itself (which also removes itself from the DelayableExecutor). + */ + class ShowNextIndication { + private final Runnable mShowIndicationRunnable; + private Runnable mCancelDelayedRunnable; + + ShowNextIndication(long delay) { + mShowIndicationRunnable = () -> { + int type = mIndicationQueue.size() == 0 + ? INDICATION_TYPE_NONE : mIndicationQueue.remove(0); + showIndication(type); + }; + mCancelDelayedRunnable = mExecutor.executeDelayed(mShowIndicationRunnable, delay); + } + + public void runImmediately() { + cancelDelayedExecution(); + mShowIndicationRunnable.run(); + } + + public void cancelDelayedExecution() { + if (mCancelDelayedRunnable != null) { + mCancelDelayedRunnable.run(); + mCancelDelayedRunnable = null; + } + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("KeyguardIndicationRotatingTextViewController:"); + pw.println(" currentMessage=" + mView.getText()); + pw.println(" dozing:" + mIsDozing); + pw.println(" queue:" + mIndicationQueue.toString()); + pw.println(" showNextIndicationRunnable:" + mShowNextIndicationRunnable); + + if (hasIndications()) { + pw.println(" All messages:"); + for (int type : mIndicationMessages.keySet()) { + pw.println(" type=" + type + + " message=" + mIndicationMessages.get(type).getMessage()); + } + } + } + + private static final int INDICATION_TYPE_NONE = -1; + public static final int INDICATION_TYPE_OWNER_INFO = 0; + public static final int INDICATION_TYPE_DISCLOSURE = 1; + public static final int INDICATION_TYPE_LOGOUT = 2; + public static final int INDICATION_TYPE_BATTERY = 3; + public static final int INDICATION_TYPE_ALIGNMENT = 4; + public static final int INDICATION_TYPE_TRANSIENT = 5; + public static final int INDICATION_TYPE_TRUST = 6; + public static final int INDICATION_TYPE_RESTING = 7; + public static final int INDICATION_TYPE_USER_LOCKED = 8; + public static final int INDICATION_TYPE_NOW_PLAYING = 9; + public static final int INDICATION_TYPE_REVERSE_CHARGING = 10; + + @IntDef({ + INDICATION_TYPE_NONE, + INDICATION_TYPE_DISCLOSURE, + INDICATION_TYPE_OWNER_INFO, + INDICATION_TYPE_LOGOUT, + INDICATION_TYPE_BATTERY, + INDICATION_TYPE_ALIGNMENT, + INDICATION_TYPE_TRANSIENT, + INDICATION_TYPE_TRUST, + INDICATION_TYPE_RESTING, + INDICATION_TYPE_USER_LOCKED, + INDICATION_TYPE_NOW_PLAYING, + INDICATION_TYPE_REVERSE_CHARGING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IndicationType{} +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 5a918d4808d6..c55fdf4783e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -67,6 +67,7 @@ import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.IRemoteAnimationFinishedCallback; import android.view.RemoteAnimationTarget; @@ -306,6 +307,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, */ private final SparseIntArray mLastSimStates = new SparseIntArray(); + /** + * Indicates if a SIM card had the SIM PIN enabled during the initialization, before + * reaching the SIM_STATE_READY state. The flag is reset to false at SIM_STATE_READY. + * Index is the slotId - in case of multiple SIM cards. + */ + private final SparseBooleanArray mSimWasLocked = new SparseBooleanArray(); + private boolean mDeviceInteractive; private boolean mGoingToSleep; @@ -477,10 +485,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, } } - boolean simWasLocked; + boolean lastSimStateWasLocked; synchronized (KeyguardViewMediator.this) { int lastState = mLastSimStates.get(slotId); - simWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED + lastSimStateWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED || lastState == TelephonyManager.SIM_STATE_PUK_REQUIRED); mLastSimStates.append(slotId, simState); } @@ -504,17 +512,19 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, if (simState == TelephonyManager.SIM_STATE_ABSENT) { // MVNO SIMs can become transiently NOT_READY when switching networks, // so we should only lock when they are ABSENT. - if (simWasLocked) { + if (lastSimStateWasLocked) { if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the " + "previous state was locked. Reset the state."); resetStateLocked(); } + mSimWasLocked.append(slotId, false); } } break; case TelephonyManager.SIM_STATE_PIN_REQUIRED: case TelephonyManager.SIM_STATE_PUK_REQUIRED: synchronized (KeyguardViewMediator.this) { + mSimWasLocked.append(slotId, true); if (!mShowing) { if (DEBUG_SIM_STATES) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't " @@ -541,9 +551,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, case TelephonyManager.SIM_STATE_READY: synchronized (KeyguardViewMediator.this) { if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing); - if (mShowing && simWasLocked) { + if (mShowing && mSimWasLocked.get(slotId, false)) { if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the " - + "previous state was locked. Reset the state."); + + "previously was locked. Reset the state."); + mSimWasLocked.append(slotId, false); resetStateLocked(); } } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 580cbcf8ce47..c67aef618652 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -37,9 +37,12 @@ import android.view.ViewGroup; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.List; +import javax.inject.Inject; + /** * Shows the user their tiles for their priority People (go/live-status). */ @@ -54,10 +57,17 @@ public class PeopleSpaceActivity extends Activity { private LauncherApps mLauncherApps; private Context mContext; private AppWidgetManager mAppWidgetManager; + private NotificationEntryManager mNotificationEntryManager; private int mAppWidgetId; private boolean mShowSingleConversation; private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + @Inject + public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) { + super(); + mNotificationEntryManager = notificationEntryManager; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -91,8 +101,8 @@ public class PeopleSpaceActivity extends Activity { */ private void setTileViewsWithPriorityConversations() { try { - List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles( - mContext, mNotificationManager, mPeopleManager, mLauncherApps); + List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager, + mPeopleManager, mLauncherApps, mNotificationEntryManager); for (PeopleSpaceTile tile : tiles) { PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout, tile.getId()); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 994dc6d78507..dd054848aed2 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -60,9 +60,12 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ArrayUtils; import com.android.settingslib.utils.ThreadUtils; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.text.SimpleDateFormat; import java.time.Duration; @@ -137,7 +140,7 @@ public class PeopleSpaceUtils { /** Returns a list of map entries corresponding to user's conversations. */ public static List<PeopleSpaceTile> getTiles( Context context, INotificationManager notificationManager, IPeopleManager peopleManager, - LauncherApps launcherApps) + LauncherApps launcherApps, NotificationEntryManager notificationEntryManager) throws Exception { boolean showOnlyPriority = Settings.Global.getInt(context.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 1; @@ -173,6 +176,8 @@ public class PeopleSpaceUtils { getSortedTiles(peopleManager, launcherApps, mergedStream); tiles.addAll(recentTiles); } + + tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager); return tiles; } @@ -258,7 +263,8 @@ public class PeopleSpaceUtils { ServiceManager.getService(Context.NOTIFICATION_SERVICE)), IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)), - context.getSystemService(LauncherApps.class)); + context.getSystemService(LauncherApps.class), + Dependency.get(NotificationEntryManager.class)); Optional<PeopleSpaceTile> entry = tiles.stream().filter( e -> e.getId().equals(shortcutId)).findFirst(); if (entry.isPresent()) { @@ -339,6 +345,41 @@ public class PeopleSpaceUtils { && storedUserId == userId; } + static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles, + NotificationEntryManager notificationEntryManager) { + if (notificationEntryManager == null) { + Log.w(TAG, "NotificationEntryManager is null"); + return tiles; + } + Map<String, NotificationEntry> visibleNotifications = notificationEntryManager + .getVisibleNotifications() + .stream() + .filter(entry -> entry.getRanking() != null + && entry.getRanking().getConversationShortcutInfo() != null) + .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e)); + if (DEBUG) { + Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size()); + } + return tiles + .stream() + .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications)) + .collect(Collectors.toList()); + } + + static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile, + Map<String, NotificationEntry> visibleNotifications) { + String shortcutId = tile.getId(); + String packageName = tile.getPackageName(); + int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier(); + String key = getKey(shortcutId, packageName, userId); + if (!visibleNotifications.containsKey(key)) { + if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key); + return tile; + } + if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key); + return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn()); + } + /** * If incoming notification changed tile, store the changes in the tile options. */ @@ -355,17 +396,7 @@ public class PeopleSpaceUtils { } if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); - Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn); - if (message == null) { - if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping."); - return; - } - storedTile = storedTile - .toBuilder() - .setNotificationKey(sbn.getKey()) - .setNotificationContent(message.getText()) - .setNotificationDataUri(message.getDataUri()) - .build(); + storedTile = augmentTileFromNotification(storedTile, sbn); } else { if (DEBUG) { Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); @@ -380,6 +411,21 @@ public class PeopleSpaceUtils { updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile); } + static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile, + StatusBarNotification sbn) { + Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn); + if (message == null) { + if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping."); + return tile; + } + return tile + .toBuilder() + .setNotificationKey(sbn.getKey()) + .setNotificationContent(message.getText()) + .setNotificationDataUri(message.getDataUri()) + .build(); + } + private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId, PeopleSpaceTile tile) { if (tile == null) { @@ -792,6 +838,16 @@ public class PeopleSpaceUtils { return lookupKeysWithBirthdaysToday; } + static String getKey(NotificationEntry entry) { + if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null + || entry.getSbn() == null || entry.getSbn().getUser() == null) { + return null; + } + return getKey(entry.getRanking().getConversationShortcutInfo().getId(), + entry.getSbn().getPackageName(), + entry.getSbn().getUser().getIdentifier()); + } + /** * Returns the uniquely identifying key for the conversation. * diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index bee54e4dca0a..bee9889eaa4e 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -135,12 +135,14 @@ public class PeopleSpaceWidgetManager { try { String sbnShortcutId = sbn.getShortcutId(); if (sbnShortcutId == null) { + if (DEBUG) Log.d(TAG, "Sbn shortcut id is null"); return; } int[] widgetIds = mAppWidgetService.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class) ); if (widgetIds.length == 0) { + Log.d(TAG, "No app widget ids returned"); return; } SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); @@ -148,6 +150,7 @@ public class PeopleSpaceWidgetManager { String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId); Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); if (storedWidgetIds.isEmpty()) { + Log.d(TAG, "No stored widget ids"); return; } for (String widgetIdString : storedWidgetIds) { diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java index fb33affcbac5..80794cb64883 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java @@ -28,9 +28,11 @@ import android.util.Log; import android.widget.RemoteViews; import android.widget.RemoteViewsService; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.people.PeopleSpaceTileView; import com.android.systemui.people.PeopleSpaceUtils; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.ArrayList; import java.util.List; @@ -42,6 +44,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R private IPeopleManager mPeopleManager; private INotificationManager mNotificationManager; + private NotificationEntryManager mNotificationEntryManager; private PackageManager mPackageManager; private LauncherApps mLauncherApps; private List<PeopleSpaceTile> mTiles = new ArrayList<>(); @@ -56,6 +59,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R if (DEBUG) Log.d(TAG, "onCreate called"); mNotificationManager = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); mPackageManager = mContext.getPackageManager(); mPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); @@ -70,7 +74,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R private void setTileViewsWithPriorityConversations() { try { mTiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager, - mPeopleManager, mLauncherApps); + mPeopleManager, mLauncherApps, mNotificationEntryManager); } catch (Exception e) { Log.e(TAG, "Couldn't retrieve conversations", e); } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 870e714ee24c..bdd37fc521fa 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -16,11 +16,11 @@ package com.android.systemui.privacy import android.content.Context import android.util.AttributeSet -import android.view.Gravity import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout +import com.android.settingslib.Utils import com.android.systemui.R class OngoingPrivacyChip @JvmOverloads constructor( @@ -30,26 +30,13 @@ class OngoingPrivacyChip @JvmOverloads constructor( defStyleRes: Int = 0 ) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) { - private val iconMarginExpanded = context.resources.getDimensionPixelSize( - R.dimen.ongoing_appops_chip_icon_margin_expanded) - private val iconMarginCollapsed = context.resources.getDimensionPixelSize( - R.dimen.ongoing_appops_chip_icon_margin_collapsed) - private val iconSize = - context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) - private val iconColor = context.resources.getColor( - R.color.status_bar_clock_color, context.theme) - private val sidePadding = - context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) - private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg) + private var iconMargin = 0 + private var iconSize = 0 + private var iconColor = 0 + private var defaultBackgroundColor = 0 + private var cameraBackgroundColor = 0 + private lateinit var iconsContainer: LinearLayout - private lateinit var back: FrameLayout - var expanded = false - set(value) { - if (value != field) { - field = value - updateView(PrivacyChipBuilder(context, privacyList)) - } - } var privacyList = emptyList<PrivacyItem>() set(value) { @@ -60,15 +47,13 @@ class OngoingPrivacyChip @JvmOverloads constructor( override fun onFinishInflate() { super.onFinishInflate() - back = requireViewById(R.id.background) iconsContainer = requireViewById(R.id.icons_container) + + updateResources() } // Should only be called if the builder icons or app changed private fun updateView(builder: PrivacyChipBuilder) { - back.background = if (expanded) backgroundDrawable else null - val padding = if (expanded) sidePadding else 0 - back.setPaddingRelative(padding, 0, padding, 0) fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) { iconsContainer.removeAllViews() chipBuilder.generateIcons().forEachIndexed { i, it -> @@ -81,7 +66,7 @@ class OngoingPrivacyChip @JvmOverloads constructor( iconsContainer.addView(image, iconSize, iconSize) if (i != 0) { val lp = image.layoutParams as MarginLayoutParams - lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed + lp.marginStart = iconMargin image.layoutParams = lp } } @@ -90,10 +75,11 @@ class OngoingPrivacyChip @JvmOverloads constructor( if (!privacyList.isEmpty()) { generateContentDescription(builder) setIcons(builder, iconsContainer) - val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams - lp.gravity = Gravity.CENTER_VERTICAL or - (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END) - iconsContainer.layoutParams = lp + if (builder.types.contains(PrivacyType.TYPE_CAMERA)) { + iconsContainer.background.setTint(cameraBackgroundColor) + } else { + iconsContainer.background.setTint(defaultBackgroundColor) + } } else { iconsContainer.removeAllViews() } @@ -105,4 +91,20 @@ class OngoingPrivacyChip @JvmOverloads constructor( contentDescription = context.getString( R.string.ongoing_privacy_chip_content_multiple_apps, typesText) } + + private fun updateResources() { + iconMargin = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) + iconSize = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) + iconColor = + Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) + defaultBackgroundColor = context.getColor(R.color.privacy_circle_microphone_location) + cameraBackgroundColor = context.getColor(R.color.privacy_circle_camera) + + val padding = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) + iconsContainer.setPaddingRelative(padding, 0, padding, 0) + iconsContainer.background = context.getDrawable(R.drawable.privacy_chip_bg) + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt index 3f79be8fcdd4..680a617e4d26 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt @@ -41,13 +41,12 @@ import java.util.concurrent.atomic.AtomicBoolean * @param context A context to create the dialog * @param list list of elements to show in the dialog. The elements will show in the same order they * appear in the list - * @param activityStarter a callback to start an activity for a given permission group name (as - * given by [PrivacyType.permGroupName]) + * @param activityStarter a callback to start an activity for a given package name and user id */ class PrivacyDialog( context: Context, private val list: List<PrivacyElement>, - activityStarter: (String) -> Unit + activityStarter: (String, Int) -> Unit ) : SystemUIDialog(context, R.style.ScreenRecord) { private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>() @@ -129,7 +128,7 @@ class PrivacyDialog( } ?: firstLine newView.requireViewById<TextView>(R.id.text).text = finalText newView.apply { - tag = element.type.permGroupName + setTag(element) setOnClickListener(clickListener) } return newView @@ -152,12 +151,17 @@ class PrivacyDialog( } private val clickListener = View.OnClickListener { v -> - v.tag?.let { activityStarter(it as String) } + v.tag?.let { + val element = it as PrivacyElement + activityStarter(element.packageName, element.userId) + } } /** */ data class PrivacyElement( val type: PrivacyType, + val packageName: String, + val userId: Int, val applicationName: CharSequence, val attribution: CharSequence?, val lastActiveTimestamp: Long, @@ -169,6 +173,8 @@ class PrivacyDialog( init { builder.append("type=${type.logName}") + builder.append(", packageName=$packageName") + builder.append(", userId=$userId") builder.append(", appName=$applicationName") if (attribution != null) { builder.append(", attribution=$attribution") diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt index 8b6c04fb3b01..03c184336364 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt @@ -43,7 +43,7 @@ private val defaultDialogProvider = object : PrivacyDialogController.DialogProvi override fun makeDialog( context: Context, list: List<PrivacyDialog.PrivacyElement>, - starter: (String) -> Unit + starter: (String, Int) -> Unit ): PrivacyDialog { return PrivacyDialog(context, list, starter) } @@ -107,10 +107,11 @@ class PrivacyDialogController( } @MainThread - private fun startActivity(permGroupName: String) { - val intent = Intent(Intent.ACTION_MANAGE_PERMISSION_APPS) - intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName) - privacyLogger.logStartSettingsActivityFromDialog(permGroupName) + private fun startActivity(packageName: String, userId: Int) { + val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS) + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) + privacyLogger.logStartSettingsActivityFromDialog(packageName, userId) if (!keyguardStateController.isUnlocked) { // If we are locked, hide the dialog so the user can unlock dialog?.hide() @@ -159,6 +160,8 @@ class PrivacyDialogController( } PrivacyDialog.PrivacyElement( t, + it.packageName, + UserHandle.getUserId(it.uid), appName, it.attribution, it.lastAccess, @@ -257,7 +260,7 @@ class PrivacyDialogController( fun makeDialog( context: Context, list: List<PrivacyDialog.PrivacyElement>, - starter: (String) -> Unit + starter: (String, Int) -> Unit ): PrivacyDialog } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt index 8059475dd832..ebf6ae9be22f 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt @@ -121,11 +121,12 @@ class PrivacyLogger @Inject constructor( }) } - fun logStartSettingsActivityFromDialog(permGroupName: String) { + fun logStartSettingsActivityFromDialog(packageName: String, userId: Int) { log(LogLevel.INFO, { - str1 = permGroupName + str1 = packageName + int1 = userId }, { - "Start settings activity from dialog for perm group: $str1" + "Start settings activity from dialog for packageName=$str1, userId=$int1 " }) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index d248ab544656..c8edaec98ee4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -362,7 +362,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if(view == parent || view == null) return; // Ignore tile pages as they can have some offset we don't want to take into account in // RTL. - if (!(view instanceof PagedTileLayout.TilePage)) { + if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) { loc1[0] += view.getLeft(); loc1[1] += view.getTop(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 4248cf2efcd0..7776c12f587e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -75,7 +75,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn protected QuickQSPanel mHeaderQsPanel; private TouchAnimator mStatusIconsAlphaAnimator; private TouchAnimator mHeaderTextContainerAlphaAnimator; - private TouchAnimator mPrivacyChipAlphaAnimator; private DualToneHandler mDualToneHandler; private View mSystemIconsView; @@ -284,7 +283,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn updateStatusIconAlphaAnimator(); updateHeaderTextContainerAlphaAnimator(); - updatePrivacyChipAlphaAnimator(); } private void updateStatusIconAlphaAnimator() { @@ -299,12 +297,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn .build(); } - private void updatePrivacyChipAlphaAnimator() { - mPrivacyChipAlphaAnimator = new TouchAnimator.Builder() - .addFloat(mPrivacyChip, "alpha", 1, 0, 1) - .build(); - } - /** */ public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) { if (mExpanded == expanded) return; @@ -344,10 +336,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn mHeaderTextContainerView.setVisibility(INVISIBLE); } } - if (mPrivacyChipAlphaAnimator != null) { - mPrivacyChip.setExpanded(expansionFraction > 0.5); - mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction); - } mKeyguardExpansionFraction = keyguardExpansionFraction; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 507048c62186..21464fd37c6c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -441,15 +441,17 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return position > mEditIndex; } - private void addFromPosition(int position) { - if (!canAddFromPosition(position)) return; + private boolean addFromPosition(int position) { + if (!canAddFromPosition(position)) return false; move(position, mEditIndex); + return true; } - private void removeFromPosition(int position) { - if (!canRemoveFromPosition(position)) return; + private boolean removeFromPosition(int position) { + if (!canRemoveFromPosition(position)) return false; TileInfo info = mTiles.get(position); move(position, info.isSystem ? mEditIndex : mTileDividerIndex); + return true; } public SpanSizeLookup getSizeLookup() { @@ -578,11 +580,17 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } private void add() { - addFromPosition(getLayoutPosition()); + if (addFromPosition(getLayoutPosition())) { + itemView.announceForAccessibility( + itemView.getContext().getText(R.string.accessibility_qs_edit_tile_added)); + } } private void remove() { - removeFromPosition(getLayoutPosition()); + if (removeFromPosition(getLayoutPosition())) { + itemView.announceForAccessibility( + itemView.getContext().getText(R.string.accessibility_qs_edit_tile_removed)); + } } boolean isCurrentTile() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 01a8c1c89f84..5b2a7e7ff617 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -620,6 +620,19 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override + public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { + if (!verifyCaller("exitSplitScreenOnHide")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void startTask(int taskId, int stage, int position, Bundle options) { if (!verifyCaller("startTask")) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index c816784a22f2..c70a93b5c894 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -16,10 +16,24 @@ package com.android.systemui.statusbar; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.app.ActivityManager; +import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -47,6 +61,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.ViewClippingUtil; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -56,13 +71,16 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; +import com.android.systemui.keyguard.KeyguardIndication; +import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -76,8 +94,7 @@ import javax.inject.Inject; * Controls the indications and error messages shown on the Keyguard */ @SysUISingleton -public class KeyguardIndicationController implements StateListener, - KeyguardStateController.Callback { +public class KeyguardIndicationController implements KeyguardStateController.Callback { private static final String TAG = "KeyguardIndication"; private static final boolean DEBUG_CHARGING_SPEED = false; @@ -94,14 +111,17 @@ public class KeyguardIndicationController implements StateListener, private final StatusBarStateController mStatusBarStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private ViewGroup mIndicationArea; - private KeyguardIndicationTextView mTextView; - private KeyguardIndicationTextView mDisclosure; + private KeyguardIndicationTextView mTopIndicationView; private final IBatteryStats mBatteryInfo; private final SettableWakeLock mWakeLock; private final DockManager mDockManager; private final DevicePolicyManager mDevicePolicyManager; private final UserManager mUserManager; + private final @Main DelayableExecutor mExecutor; + private final LockPatternUtils mLockPatternUtils; + private final IActivityManager mIActivityManager; + protected KeyguardIndicationRotateTextViewController mRotateTextViewController; private BroadcastReceiver mBroadcastReceiver; private LockscreenLockIconController mLockIconController; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -110,7 +130,7 @@ public class KeyguardIndicationController implements StateListener, private String mAlignmentIndication; private CharSequence mTransientIndication; private boolean mTransientTextIsError; - private ColorStateList mInitialTextColorState; + protected ColorStateList mInitialTextColorState; private boolean mVisible; private boolean mHideTransientMessageOnScreenOff; @@ -124,8 +144,8 @@ public class KeyguardIndicationController implements StateListener, private int mBatteryLevel; private boolean mBatteryPresent = true; private long mChargingTimeRemaining; - private float mDisclosureMaxAlpha; private String mMessageToShowOnScreenOn; + protected int mLockScreenMode; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; @@ -151,7 +171,8 @@ public class KeyguardIndicationController implements StateListener, BroadcastDispatcher broadcastDispatcher, DevicePolicyManager devicePolicyManager, IBatteryStats iBatteryStats, - UserManager userManager) { + UserManager userManager, + @Main DelayableExecutor executor) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mDevicePolicyManager = devicePolicyManager; @@ -165,23 +186,29 @@ public class KeyguardIndicationController implements StateListener, wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG); mBatteryInfo = iBatteryStats; mUserManager = userManager; + mExecutor = executor; + mLockPatternUtils = new LockPatternUtils(context); + mIActivityManager = ActivityManager.getService(); mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); mKeyguardUpdateMonitor.registerCallback(mTickReceiver); - mStatusBarStateController.addCallback(this); + mStatusBarStateController.addCallback(mStatusBarStateListener); mKeyguardStateController.addCallback(this); } public void setIndicationArea(ViewGroup indicationArea) { mIndicationArea = indicationArea; - mTextView = indicationArea.findViewById(R.id.keyguard_indication_text); - mInitialTextColorState = mTextView != null ? - mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE); - mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure); - mDisclosureMaxAlpha = mDisclosure.getAlpha(); + mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text); + mInitialTextColorState = mTopIndicationView != null + ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE); + mRotateTextViewController = new KeyguardIndicationRotateTextViewController( + indicationArea.findViewById(R.id.keyguard_indication_text_bottom), + mExecutor, + mStatusBarStateController, + mLockScreenMode); updateIndication(false /* animate */); updateDisclosure(); - + updateOwnerInfo(); if (mBroadcastReceiver == null) { // Update the disclosure proactively to avoid IPC on the critical path. mBroadcastReceiver = new BroadcastReceiver() { @@ -233,19 +260,196 @@ public class KeyguardIndicationController implements StateListener, return mUpdateMonitorCallback; } + /** + * Doesn't include owner information or disclosure which get triggered separately. + */ + private void updateIndications(boolean animate, int userId) { + updateBattery(animate); + updateUserLocked(userId); + updateTransient(); + updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication()); + updateAlignment(); + updateResting(); + } + private void updateDisclosure() { - // NOTE: Because this uses IPC, avoid calling updateDisclosure() on a critical path. if (whitelistIpcs(this::isOrganizationOwnedDevice)) { - CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); - if (organizationName != null) { - mDisclosure.switchIndication(mContext.getResources().getString( - R.string.do_disclosure_with_name, organizationName)); - } else { - mDisclosure.switchIndication(R.string.do_disclosure_generic); + final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); + final CharSequence disclosure = organizationName != null + ? mContext.getResources().getString(R.string.do_disclosure_with_name, + organizationName) + : mContext.getResources().getText(R.string.do_disclosure_generic); + mRotateTextViewController.updateIndication( + INDICATION_TYPE_DISCLOSURE, + new KeyguardIndication.Builder() + .setMessage(disclosure) + .setTextColor(mInitialTextColorState) + .build(), + /* updateImmediately */ false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE); + } + + if (isKeyguardLayoutEnabled()) { + updateIndication(false); // resting indication may need to update + } + } + + private void updateBattery(boolean animate) { + if (mPowerPluggedIn || mEnableBatteryDefender) { + String powerIndication = computePowerIndication(); + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; } - mDisclosure.setVisibility(View.VISIBLE); + + mRotateTextViewController.updateIndication( + INDICATION_TYPE_BATTERY, + new KeyguardIndication.Builder() + .setMessage(powerIndication) + .setTextColor(mInitialTextColorState) + .build(), + animate); } else { - mDisclosure.setVisibility(View.GONE); + // don't show the charging information if device isn't plugged in + mRotateTextViewController.hideIndication(INDICATION_TYPE_BATTERY); + } + } + + private void updateUserLocked(int userId) { + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_USER_LOCKED, + new KeyguardIndication.Builder() + .setMessage(mContext.getResources().getText( + com.android.internal.R.string.lockscreen_storage_locked)) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_USER_LOCKED); + } + } + + private void updateTransient() { + if (!TextUtils.isEmpty(mTransientIndication)) { + mRotateTextViewController.showTransient(mTransientIndication, + mTransientTextIsError); + } else { + mRotateTextViewController.hideTransient(); + } + } + + private void updateTrust(int userId, CharSequence trustGrantedIndication, + CharSequence trustManagedIndication) { + if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_TRUST, + new KeyguardIndication.Builder() + .setMessage(trustGrantedIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_TRUST, + new KeyguardIndication.Builder() + .setMessage(trustManagedIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_TRUST); + } + } + + private void updateAlignment() { + if (!TextUtils.isEmpty(mAlignmentIndication)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_ALIGNMENT, + new KeyguardIndication.Builder() + .setMessage(mAlignmentIndication) + .setTextColor(Utils.getColorError(mContext)) + .build(), + true); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_ALIGNMENT); + } + } + + private void updateResting() { + if (mRestingIndication != null + && !mRotateTextViewController.hasIndications()) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_RESTING, + new KeyguardIndication.Builder() + .setMessage(mRestingIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_RESTING); + } + } + + protected boolean isKeyguardLayoutEnabled() { + return mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1; + } + + private void updateLogoutView() { + if (!isKeyguardLayoutEnabled()) { + return; + } + final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled() + && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM; + String logoutString = shouldShowLogout ? mContext.getResources().getString( + com.android.internal.R.string.global_action_logout) : null; + mRotateTextViewController.updateIndication( + INDICATION_TYPE_LOGOUT, + new KeyguardIndication.Builder() + .setMessage(logoutString) + .setTextColor(mInitialTextColorState) + .setBackground(mContext.getDrawable( + com.android.systemui.R.drawable.logout_button_background)) + .setClickListener((view) -> { + int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); + try { + mIActivityManager.switchUser(UserHandle.USER_SYSTEM); + mIActivityManager.stopUser(currentUserId, true /* force */, null); + } catch (RemoteException re) { + Log.e(TAG, "Failed to logout user", re); + } + }) + .build(), + false); + updateIndication(false); // resting indication may need to update + } + + private void updateOwnerInfo() { + if (!isKeyguardLayoutEnabled()) { + return; + } + String info = mLockPatternUtils.getDeviceOwnerInfo(); + if (info == null) { + // Use the current user owner information if enabled. + final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled( + KeyguardUpdateMonitor.getCurrentUser()); + if (ownerInfoEnabled) { + info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser()); + } + } + if (info != null) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_OWNER_INFO, + new KeyguardIndication.Builder() + .setMessage(info) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + updateIndication(false); // resting indication may need to update } } @@ -281,9 +485,10 @@ public class KeyguardIndicationController implements StateListener, return UserHandle.USER_NULL; } - public void setVisible(boolean visible) { + @VisibleForTesting + protected void setVisible(boolean visible) { mVisible = visible; - mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE); + mIndicationArea.setVisibility(visible ? VISIBLE : GONE); if (visible) { // If this is called after an error message was already shown, we should not clear it. // Otherwise the error message won't be shown @@ -382,6 +587,7 @@ public class KeyguardIndicationController implements StateListener, mTransientIndication = null; mHideTransientMessageOnScreenOff = false; mHandler.removeMessages(MSG_HIDE_TRANSIENT); + mRotateTextViewController.hideTransient(); updateIndication(false); } } @@ -396,98 +602,109 @@ public class KeyguardIndicationController implements StateListener, } // A few places might need to hide the indication, so always start by making it visible - mIndicationArea.setVisibility(View.VISIBLE); + mIndicationArea.setVisibility(VISIBLE); // Walk down a precedence-ordered list of what indication // should be shown based on user or device state + // AoD if (mDozing) { + mTopIndicationView.setVisibility(VISIBLE); // When dozing we ignore any text color and use white instead, because // colors can be hard to read in low brightness. - mTextView.setTextColor(Color.WHITE); + mTopIndicationView.setTextColor(Color.WHITE); if (!TextUtils.isEmpty(mTransientIndication)) { - mTextView.switchIndication(mTransientIndication); + mTopIndicationView.switchIndication(mTransientIndication, null); } else if (!mBatteryPresent) { // If there is no battery detected, hide the indication and bail - mIndicationArea.setVisibility(View.GONE); + mIndicationArea.setVisibility(GONE); } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); + mTopIndicationView.switchIndication(mAlignmentIndication, null); + mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); } else if (mPowerPluggedIn || mEnableBatteryDefender) { String indication = computePowerIndication(); if (animate) { - animateText(mTextView, indication); + animateText(mTopIndicationView, indication); } else { - mTextView.switchIndication(indication); + mTopIndicationView.switchIndication(indication, null); } } else { String percentage = NumberFormat.getPercentInstance() .format(mBatteryLevel / 100f); - mTextView.switchIndication(percentage); + mTopIndicationView.switchIndication(percentage, null); } return; } + // LOCK SCREEN + // Some cases here might need to hide the indication (if the battery is not present) int userId = KeyguardUpdateMonitor.getCurrentUser(); - String trustGrantedIndication = getTrustGrantedIndication(); - String trustManagedIndication = getTrustManagedIndication(); - - String powerIndication = null; - if (mPowerPluggedIn || mEnableBatteryDefender) { - powerIndication = computePowerIndication(); - } - // Some cases here might need to hide the indication (if the battery is not present) - boolean hideIndication = false; - boolean isError = false; - if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { - mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); - } else if (!TextUtils.isEmpty(mTransientIndication)) { - if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - mTransientIndication, powerIndication); - mTextView.switchIndication(indication); - hideIndication = !mBatteryPresent; - } else { - mTextView.switchIndication(mTransientIndication); + if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) { + mTopIndicationView.setVisibility(GONE); + updateIndications(animate, userId); + } else { + boolean hideIndication = false; + boolean isError = false; + String trustGrantedIndication = getTrustGrantedIndication(); + String trustManagedIndication = getTrustManagedIndication(); + String powerIndication = null; + + if (mPowerPluggedIn || mEnableBatteryDefender) { + powerIndication = computePowerIndication(); } - isError = mTransientTextIsError; - } else if (!TextUtils.isEmpty(trustGrantedIndication) - && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - if (powerIndication != null) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - trustGrantedIndication, powerIndication); - mTextView.switchIndication(indication); + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mTopIndicationView.switchIndication( + com.android.internal.R.string.lockscreen_storage_locked); + } else if (!TextUtils.isEmpty(mTransientIndication)) { + if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + mTransientIndication, powerIndication); + mTopIndicationView.switchIndication(indication, null); + hideIndication = !mBatteryPresent; + } else { + mTopIndicationView.switchIndication(mTransientIndication, null); + } + isError = mTransientTextIsError; + } else if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + if (powerIndication != null) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + trustGrantedIndication, powerIndication); + mTopIndicationView.switchIndication(indication, null); + hideIndication = !mBatteryPresent; + } else { + mTopIndicationView.switchIndication(trustGrantedIndication, null); + } + } else if (!TextUtils.isEmpty(mAlignmentIndication)) { + mTopIndicationView.switchIndication(mAlignmentIndication, null); + isError = true; + hideIndication = !mBatteryPresent; + } else if (mPowerPluggedIn || mEnableBatteryDefender) { + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + } + if (animate) { + animateText(mTopIndicationView, powerIndication); + } else { + mTopIndicationView.switchIndication(powerIndication, null); + } hideIndication = !mBatteryPresent; + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mTopIndicationView.switchIndication(trustManagedIndication, null); } else { - mTextView.switchIndication(trustGrantedIndication); - } - } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - isError = true; - hideIndication = !mBatteryPresent; - } else if (mPowerPluggedIn || mEnableBatteryDefender) { - if (DEBUG_CHARGING_SPEED) { - powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + mTopIndicationView.switchIndication(mRestingIndication, null); } - if (animate) { - animateText(mTextView, powerIndication); - } else { - mTextView.switchIndication(powerIndication); + + mTopIndicationView.setTextColor( + isError ? Utils.getColorError(mContext) : mInitialTextColorState); + + if (hideIndication) { + mIndicationArea.setVisibility(GONE); } - hideIndication = !mBatteryPresent; - } else if (!TextUtils.isEmpty(trustManagedIndication) - && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) - && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - mTextView.switchIndication(trustManagedIndication); - } else { - mTextView.switchIndication(mRestingIndication); - } - mTextView.setTextColor(isError ? Utils.getColorError(mContext) - : mInitialTextColorState); - if (hideIndication) { - mIndicationArea.setVisibility(View.GONE); } } @@ -510,7 +727,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onAnimationStart(Animator animation) { - textView.switchIndication(indication); + textView.switchIndication(indication, null); } @Override @@ -634,18 +851,6 @@ public class KeyguardIndicationController implements StateListener, } } - public void setDozing(boolean dozing) { - if (mDozing == dozing) { - return; - } - mDozing = dozing; - if (mHideTransientMessageOnScreenOff && mDozing) { - hideTransientIndication(); - } else { - updateIndication(false); - } - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardIndicationController:"); pw.println(" mTransientTextIsError: " + mTransientTextIsError); @@ -659,23 +864,10 @@ public class KeyguardIndicationController implements StateListener, pw.println(" mDozing: " + mDozing); pw.println(" mBatteryLevel: " + mBatteryLevel); pw.println(" mBatteryPresent: " + mBatteryPresent); - pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText())); + pw.println(" mTextView.getText(): " + ( + mTopIndicationView == null ? null : mTopIndicationView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); - } - - @Override - public void onStateChanged(int newState) { - // don't care - } - - @Override - public void onDozingChanged(boolean isDozing) { - setDozing(isDozing); - } - - @Override - public void onDozeAmountChanged(float linear, float eased) { - mDisclosure.setAlpha((1 - linear) * mDisclosureMaxAlpha); + mRotateTextViewController.dump(fd, pw, args); } @Override @@ -687,6 +879,11 @@ public class KeyguardIndicationController implements StateListener, public static final int HIDE_DELAY_MS = 5000; @Override + public void onLockScreenModeChanged(int mode) { + mLockScreenMode = mode; + } + + @Override public void onRefreshBatteryInfo(BatteryStatus status) { boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING || status.status == BatteryManager.BATTERY_STATUS_FULL; @@ -845,6 +1042,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onUserSwitchComplete(int userId) { if (mVisible) { + updateOwnerInfo(); updateIndication(false); } } @@ -857,11 +1055,39 @@ public class KeyguardIndicationController implements StateListener, } @Override + public void onLogoutEnabledChanged() { + if (mVisible) { + updateLogoutView(); + } + } + + @Override public void onRequireUnlockForNfc() { showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc), false /* isError */, false /* hideOnScreenOff */); hideTransientIndicationDelayed(HIDE_DELAY_MS); } - } + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + setVisible(newState == StatusBarState.KEYGUARD); + } + + @Override + public void onDozingChanged(boolean dozing) { + if (mDozing == dozing) { + return; + } + mDozing = dozing; + + if (mHideTransientMessageOnScreenOff && mDozing) { + hideTransientIndication(); + } else { + updateIndication(false); + } + } + }; } 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 a03fc136da61..845d321416ee 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 @@ -71,6 +71,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; +import com.android.internal.widget.CallLayout; import com.android.internal.widget.MessagingLayout; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; @@ -165,6 +166,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mMaxSmallHeightLarge; private int mMaxSmallHeightMedia; private int mMaxExpandedHeight; + private int mMaxCallHeight; private int mIncreasedPaddingBetweenElements; private int mNotificationLaunchHeight; private boolean mMustStayOnScreen; @@ -645,8 +647,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private void updateLimitsForView(NotificationContentView layout) { - boolean customView = layout.getContractedChild() != null - && layout.getContractedChild().getId() + View contractedView = layout.getContractedChild(); + boolean customView = contractedView != null + && contractedView.getId() != com.android.internal.R.id.status_bar_latest_event_content; boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N; boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P; @@ -661,7 +664,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView View expandedView = layout.getExpandedChild(); boolean isMediaLayout = expandedView != null && expandedView.findViewById(com.android.internal.R.id.media_actions) != null; - boolean isMessagingLayout = layout.getContractedChild() instanceof MessagingLayout; + boolean isMessagingLayout = contractedView instanceof MessagingLayout; + boolean isCallLayout = contractedView instanceof CallLayout; boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar(); if (customView && beforeS && !mIsSummaryWithChildren) { @@ -684,6 +688,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // make sure we don't crop them terribly. We actually need to revisit this and give // them a headerless design, then remove this hack. smallHeight = mMaxSmallHeightLarge; + } else if (isCallLayout) { + smallHeight = mMaxCallHeight; } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) { smallHeight = mMaxSmallHeightLarge; } else { @@ -1645,6 +1651,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView R.dimen.notification_min_height_media); mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_max_height); + mMaxCallHeight = NotificationUtils.getFontScaledHeight(mContext, + R.dimen.call_notification_full_height); mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_max_heads_up_height_legacy); mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt new file mode 100644 index 000000000000..4541ebf4c4f2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.wrapper + +import android.content.Context +import android.view.View +import com.android.internal.widget.CachingIconView +import com.android.internal.widget.CallLayout +import com.android.systemui.R +import com.android.systemui.statusbar.notification.NotificationUtils +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow + +/** + * Wraps a notification containing a call template + */ +class NotificationCallTemplateViewWrapper constructor( + ctx: Context, + view: View, + row: ExpandableNotificationRow +) : NotificationTemplateViewWrapper(ctx, view, row) { + + private val minHeightWithActions: Int = + NotificationUtils.getFontScaledHeight(ctx, R.dimen.call_notification_full_height) + private val callLayout: CallLayout = view as CallLayout + + private lateinit var conversationIconView: CachingIconView + private lateinit var conversationBadgeBg: View + private lateinit var expandBtn: View + private lateinit var appName: View + private lateinit var conversationTitleView: View + + private fun resolveViews() { + with(callLayout) { + conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon) + conversationBadgeBg = + requireViewById(com.android.internal.R.id.conversation_icon_badge_bg) + expandBtn = requireViewById(com.android.internal.R.id.expand_button) + appName = requireViewById(com.android.internal.R.id.app_name_text) + conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text) + } + } + + override fun onContentUpdated(row: ExpandableNotificationRow) { + // Reinspect the notification. Before the super call, because the super call also updates + // the transformation types and we need to have our values set by then. + resolveViews() + super.onContentUpdated(row) + } + + override fun updateTransformedTypes() { + // This also clears the existing types + super.updateTransformedTypes() + addTransformedViews( + appName, + conversationTitleView + ) + addViewsTransformingToSimilar( + conversationIconView, + conversationBadgeBg, + expandBtn + ) + } + + override fun disallowSingleClick(x: Float, y: Float): Boolean { + val isOnExpandButton = expandBtn.visibility == View.VISIBLE && + isOnView(expandBtn, x, y) + return isOnExpandButton || super.disallowSingleClick(x, y) + } + + override fun getMinLayoutHeight(): Int = minHeightWithActions +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt index c49f6cbda8ac..905bccfa6cdf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row.wrapper import android.content.Context import android.view.View -import android.view.View.GONE import android.view.ViewGroup import com.android.internal.widget.CachingIconView import com.android.internal.widget.ConversationLayout @@ -48,8 +47,8 @@ class NotificationConversationTemplateViewWrapper constructor( private lateinit var conversationIconView: CachingIconView private lateinit var conversationBadgeBg: View - private lateinit var expandButton: View - private lateinit var expandButtonContainer: View + private lateinit var expandBtn: View + private lateinit var expandBtnContainer: View private lateinit var imageMessageContainer: ViewGroup private lateinit var messagingLinearLayout: MessagingLinearLayout private lateinit var conversationTitleView: View @@ -66,9 +65,8 @@ class NotificationConversationTemplateViewWrapper constructor( conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon) conversationBadgeBg = requireViewById(com.android.internal.R.id.conversation_icon_badge_bg) - expandButton = requireViewById(com.android.internal.R.id.expand_button) - expandButtonContainer = - requireViewById(com.android.internal.R.id.expand_button_container) + expandBtn = requireViewById(com.android.internal.R.id.expand_button) + expandBtnContainer = requireViewById(com.android.internal.R.id.expand_button_container) importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring) appName = requireViewById(com.android.internal.R.id.app_name_text) conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text) @@ -126,7 +124,7 @@ class NotificationConversationTemplateViewWrapper constructor( addViewsTransformingToSimilar( conversationIconView, conversationBadgeBg, - expandButton, + expandBtn, importanceRing, facePileTop, facePileBottom, @@ -134,11 +132,9 @@ class NotificationConversationTemplateViewWrapper constructor( ) } - override fun getExpandButton() = super.getExpandButton() - override fun setShelfIconVisible(visible: Boolean) { if (conversationLayout.isImportantConversation) { - if (conversationIconView.visibility != GONE) { + if (conversationIconView.visibility != View.GONE) { conversationIconView.isForceHidden = visible // We don't want the small icon to be hidden by the extended wrapper, as force // hiding the conversationIcon will already do that via its listener. @@ -152,7 +148,7 @@ class NotificationConversationTemplateViewWrapper constructor( override fun getShelfTransformationTarget(): View? = if (conversationLayout.isImportantConversation) - if (conversationIconView.visibility != GONE) + if (conversationIconView.visibility != View.GONE) conversationIconView else // A notification with a fallback icon was set to important. Currently @@ -169,8 +165,8 @@ class NotificationConversationTemplateViewWrapper constructor( conversationLayout.updateExpandability(expandable, onClickListener) override fun disallowSingleClick(x: Float, y: Float): Boolean { - val isOnExpandButton = expandButtonContainer.visibility == View.VISIBLE && - isOnView(expandButtonContainer, x, y) + val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE && + isOnView(expandBtnContainer, x, y) return isOnExpandButton || super.disallowSingleClick(x, y) } @@ -179,10 +175,4 @@ class NotificationConversationTemplateViewWrapper constructor( minHeightWithActions else super.getMinLayoutHeight() - - private fun addTransformedViews(vararg vs: View?) = - vs.forEach { view -> view?.let(mTransformationHelper::addTransformedView) } - - private fun addViewsTransformingToSimilar(vararg vs: View?) = - vs.forEach { view -> view?.let(mTransformationHelper::addViewTransformingToSimilar) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 97201f5c9a34..34bc5370e8f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -36,7 +36,6 @@ import android.widget.TextView; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.NotificationExpandButton; -import com.android.settingslib.Utils; import com.android.systemui.Interpolators; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.ViewTransformationHelper; @@ -60,6 +59,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private CachingIconView mIcon; private NotificationExpandButton mExpandButton; private View mAltExpandTarget; + private View mIconContainer; protected NotificationHeaderView mNotificationHeader; protected NotificationTopLineView mNotificationTopLine; private TextView mHeaderText; @@ -112,6 +112,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text); mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target); + mIconContainer = mView.findViewById(com.android.internal.R.id.conversation_icon_container); mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon); mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); @@ -203,11 +204,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { public void clearConversationSkin() { if (mAppNameText != null) { final ColorStateList colors = mAppNameText.getTextColors(); - final int textAppearance = Utils.getThemeAttr( - mAppNameText.getContext(), - com.android.internal.R.attr.notificationHeaderTextAppearance, + mAppNameText.setTextAppearance( com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info); - mAppNameText.setTextAppearance(textAppearance); mAppNameText.setTextColor(colors); MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams(); final int marginStart = mAppNameText.getResources().getDimensionPixelSize( @@ -265,19 +263,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon); mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_EXPANDER, mExpandButton); - if (mWorkProfileImage != null) { - mTransformationHelper.addViewTransformingToSimilar(mWorkProfileImage); - } if (mIsLowPriority && mHeaderText != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mHeaderText); } - if (mAudiblyAlertedIcon != null) { - mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon); - } - if (mFeedbackIcon != null) { - mTransformationHelper.addViewTransformingToSimilar(mFeedbackIcon); - } + addViewsTransformingToSimilar(mWorkProfileImage, mAudiblyAlertedIcon, mFeedbackIcon); } @Override @@ -287,6 +277,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { if (mAltExpandTarget != null) { mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null); } + if (mIconContainer != null) { + mIconContainer.setOnClickListener(expandable ? onClickListener : null); + } if (mNotificationHeader != null) { mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); } @@ -371,4 +364,20 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { super.setVisible(visible); mTransformationHelper.setVisible(visible); } + + protected void addTransformedViews(View... views) { + for (View view : views) { + if (view != null) { + mTransformationHelper.addTransformedView(view); + } + } + } + + protected void addViewsTransformingToSimilar(View... views) { + for (View view : views) { + if (view != null) { + mTransformationHelper.addViewTransformingToSimilar(view); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index b3d1a94beaa9..5fff8c83048f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -74,6 +74,8 @@ public abstract class NotificationViewWrapper implements TransformableView { return new NotificationMessagingTemplateViewWrapper(ctx, v, row); } else if ("conversation".equals(v.getTag())) { return new NotificationConversationTemplateViewWrapper(ctx, v, row); + } else if ("call".equals(v.getTag())) { + return new NotificationCallTemplateViewWrapper(ctx, v, row); } Class<? extends Notification.Style> style = row.getEntry().getSbn().getNotification().getNotificationStyle(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index dd1419f4ff42..2ce0a8776266 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -131,8 +131,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private KeyguardAffordanceView mLeftAffordanceView; private ImageView mAltLeftButton; private ViewGroup mIndicationArea; - private TextView mEnterpriseDisclosure; private TextView mIndicationText; + private TextView mIndicationTextBottom; private ViewGroup mPreviewContainer; private ViewGroup mOverlayContainer; @@ -251,9 +251,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLeftAffordanceView = findViewById(R.id.left_button); mAltLeftButton = findViewById(R.id.alt_left_button); mIndicationArea = findViewById(R.id.keyguard_indication_area); - mEnterpriseDisclosure = findViewById( - R.id.keyguard_indication_enterprise_disclosure); mIndicationText = findViewById(R.id.keyguard_indication_text); + mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom); mIndicationBottomMargin = getResources().getDimensionPixelSize( R.dimen.keyguard_indication_margin_bottom); mBurnInYOffset = getResources().getDimensionPixelSize( @@ -330,7 +329,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } // Respect font size setting. - mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize( com.android.internal.R.dimen.text_size_small_material)); mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index 30c951ac518c..8970a9a20931 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -16,11 +16,15 @@ package com.android.systemui.statusbar.phone; +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -28,6 +32,7 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Interpolators; +import com.android.systemui.keyguard.KeyguardIndication; import java.util.LinkedList; @@ -35,13 +40,15 @@ import java.util.LinkedList; * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open"). */ public class KeyguardIndicationTextView extends TextView { - private static final int FADE_OUT_MILLIS = 200; private static final int FADE_IN_MILLIS = 250; private static final long MSG_DURATION_MILLIS = 600; private long mNextAnimationTime = 0; private boolean mAnimationsEnabled = true; private LinkedList<CharSequence> mMessages = new LinkedList<>(); + private LinkedList<KeyguardIndication> mKeyguardIndicationInfo = new LinkedList<>(); + + private boolean mUseNewAnimations = false; public KeyguardIndicationTextView(Context context) { super(context); @@ -60,12 +67,33 @@ public class KeyguardIndicationTextView extends TextView { super(context, attrs, defStyleAttr, defStyleRes); } + public void setLockScreenMode(int lockScreenMode) { + mUseNewAnimations = lockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1; + } + + /** + * Changes the text with an animation and makes sure a single indication is shown long enough. + */ + public void switchIndication(int textResId) { + switchIndication(getResources().getText(textResId), null); + } + + /** + * Changes the text with an animation and makes sure a single indication is shown long enough. + * + * @param indication The text to show. + */ + public void switchIndication(KeyguardIndication indication) { + switchIndication(indication == null ? null : indication.getMessage(), indication); + } + /** * Changes the text with an animation and makes sure a single indication is shown long enough. * * @param text The text to show. + * @param indication optional display information for the text */ - public void switchIndication(CharSequence text) { + public void switchIndication(CharSequence text, KeyguardIndication indication) { if (text == null) text = ""; CharSequence lastPendingMessage = mMessages.peekLast(); @@ -74,55 +102,119 @@ public class KeyguardIndicationTextView extends TextView { return; } mMessages.add(text); + mKeyguardIndicationInfo.add(indication); - Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); - fadeOut.setDuration(getFadeOutMillis()); - fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - - final CharSequence nextText = text; - fadeOut.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - setText(mMessages.poll()); - } - }); - + final boolean hasIcon = indication != null && indication.getIcon() != null; final AnimatorSet animSet = new AnimatorSet(); - final AnimatorSet.Builder animSetBuilder = animSet.play(fadeOut); + final AnimatorSet.Builder animSetBuilder = animSet.play(getOutAnimator()); // Make sure each animation is visible for a minimum amount of time, while not worrying // about fading in blank text long timeInMillis = System.currentTimeMillis(); long delay = Math.max(0, mNextAnimationTime - timeInMillis); - setNextAnimationTime(timeInMillis + delay + getFadeOutMillis()); + setNextAnimationTime(timeInMillis + delay + getFadeOutDuration()); - if (!text.equals("")) { + if (!text.equals("") || hasIcon) { setNextAnimationTime(mNextAnimationTime + MSG_DURATION_MILLIS); - - ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); - fadeIn.setDuration(getFadeInMillis()); - fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - animSetBuilder.before(fadeIn); + animSetBuilder.before(getInAnimator()); } animSet.setStartDelay(delay); animSet.start(); } + private AnimatorSet getOutAnimator() { + AnimatorSet animatorSet = new AnimatorSet(); + Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); + fadeOut.setDuration(getFadeOutDuration()); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + fadeOut.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + KeyguardIndication info = mKeyguardIndicationInfo.poll(); + if (info != null) { + setTextColor(info.getTextColor()); + setOnClickListener(info.getClickListener()); + final Drawable icon = info.getIcon(); + if (icon != null) { + icon.setTint(getCurrentTextColor()); + if (icon instanceof AnimatedVectorDrawable) { + ((AnimatedVectorDrawable) icon).start(); + } + } + setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null); + } + setText(mMessages.poll()); + } + }); + + if (mUseNewAnimations) { + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels()); + yTranslate.setDuration(getFadeOutDuration()); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + animatorSet.playTogether(fadeOut, yTranslate); + } else { + animatorSet.play(fadeOut); + } + + return animatorSet; + } + + private AnimatorSet getInAnimator() { + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); + fadeIn.setStartDelay(getFadeInDelay()); + fadeIn.setDuration(getFadeInDuration()); + fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + + if (mUseNewAnimations) { + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, getYTranslationPixels(), 0); + yTranslate.setDuration(getYInDuration()); + yTranslate.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + setTranslationY(0); + } + }); + animatorSet.playTogether(yTranslate, fadeIn); + } else { + animatorSet.play(fadeIn); + } + + return animatorSet; + } + @VisibleForTesting public void setAnimationsEnabled(boolean enabled) { mAnimationsEnabled = enabled; } - private long getFadeInMillis() { - if (mAnimationsEnabled) return FADE_IN_MILLIS; + private long getFadeInDelay() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 150L; return 0L; } - private long getFadeOutMillis() { - if (mAnimationsEnabled) return FADE_OUT_MILLIS; + private long getFadeInDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 317L; + return FADE_IN_MILLIS; + } + + private long getYInDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 600L; return 0L; } + private long getFadeOutDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 167L; + return FADE_OUT_MILLIS; + } + private void setNextAnimationTime(long time) { if (mAnimationsEnabled) { mNextAnimationTime = time; @@ -131,10 +223,8 @@ public class KeyguardIndicationTextView extends TextView { } } - /** - * See {@link #switchIndication}. - */ - public void switchIndication(int textResId) { - switchIndication(getResources().getText(textResId)); + private int getYTranslationPixels() { + return mContext.getResources().getDimensionPixelSize( + com.android.systemui.R.dimen.keyguard_indication_y_translation); } } 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 061f2a8cc1a3..1e19beeff730 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1146,8 +1146,6 @@ public class StatusBar extends SystemUI implements DemoMode, mLockscreenWallpaper = mLockscreenWallpaperLazy.get(); } - mKeyguardIndicationController.setIndicationArea( - mNotificationShadeWindowView.findViewById(R.id.keyguard_indication_area)); mNotificationPanelViewController.setKeyguardIndicationController( mKeyguardIndicationController); @@ -3625,26 +3623,19 @@ public class StatusBar extends SystemUI implements DemoMode, mNavigationBarController.touchAutoDim(mDisplayId); Trace.beginSection("StatusBar#updateKeyguardState"); if (mState == StatusBarState.KEYGUARD) { - mKeyguardIndicationController.setVisible(true); if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.setKeyguard(true, mStatusBarStateController.fromShadeLocked()); } if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables(); - if (mAmbientIndicationContainer != null) { - mAmbientIndicationContainer.setVisibility(View.VISIBLE); - } } else { - mKeyguardIndicationController.setVisible(false); if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.setKeyguard(false, mStatusBarStateController.goingToFullShade() || mState == StatusBarState.SHADE_LOCKED || mStatusBarStateController.fromShadeLocked()); } - if (mAmbientIndicationContainer != null) { - mAmbientIndicationContainer.setVisibility(View.INVISIBLE); - } + } updateDozingState(); checkBarModes(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 4f4a504ddc9c..738cab15431a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -31,12 +31,11 @@ import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; -import android.net.IConnectivityManager; import android.net.Network; import android.net.NetworkRequest; +import android.net.VpnManager; import android.os.Handler; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.security.KeyChain; @@ -84,7 +83,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private final Context mContext; private final ConnectivityManager mConnectivityManager; - private final IConnectivityManager mConnectivityManagerService; + private final VpnManager mVpnManager; private final DevicePolicyManager mDevicePolicyManager; private final PackageManager mPackageManager; private final UserManager mUserManager; @@ -116,8 +115,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi context.getSystemService(Context.DEVICE_POLICY_SERVICE); mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - mConnectivityManagerService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + mVpnManager = context.getSystemService(VpnManager.class); mPackageManager = context.getPackageManager(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mBgExecutor = bgExecutor; @@ -399,25 +397,19 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private void updateState() { // Find all users with an active VPN SparseArray<VpnConfig> vpns = new SparseArray<>(); - try { - for (UserInfo user : mUserManager.getUsers()) { - VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id); - if (cfg == null) { + for (UserInfo user : mUserManager.getUsers()) { + VpnConfig cfg = mVpnManager.getVpnConfig(user.id); + if (cfg == null) { + continue; + } else if (cfg.legacy) { + // Legacy VPNs should do nothing if the network is disconnected. Third-party + // VPN warnings need to continue as traffic can still go to the app. + LegacyVpnInfo legacyVpn = mVpnManager.getLegacyVpnInfo(user.id); + if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) { continue; - } else if (cfg.legacy) { - // Legacy VPNs should do nothing if the network is disconnected. Third-party - // VPN warnings need to continue as traffic can still go to the app. - LegacyVpnInfo legacyVpn = mConnectivityManagerService.getLegacyVpnInfo(user.id); - if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) { - continue; - } } - vpns.put(user.id, cfg); } - } catch (RemoteException rme) { - // Roll back to previous state - Log.e(TAG, "Unable to list active VPNs", rme); - return; + vpns.put(user.id, cfg); } mCurrentVpns = vpns; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 0552396bab07..68d74ef760b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -113,7 +113,9 @@ public class UserSwitcherController implements Dumpable { private int mLastNonGuestUser = UserHandle.USER_SYSTEM; private boolean mResumeUserOnGuestLogout = true; private boolean mSimpleUserSwitcher; - private boolean mAddUsersWhenLocked; + // When false, there won't be any visual affordance to add a new user from the keyguard even if + // the user is unlocked + private boolean mAddUsersFromLockScreen; private boolean mPauseRefreshUsers; private int mSecondaryUser = UserHandle.USER_NULL; private Intent mSecondaryUserServiceIntent; @@ -204,7 +206,7 @@ public class UserSwitcherController implements Dumpable { } mForcePictureLoadForUserId.clear(); - final boolean addUsersWhenLocked = mAddUsersWhenLocked; + final boolean addUsersWhenLocked = mAddUsersFromLockScreen; new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() { @SuppressWarnings("unchecked") @Override @@ -554,7 +556,7 @@ public class UserSwitcherController implements Dumpable { private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { public void onChange(boolean selfChange) { mSimpleUserSwitcher = shouldUseSimpleUserSwitcher(); - mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(), + mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; refreshUsers(UserHandle.USER_NULL); }; @@ -610,43 +612,26 @@ public class UserSwitcherController implements Dumpable { } public int getUserCount() { - boolean secureKeyguardShowing = mKeyguardStateController.isShowing() - && mKeyguardStateController.isMethodSecure() - && !mKeyguardStateController.canDismissLockScreen(); - if (!secureKeyguardShowing) { - return getUsers().size(); - } - // The lock screen is secure and showing. Filter out restricted records. - final int userSize = getUsers().size(); - int count = 0; - for (int i = 0; i < userSize; i++) { - if (getUsers().get(i).isGuest) continue; - if (getUsers().get(i).isRestricted) { - break; - } else { - count++; - } - } - return count; + return countUsers(false); } @Override public int getCount() { - boolean secureKeyguardShowing = mKeyguardStateController.isShowing() - && mKeyguardStateController.isMethodSecure() - && !mKeyguardStateController.canDismissLockScreen(); - if (!secureKeyguardShowing) { - return getUsers().size(); - } - // The lock screen is secure and showing. Filter out restricted records. + return countUsers(true); + } + + private int countUsers(boolean includeGuest) { + boolean keyguardShowing = mKeyguardStateController.isShowing(); final int userSize = getUsers().size(); int count = 0; for (int i = 0; i < userSize; i++) { - if (getUsers().get(i).isRestricted) { + if (getUsers().get(i).isGuest && !includeGuest) { + continue; + } + if (getUsers().get(i).isRestricted && keyguardShowing) { break; - } else { - count++; } + count++; } return count; } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index a2eaea1a37c5..70a7b7a5acbc 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -34,6 +34,7 @@ import android.widget.FrameLayout; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -75,6 +76,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { NotificationIconAreaController mNotificationIconAreaController; @Mock ContentResolver mContentResolver; + @Mock + BroadcastDispatcher mBroadcastDispatcher; private KeyguardClockSwitchController mController; @@ -94,7 +97,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mClockManager, mKeyguardSliceViewController, mNotificationIconAreaController, - mContentResolver); + mContentResolver, + mBroadcastDispatcher); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt index 53d84dba6fb2..7b4f14dd3595 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt @@ -34,9 +34,9 @@ import org.mockito.Mockito.verify import kotlin.math.ceil -private val PAINT = arrayListOf(TextPaint().apply { +private val PAINT = TextPaint().apply { textSize = 32f -}) +} @RunWith(AndroidTestingRunner::class) @SmallTest @@ -49,10 +49,10 @@ class TextAnimatorTest : SysuiTestCase() { @Test fun testAnimationStarted() { - val layout = makeLayout("Hello, World", PAINT[0]) + val layout = makeLayout("Hello, World", PAINT) val valueAnimator = mock(ValueAnimator::class.java) val textInterpolator = mock(TextInterpolator::class.java) - val paint = arrayListOf(mock(TextPaint::class.java)) + val paint = mock(TextPaint::class.java) `when`(textInterpolator.targetPaint).thenReturn(paint) val textAnimator = TextAnimator(layout, {}).apply { @@ -81,10 +81,10 @@ class TextAnimatorTest : SysuiTestCase() { @Test fun testAnimationNotStarted() { - val layout = makeLayout("Hello, World", PAINT[0]) + val layout = makeLayout("Hello, World", PAINT) val valueAnimator = mock(ValueAnimator::class.java) val textInterpolator = mock(TextInterpolator::class.java) - val paint = arrayListOf(mock(TextPaint::class.java)) + val paint = mock(TextPaint::class.java) `when`(textInterpolator.targetPaint).thenReturn(paint) val textAnimator = TextAnimator(layout, {}).apply { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt index 1206dab7b56d..149e179de5a6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt @@ -21,9 +21,9 @@ import android.graphics.Canvas import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout -import android.text.TextPaint import android.text.TextDirectionHeuristic import android.text.TextDirectionHeuristics +import android.text.TextPaint import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -40,13 +40,13 @@ private val PAINT = TextPaint().apply { textSize = 32f } -private val START_PAINT = arrayListOf(TextPaint(PAINT).apply { +private val START_PAINT = TextPaint(PAINT).apply { fontVariationSettings = "'wght' 400" -}) +} -private val END_PAINT = arrayListOf(TextPaint(PAINT).apply { +private val END_PAINT = TextPaint(PAINT).apply { fontVariationSettings = "'wght' 700" -}) +} @RunWith(AndroidTestingRunner::class) @SmallTest @@ -67,16 +67,16 @@ class TextInterpolatorTest : SysuiTestCase() { val layout = makeLayout(TEXT, PAINT) val interp = TextInterpolator(layout) - TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.basePaint.set(START_PAINT) interp.onBasePaintModified() - TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.targetPaint.set(END_PAINT) interp.onTargetPaintModified() // Just after created TextInterpolator, it should have 0 progress. assertThat(interp.progress).isEqualTo(0f) val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(TEXT, START_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(TEXT, START_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() } @@ -86,15 +86,15 @@ class TextInterpolatorTest : SysuiTestCase() { val layout = makeLayout(TEXT, PAINT) val interp = TextInterpolator(layout) - TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.basePaint.set(START_PAINT) interp.onBasePaintModified() - TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.targetPaint.set(END_PAINT) interp.onTargetPaintModified() interp.progress = 1f val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(TEXT, END_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(TEXT, END_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() } @@ -104,10 +104,10 @@ class TextInterpolatorTest : SysuiTestCase() { val layout = makeLayout(TEXT, PAINT) val interp = TextInterpolator(layout) - TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.basePaint.set(START_PAINT) interp.onBasePaintModified() - TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.targetPaint.set(END_PAINT) interp.onTargetPaintModified() // We cannot expect exact text layout of the middle position since we don't use text shaping @@ -115,9 +115,9 @@ class TextInterpolatorTest : SysuiTestCase() { // end state. interp.progress = 0.5f val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0]) + assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT) .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse() - assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0]) + assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT) .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse() } @@ -126,10 +126,10 @@ class TextInterpolatorTest : SysuiTestCase() { val layout = makeLayout(TEXT, PAINT) val interp = TextInterpolator(layout) - TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.basePaint.set(START_PAINT) interp.onBasePaintModified() - TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.targetPaint.set(END_PAINT) interp.onTargetPaintModified() interp.progress = 0.5f @@ -148,16 +148,16 @@ class TextInterpolatorTest : SysuiTestCase() { val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR) val interp = TextInterpolator(layout) - TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.basePaint.set(START_PAINT) interp.onBasePaintModified() - TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.targetPaint.set(END_PAINT) interp.onTargetPaintModified() // Just after created TextInterpolator, it should have 0 progress. assertThat(interp.progress).isEqualTo(0f) val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.LTR) + val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.LTR) .toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() @@ -168,16 +168,16 @@ class TextInterpolatorTest : SysuiTestCase() { val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) val interp = TextInterpolator(layout) - TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.basePaint.set(START_PAINT) interp.onBasePaintModified() - TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.targetPaint.set(END_PAINT) interp.onTargetPaintModified() // Just after created TextInterpolator, it should have 0 progress. assertThat(interp.progress).isEqualTo(0f) val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.RTL) + val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL) .toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java new file mode 100644 index 000000000000..b6729adeb9b3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.keyguard; + + +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase { + + private static final String TEST_MESSAGE = "test message"; + private static final String TEST_MESSAGE_2 = "test message 2"; + + @Mock + private DelayableExecutor mExecutor; + @Mock + private KeyguardIndicationTextView mView; + @Mock + private StatusBarStateController mStatusBarStateController; + @Captor + private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; + + private KeyguardIndicationRotateTextViewController mController; + private StatusBarStateController.StateListener mStatusBarStateListener; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mView.getTextColors()).thenReturn(ColorStateList.valueOf(Color.WHITE)); + mController = new KeyguardIndicationRotateTextViewController(mView, mExecutor, + mStatusBarStateController, LOCK_SCREEN_MODE_LAYOUT_1); + mController.onViewAttached(); + + verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); + mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue(); + } + + @Test + public void testInitialState_noIndication() { + assertFalse(mController.hasIndications()); + } + + @Test + public void testShowOneIndication() { + // WHEN we add our first indication + final KeyguardIndication indication = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication, false); + + // THEN + // - we see controller has an indication + // - the indication shows immediately since it's the only one + // - no next indication is scheduled since there's only one indication + assertTrue(mController.hasIndications()); + verify(mView).switchIndication(indication); + verify(mExecutor, never()).executeDelayed(any(), anyLong()); + } + + @Test + public void testShowTwoRotatingMessages() { + // GIVEN we already have an indication message + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + reset(mView); + + // WHEN we have a new indication type to display + final KeyguardIndication indication2 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication2, false); + + // THEN + // - we don't immediately see the new message until the delay + // - next indication is scheduled + verify(mView, never()).switchIndication(indication2); + verify(mExecutor).executeDelayed(any(), anyLong()); + } + + @Test + public void testUpdateCurrentMessage() { + // GIVEN we already have an indication message + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + reset(mView); + + // WHEN we have a new message for this indication type to display + final KeyguardIndication indication2 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication2, false); + + // THEN + // - new indication is updated immediately + // - we don't schedule to show anything later + verify(mView).switchIndication(indication2); + verify(mExecutor, never()).executeDelayed(any(), anyLong()); + } + + @Test + public void testUpdateRotatingMessageForUndisplayedIndication() { + // GIVEN we already have two indication messages + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + reset(mView); + reset(mExecutor); + + // WHEN we have a new message for an undisplayed indication type + final KeyguardIndication indication3 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication3, false); + + // THEN + // - we don't immediately update + // - we don't schedule to show anything new + verify(mView, never()).switchIndication(indication3); + verify(mExecutor, never()).executeDelayed(any(), anyLong()); + } + + @Test + public void testUpdateImmediately() { + // GIVEN we already have three indication messages + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_BATTERY, createIndication(), false); + reset(mView); + reset(mExecutor); + + // WHEN we have a new message for a currently shown type that we want to show immediately + final KeyguardIndication indication4 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_BATTERY, indication4, true); + + // THEN + // - we immediately update + // - we schedule a new delayable to show the next message later + verify(mView).switchIndication(indication4); + verify(mExecutor).executeDelayed(any(), anyLong()); + + // WHEN an already existing type is updated to show immediately + reset(mView); + reset(mExecutor); + final KeyguardIndication indication5 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication5, true); + + // THEN + // - we immediately update + // - we schedule a new delayable to show the next message later + verify(mView).switchIndication(indication5); + verify(mExecutor).executeDelayed(any(), anyLong()); + } + + @Test + public void testTransientIndication() { + // GIVEN we already have two indication messages + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + reset(mView); + reset(mExecutor); + + // WHEN we have a transient message + mController.showTransient(TEST_MESSAGE_2, false); + + // THEN + // - we immediately update + // - we schedule a new delayable to show the next message later + verify(mView).switchIndication(any(KeyguardIndication.class)); + verify(mExecutor).executeDelayed(any(), anyLong()); + } + + @Test + public void testHideIndicationOneMessage() { + // GIVEN we have one indication message + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + + // WHEN we hide the current indication type + mController.hideIndication(INDICATION_TYPE_OWNER_INFO); + + // THEN we immediately update the text to show no text + verify(mView).switchIndication(null); + } + + @Test + public void testHideIndicationTwoMessages() { + // GIVEN we have two indication messages + final KeyguardIndication indication1 = createIndication(); + final KeyguardIndication indication2 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, indication1, false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication2, false); + assertTrue(mController.isNextIndicationScheduled()); + + // WHEN we hide the current indication type + mController.hideIndication(INDICATION_TYPE_OWNER_INFO); + + // THEN we show the next indication and there's no scheduled next indication + verify(mView).switchIndication(indication2); + assertFalse(mController.isNextIndicationScheduled()); + } + + @Test + public void testStartDozing() { + // WHEN the device is dozing + mStatusBarStateListener.onDozingChanged(true); + + // THEN the view is GONE + verify(mView).setVisibility(View.GONE); + } + + @Test + public void testStoppedDozing() { + // GIVEN we're dozing & we have an indication message + mStatusBarStateListener.onDozingChanged(true); + final KeyguardIndication indication = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication, false); + reset(mView); + reset(mExecutor); + + // WHEN the device is no longer dozing + mStatusBarStateListener.onDozingChanged(false); + + // THEN show the next message + verify(mView).switchIndication(indication); + } + + @Test + public void testIsDozing() { + // GIVEN the device is dozing + mStatusBarStateListener.onDozingChanged(true); + reset(mView); + + // WHEN an indication is updated + final KeyguardIndication indication = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication, false); + + // THEN no message is shown since we're dozing + verify(mView, never()).switchIndication(any()); + } + + private KeyguardIndication createIndication() { + return new KeyguardIndication.Builder() + .setMessage(TEST_MESSAGE) + .setTextColor(ColorStateList.valueOf(Color.WHITE)) + .build(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 6db21f9159ec..4ee2759028a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -17,6 +17,7 @@ package com.android.systemui.people; import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE; +import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; import static com.google.common.truth.Truth.assertThat; @@ -52,6 +53,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.ContactsContract; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; @@ -64,6 +66,9 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.SbnBuilder; +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; @@ -82,10 +87,17 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private static final int WIDGET_ID_WITH_SHORTCUT = 1; private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2; - private static final String SHORTCUT_ID = "101"; + private static final String SHORTCUT_ID_1 = "101"; + private static final String SHORTCUT_ID_2 = "202"; + private static final String SHORTCUT_ID_3 = "303"; + private static final String SHORTCUT_ID_4 = "404"; private static final String NOTIFICATION_KEY = "notification_key"; private static final String NOTIFICATION_CONTENT = "notification_content"; private static final String TEST_LOOKUP_KEY = "lookup_key"; + private static final String NOTIFICATION_TEXT_1 = "notification_text_1"; + private static final String NOTIFICATION_TEXT_2 = "notification_text_2"; + private static final String NOTIFICATION_TEXT_3 = "notification_text_3"; + private static final String NOTIFICATION_TEXT_4 = "notification_text_4"; private static final int TEST_COLUMN_INDEX = 1; private static final Uri URI = Uri.parse("fake_uri"); private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android); @@ -97,20 +109,66 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); private static final PeopleSpaceTile PERSON_TILE = new PeopleSpaceTile - .Builder(SHORTCUT_ID, "username", ICON, new Intent()) + .Builder(SHORTCUT_ID_1, "username", ICON, new Intent()) .setNotificationKey(NOTIFICATION_KEY) .setNotificationContent(NOTIFICATION_CONTENT) .setNotificationDataUri(URI) .build(); private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext, - SHORTCUT_ID).setLongLabel( + SHORTCUT_ID_1).setLongLabel( "name").setPerson(PERSON) .build(); private final ShortcutInfo mShortcutInfoWithoutPerson = new ShortcutInfo.Builder(mContext, - SHORTCUT_ID).setLongLabel( + SHORTCUT_ID_1).setLongLabel( "name") .build(); + private final Notification mNotification1 = new Notification.Builder(mContext, "test") + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID_1) + .setStyle(new Notification.MessagingStyle(PERSON) + .addMessage(new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_1, 0, PERSON)) + .addMessage(new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_2, 20, PERSON)) + .addMessage(new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_3, 10, PERSON)) + ) + .build(); + private final Notification mNotification2 = new Notification.Builder(mContext, "test2") + .setContentTitle("TEST_TITLE") + .setContentText("OTHER_TEXT") + .setShortcutId(SHORTCUT_ID_2) + .setStyle(new Notification.MessagingStyle(PERSON) + .addMessage(new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_4, 0, PERSON)) + ) + .build(); + private final Notification mNotification3 = new Notification.Builder(mContext, "test2") + .setContentTitle("TEST_TITLE") + .setContentText("OTHER_TEXT") + .setShortcutId(SHORTCUT_ID_3) + .setStyle(new Notification.MessagingStyle(PERSON)) + .build(); + private final NotificationEntry mNotificationEntry1 = new NotificationEntryBuilder() + .setNotification(mNotification1) + .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build()) + .setUser(UserHandle.of(0)) + .setPkg(PACKAGE_NAME) + .build(); + private final NotificationEntry mNotificationEntry2 = new NotificationEntryBuilder() + .setNotification(mNotification2) + .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_2).build()) + .setUser(UserHandle.of(0)) + .setPkg(PACKAGE_NAME) + .build(); + private final NotificationEntry mNotificationEntry3 = new NotificationEntryBuilder() + .setNotification(mNotification3) + .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_3).build()) + .setUser(UserHandle.of(0)) + .setPkg(PACKAGE_NAME) + .build(); @Mock private NotificationListener mListenerService; @@ -130,6 +188,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private ContentResolver mMockContentResolver; @Mock private Context mMockContext; + @Mock + private NotificationEntryManager mNotificationEntryManager; @Before public void setUp() throws RemoteException { @@ -152,15 +212,17 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { isNull())).thenReturn(mMockCursor); when(mMockContext.getString(R.string.birthday_status)).thenReturn( mContext.getString(R.string.birthday_status)); + when(mNotificationEntryManager.getVisibleNotifications()) + .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3)); } @Test public void testGetTilesReturnsSortedListWithMultipleRecentConversations() throws Exception { // Ensure the less-recent Important conversation is before more recent conversations. ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper( - SHORTCUT_ID, false, 3); + SHORTCUT_ID_1, false, 3); ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper( - SHORTCUT_ID + 1, + SHORTCUT_ID_1 + 1, true, 1); when(mNotificationManager.getConversations(anyBoolean())).thenReturn( new ParceledListSlice(Arrays.asList( @@ -169,9 +231,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { // Ensure the non-Important conversation is sorted between these recent conversations. ConversationChannel recentConversationBeforeNonImportantConversation = getConversationChannel( - SHORTCUT_ID + 2, 4); + SHORTCUT_ID_1 + 2, 4); ConversationChannel recentConversationAfterNonImportantConversation = - getConversationChannel(SHORTCUT_ID + 3, + getConversationChannel(SHORTCUT_ID_1 + 3, 2); when(mPeopleManager.getRecentConversations()).thenReturn( new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation, @@ -179,7 +241,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles( mContext, mNotificationManager, mPeopleManager, - mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList()); + mLauncherApps, mNotificationEntryManager) + .stream().map(tile -> tile.getId()).collect(Collectors.toList()); assertThat(orderedShortcutIds).containsExactly( // Even though the oldest conversation, should be first since "important" @@ -196,11 +259,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { throws Exception { // Ensure the less-recent Important conversation is before more recent conversations. ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper( - SHORTCUT_ID, false, 3); + SHORTCUT_ID_1, false, 3); ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper( - SHORTCUT_ID + 1, true, 3); + SHORTCUT_ID_1 + 1, true, 3); ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper( - SHORTCUT_ID + 2, + SHORTCUT_ID_1 + 2, true, 1); when(mNotificationManager.getConversations(anyBoolean())).thenReturn( new ParceledListSlice(Arrays.asList( @@ -210,9 +273,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { // Ensure the non-Important conversation is sorted between these recent conversations. ConversationChannel recentConversationBeforeNonImportantConversation = getConversationChannel( - SHORTCUT_ID + 3, 4); + SHORTCUT_ID_1 + 3, 4); ConversationChannel recentConversationAfterNonImportantConversation = - getConversationChannel(SHORTCUT_ID + 4, + getConversationChannel(SHORTCUT_ID_1 + 4, 2); when(mPeopleManager.getRecentConversations()).thenReturn( new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation, @@ -220,7 +283,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles( mContext, mNotificationManager, mPeopleManager, - mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList()); + mLauncherApps, mNotificationEntryManager) + .stream().map(tile -> tile.getId()).collect(Collectors.toList()); assertThat(orderedShortcutIds).containsExactly( // Important conversations should be sorted at the beginning. @@ -238,7 +302,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { Notification notification = new Notification.Builder(mContext, "test") .setContentTitle("TEST_TITLE") .setContentText("TEST_TEXT") - .setShortcutId(SHORTCUT_ID) + .setShortcutId(SHORTCUT_ID_1) .build(); StatusBarNotification sbn = new SbnBuilder() .setNotification(notification) @@ -341,24 +405,126 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { @Test public void testGetLastMessagingStyleMessage() { - Notification notification = new Notification.Builder(mContext, "test") - .setContentTitle("TEST_TITLE") - .setContentText("TEST_TEXT") - .setShortcutId(SHORTCUT_ID) - .setStyle(new Notification.MessagingStyle(PERSON) - .addMessage(new Notification.MessagingStyle.Message("text1", 0, PERSON)) - .addMessage(new Notification.MessagingStyle.Message("text2", 20, PERSON)) - .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON)) - ) - .build(); StatusBarNotification sbn = new SbnBuilder() - .setNotification(notification) + .setNotification(mNotification1) .build(); Notification.MessagingStyle.Message lastMessage = PeopleSpaceUtils.getLastMessagingStyleMessage(sbn); - assertThat(lastMessage.getText()).isEqualTo("text2"); + assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2); + } + + @Test + public void testAugmentTileFromNotification() { + StatusBarNotification sbn = new SbnBuilder() + .setNotification(mNotification1) + .build(); + + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUid(0) + .build(); + PeopleSpaceTile actual = PeopleSpaceUtils + .augmentTileFromNotification(tile, sbn); + + assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); + } + + @Test + public void testAugmentTileFromNotificationNoContent() { + StatusBarNotification sbn = new SbnBuilder() + .setNotification(mNotification3) + .build(); + + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUid(0) + .build(); + PeopleSpaceTile actual = PeopleSpaceUtils + .augmentTileFromNotification(tile, sbn); + + assertThat(actual.getNotificationKey()).isEqualTo(null); + assertThat(actual.getNotificationContent()).isEqualTo(null); + } + + @Test + public void testAugmentTileFromVisibleNotifications() { + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUid(0) + .build(); + PeopleSpaceTile actual = PeopleSpaceUtils + .augmentTileFromVisibleNotifications(tile, + Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); + + assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); + } + + @Test + public void testAugmentTileFromVisibleNotificationsDifferentShortcutId() { + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUid(0) + .build(); + PeopleSpaceTile actual = PeopleSpaceUtils + .augmentTileFromVisibleNotifications(tile, + Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); + + assertThat(actual.getNotificationContent()).isEqualTo(null); + } + + @Test + public void testAugmentTilesFromVisibleNotificationsSingleTile() { + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUid(0) + .build(); + List<PeopleSpaceTile> actualList = PeopleSpaceUtils + .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager); + + assertThat(actualList.size()).isEqualTo(1); + assertThat(actualList.get(0).getNotificationContent().toString()) + .isEqualTo(NOTIFICATION_TEXT_2); + + verify(mNotificationEntryManager, times(1)).getVisibleNotifications(); + } + + @Test + public void testAugmentTilesFromVisibleNotificationsMultipleTiles() { + PeopleSpaceTile tile1 = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUid(1) + .build(); + PeopleSpaceTile tile2 = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUid(0) + .build(); + List<PeopleSpaceTile> actualList = PeopleSpaceUtils + .augmentTilesFromVisibleNotifications(List.of(tile1, tile2), + mNotificationEntryManager); + + assertThat(actualList.size()).isEqualTo(2); + assertThat(actualList.get(0).getNotificationContent().toString()) + .isEqualTo(NOTIFICATION_TEXT_2); + assertThat(actualList.get(1).getNotificationContent().toString()) + .isEqualTo(NOTIFICATION_TEXT_4); + + verify(mNotificationEntryManager, times(1)).getVisibleNotifications(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index ef314ad16556..9470141178dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -421,6 +421,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext) .setContentTitle("TEST_TITLE") @@ -429,15 +430,21 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .build(); StatusBarNotification sbn = new SbnBuilder() .setNotification(notificationWithoutMessagingStyle) + .setPkg(TEST_PACKAGE_A) + .setUid(0) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mAppWidgetManager, never()) - .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); - verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle options = requireNonNull(mBundleArgumentCaptor.getValue()); + assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE)) + .isEqualTo(PERSON_TILE); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt index f86b4651056b..072f7b8a7756 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt @@ -100,12 +100,12 @@ class PrivacyDialogControllerTest : SysuiTestCase() { private val dialogProvider = object : PrivacyDialogController.DialogProvider { var list: List<PrivacyDialog.PrivacyElement>? = null - var starter: ((String) -> Unit)? = null + var starter: ((String, Int) -> Unit)? = null override fun makeDialog( context: Context, list: List<PrivacyDialog.PrivacyElement>, - starter: (String) -> Unit + starter: (String, Int) -> Unit ): PrivacyDialog { this.list = list this.starter = starter @@ -210,7 +210,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() { fun testSingleElementInList() { val usage = createMockPermGroupUsage( packageName = TEST_PACKAGE_NAME, - uid = generateUidForUser(0), + uid = generateUidForUser(USER_ID), permGroupName = PERM_CAMERA, lastAccess = 5L, isActive = true, @@ -224,6 +224,8 @@ class PrivacyDialogControllerTest : SysuiTestCase() { val expected = PrivacyDialog.PrivacyElement( type = PrivacyType.TYPE_CAMERA, + packageName = TEST_PACKAGE_NAME, + userId = USER_ID, applicationName = TEST_PACKAGE_NAME, attribution = TEST_ATTRIBUTION, lastActiveTimestamp = 5L, @@ -459,13 +461,28 @@ class PrivacyDialogControllerTest : SysuiTestCase() { controller.showDialog(context) exhaustExecutors() - dialogProvider.starter?.invoke(PERM_MICROPHONE) + dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID) + verify(activityStarter) + .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>()) + + assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSIONS) + assertThat(intentCaptor.value.getStringExtra(Intent.EXTRA_PACKAGE_NAME)) + .isEqualTo(TEST_PACKAGE_NAME) + assertThat(intentCaptor.value.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle) + .isEqualTo(UserHandle.of(USER_ID)) + } + + @Test + fun testStartActivityCorrectIntent_enterpriseUser() { + controller.showDialog(context) + exhaustExecutors() + + dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID) verify(activityStarter) .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>()) - assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_APPS) - assertThat(intentCaptor.value.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME)) - .isEqualTo(PERM_MICROPHONE) + assertThat(intentCaptor.value.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle) + .isEqualTo(UserHandle.of(ENT_USER_ID)) } @Test @@ -473,7 +490,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() { controller.showDialog(context) exhaustExecutors() - dialogProvider.starter?.invoke(PERM_MICROPHONE) + dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID) verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor)) activityStartedCaptor.value.onActivityStarted(ActivityManager.START_DELIVERED_TO_TOP) @@ -486,7 +503,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() { controller.showDialog(context) exhaustExecutors() - dialogProvider.starter?.invoke(PERM_MICROPHONE) + dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID) verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor)) activityStartedCaptor.value.onActivityStarted(ActivityManager.START_ABORTED) diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt index eb5dd4e6fef6..28b44d94effb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt @@ -40,8 +40,13 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper(setAsMainLooper = true) class PrivacyDialogTest : SysuiTestCase() { + companion object { + private const val TEST_PACKAGE_NAME = "test_pkg" + private const val TEST_USER_ID = 0 + } + @Mock - private lateinit var starter: (String) -> Unit + private lateinit var starter: (String, Int) -> Unit private lateinit var dialog: PrivacyDialog @@ -58,10 +63,12 @@ class PrivacyDialogTest : SysuiTestCase() { } @Test - fun testStarterCalledWithCorrectPermGroupName() { + fun testStarterCalledWithCorrectParams() { val list = listOf( PrivacyDialog.PrivacyElement( PrivacyType.TYPE_MICROPHONE, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", null, 0L, @@ -73,7 +80,7 @@ class PrivacyDialogTest : SysuiTestCase() { dialog = PrivacyDialog(context, list, starter) dialog.show() dialog.requireViewById<View>(R.id.privacy_item).callOnClick() - verify(starter).invoke(PrivacyType.TYPE_MICROPHONE.permGroupName) + verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID) } @Test @@ -104,6 +111,8 @@ class PrivacyDialogTest : SysuiTestCase() { val list = listOf( PrivacyDialog.PrivacyElement( PrivacyType.TYPE_CAMERA, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", null, 0L, @@ -113,6 +122,8 @@ class PrivacyDialogTest : SysuiTestCase() { ), PrivacyDialog.PrivacyElement( PrivacyType.TYPE_MICROPHONE, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", null, 0L, @@ -130,6 +141,8 @@ class PrivacyDialogTest : SysuiTestCase() { fun testUsingText() { val element = PrivacyDialog.PrivacyElement( PrivacyType.TYPE_CAMERA, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", null, 0L, @@ -154,6 +167,8 @@ class PrivacyDialogTest : SysuiTestCase() { fun testRecentText() { val element = PrivacyDialog.PrivacyElement( PrivacyType.TYPE_MICROPHONE, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", null, 0L, @@ -178,6 +193,8 @@ class PrivacyDialogTest : SysuiTestCase() { fun testEnterprise() { val element = PrivacyDialog.PrivacyElement( PrivacyType.TYPE_MICROPHONE, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", null, 0L, @@ -198,6 +215,8 @@ class PrivacyDialogTest : SysuiTestCase() { fun testPhoneCall() { val element = PrivacyDialog.PrivacyElement( PrivacyType.TYPE_MICROPHONE, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", null, 0L, @@ -218,6 +237,8 @@ class PrivacyDialogTest : SysuiTestCase() { fun testAttribution() { val element = PrivacyDialog.PrivacyElement( PrivacyType.TYPE_MICROPHONE, + TEST_PACKAGE_NAME, + TEST_USER_ID, "App", "attribution", 0L, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 2c96535b52bd..0cae67427e3b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; @@ -27,13 +29,11 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.Instrumentation; @@ -51,7 +51,6 @@ import android.os.BatteryManager; import android.os.Looper; import android.os.RemoteException; import android.os.UserManager; -import android.view.View; import android.view.ViewGroup; import androidx.test.InstrumentationRegistry; @@ -60,18 +59,21 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IBatteryStats; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; +import com.android.systemui.keyguard.KeyguardIndication; +import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wakelock.WakeLockFake; import org.junit.After; @@ -93,6 +95,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private static final String ORGANIZATION_NAME = "organization"; private String mDisclosureWithOrganization; + private String mDisclosureGeneric; @Mock private DevicePolicyManager mDevicePolicyManager; @@ -101,7 +104,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @Mock private KeyguardStateController mKeyguardStateController; @Mock - private KeyguardIndicationTextView mDisclosure; + private KeyguardIndicationTextView mIndicationAreaBottom; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock @@ -118,8 +121,20 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private IBatteryStats mIBatteryStats; @Mock private DockManager mDockManager; + @Mock + private KeyguardIndicationRotateTextViewController mRotateTextViewController; @Captor private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener; + @Captor + private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; + @Captor + private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + @Captor + private ArgumentCaptor<KeyguardIndication> mKeyguardIndicationCaptor; + private StatusBarStateController.StateListener mStatusBarStateListener; + private BroadcastReceiver mBroadcastReceiver; + private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + private KeyguardIndicationTextView mTextView; private KeyguardIndicationController mController; @@ -140,15 +155,15 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME); + mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic); when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true); when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); - when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure)) - .thenReturn(mDisclosure); + when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom)) + .thenReturn(mIndicationAreaBottom); when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); - when(mDisclosure.getAlpha()).thenReturn(1f); mWakeLock = new WakeLockFake(); mWakeLockBuilder = new WakeLockFake.Builder(mContext); @@ -168,11 +183,15 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController = new KeyguardIndicationController(mContext, mWakeLockBuilder, mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor, mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats, - mUserManager); + mUserManager, mExecutor); mController.setIndicationArea(mIndicationArea); + verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); + mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue(); + verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), any()); + mBroadcastReceiver = mBroadcastReceiverCaptor.getValue(); + mController.mRotateTextViewController = mRotateTextViewController; mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); clearInvocations(mIBatteryStats); - verify(mDisclosure).getAlpha(); } @Test @@ -223,7 +242,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); mController.setVisible(true); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR); }); @@ -241,7 +260,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); mController.setVisible(true); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE); }); @@ -255,136 +274,108 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @Test public void disclosure_unmanaged() { + createController(); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.GONE); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE); + verify(mRotateTextViewController, never()).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + any(), anyBoolean()); } @Test - public void disclosure_deviceOwner_noOwnerName() { + public void disclosure_deviceOwner_noOrganizationName() { + createController(); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureGeneric); } @Test - public void disclosure_orgOwnedDeviceWithManagedProfile_noOwnerName() { + public void disclosure_orgOwnedDeviceWithManagedProfile_noOrganizationName() { + createController(); when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true); when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList( new UserInfo(10, /* name */ null, /* flags */ FLAG_MANAGED_PROFILE))); when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(null); - createController(); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - } - - @Test - public void disclosure_hiddenWhenDozing() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - createController(); - - mController.setVisible(true); - mController.onDozeAmountChanged(1, 1); - mController.setDozing(true); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).setAlpha(0f); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureGeneric); } @Test - public void disclosure_visibleWhenDozing() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); + public void disclosure_deviceOwner_withOrganizationName() { createController(); - - mController.setVisible(true); - mController.onDozeAmountChanged(0, 0); - mController.setDozing(false); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).setAlpha(1f); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - } - - @Test - public void disclosure_deviceOwner_withOwnerName() { when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureWithOrganization); } @Test - public void disclosure_orgOwnedDeviceWithManagedProfile_withOwnerName() { + public void disclosure_orgOwnedDeviceWithManagedProfile_withOrganizationName() { + createController(); when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true); when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList( new UserInfo(10, /* name */ null, FLAG_MANAGED_PROFILE))); when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(ORGANIZATION_NAME); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureWithOrganization); } @Test public void disclosure_updateOnTheFly() { - ArgumentCaptor<BroadcastReceiver> receiver = ArgumentCaptor.forClass( - BroadcastReceiver.class); - doNothing().when(mBroadcastDispatcher).registerReceiver(receiver.capture(), any()); - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); createController(); - final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback(); - reset(mDisclosure); - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - receiver.getValue().onReceive(mContext, new Intent()); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - reset(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureGeneric); + reset(mRotateTextViewController); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME); - receiver.getValue().onReceive(mContext, new Intent()); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); - reset(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureWithOrganization); + reset(mRotateTextViewController); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); - receiver.getValue().onReceive(mContext, new Intent()); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.GONE); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE); } @Test public void transientIndication_holdsWakeLock_whenDozing() { createController(); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.showTransientIndication("Test"); assertTrue(mWakeLock.isHeld()); @@ -394,7 +385,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { public void transientIndication_releasesWakeLock_afterHiding() { createController(); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.showTransientIndication("Test"); mController.hideTransientIndication(); @@ -406,7 +397,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mInstrumentation.runOnMainSync(() -> { createController(); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.showTransientIndication("Test"); mController.hideTransientIndicationDelayed(0); }); @@ -425,7 +416,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController.setVisible(true); mController.showTransientIndication("Test"); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); assertThat(mTextView.getText()).isEqualTo("Test"); assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE); @@ -442,7 +433,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message, BiometricSourceType.FACE); assertThat(mTextView.getText()).isEqualTo(message); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); assertThat(mTextView.getText()).isNotEqualTo(message); } @@ -457,7 +448,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { "A message", BiometricSourceType.FACE); assertThat(mTextView.getText()).isEqualTo(message); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); assertThat(mTextView.getText()).isNotEqualTo(message); } @@ -528,8 +519,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { public void setDozing_noIBatteryCalls() throws RemoteException { createController(); mController.setVisible(true); - mController.setDozing(true); - mController.setDozing(false); + mStatusBarStateListener.onDozingChanged(true); + mStatusBarStateListener.onDozingChanged(false); verify(mIBatteryStats, never()).computeChargeTimeRemaining(); } @@ -537,7 +528,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { public void updateMonitor_listener() { createController(); verify(mKeyguardStateController).addCallback(eq(mController)); - verify(mStatusBarStateController).addCallback(eq(mController)); verify(mKeyguardUpdateMonitor, times(2)).registerCallback(any()); } @@ -612,10 +602,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { 0 /* maxChargingWattage */, true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.setVisible(true); String percentage = NumberFormat.getPercentInstance().format(90 / 100f); assertThat(mTextView.getText()).isEqualTo(percentage); } + + private void sendUpdateDisclosureBroadcast() { + mBroadcastReceiver.onReceive(mContext, new Intent()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java index 0e775047aef7..6068f0d17720 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java @@ -51,21 +51,21 @@ public class KeyguardIndicationTextViewTest extends SysuiTestCase { @Test public void switchIndication_null_hideIndication() { - mKeyguardIndicationTextView.switchIndication(null /* text */); + mKeyguardIndicationTextView.switchIndication(null /* text */, null); assertThat(mKeyguardIndicationTextView.getText()).isEqualTo(""); } @Test public void switchIndication_emptyText_hideIndication() { - mKeyguardIndicationTextView.switchIndication("" /* text */); + mKeyguardIndicationTextView.switchIndication("" /* text */, null); assertThat(mKeyguardIndicationTextView.getText()).isEqualTo(""); } @Test public void switchIndication_newText_updateProperly() { - mKeyguardIndicationTextView.switchIndication("test_indication" /* text */); + mKeyguardIndicationTextView.switchIndication("test_indication" /* text */, null); assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("test_indication"); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index 25f4abe2cb14..903a07140eae 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -19,12 +19,12 @@ package com.android.server.accessibility.gestures; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP; -import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP; @@ -147,15 +147,15 @@ class GestureManifold implements GestureMatcher.StateChangeListener { mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this)); mMultiFingerGestures.add( - new MultiFingerMultiTapAndHold( - mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this)); - mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this)); mMultiFingerGestures.add( new MultiFingerMultiTapAndHold( mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this)); mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this)); + mMultiFingerGestures.add( + new MultiFingerMultiTapAndHold( + mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, this)); // Three-finger taps. mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP, this)); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 67f654e6360f..6d72ca7bae25 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1492,7 +1492,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final Dataset dataset = (Dataset) result; final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx); - if (!isPinnedDataset(oldDataset)) { + if (!isAuthResultDatasetEphemeral(oldDataset, data)) { authenticatedResponse.getDatasets().set(datasetIdx, dataset); } autoFill(requestId, datasetIdx, dataset, false); @@ -1513,6 +1513,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** + * Returns whether the dataset returned from the authentication result is ephemeral or not. + * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more + * information. + */ + private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset, + @NonNull Bundle authResultData) { + if (authResultData.containsKey( + AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) { + return authResultData.getBoolean( + AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET); + } + return isPinnedDataset(oldDataset); + } + + /** * A dataset can potentially have multiple fields, and it's possible that some of the fields' * has inline presentation and some don't. It's also possible that some of the fields' * inline presentation is pinned and some isn't. So the concept of whether a dataset is diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 96b69dcc45d6..10b00d38fb42 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -161,6 +161,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private static final boolean DEBUG = false; private static final String LOG_TAG = "CompanionDeviceManagerService"; + private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min + private static final String PREF_FILE_NAME = "companion_device_preferences.xml"; private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done"; @@ -170,6 +172,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private static final String XML_ATTR_DEVICE = "device"; private static final String XML_ATTR_PROFILE = "profile"; private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby"; + private static final String XML_ATTR_TIME_APPROVED = "time_approved"; private static final String XML_FILE_NAME = "companion_device_manager_associations.xml"; private final CompanionDeviceManagerImpl mImpl; @@ -606,7 +609,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind association.getDeviceMacAddress(), association.getPackageName(), association.getDeviceProfile(), - active /* notifyOnDeviceNearby */); + active, /* notifyOnDeviceNearby */ + association.getTimeApprovedMs()); } else { return association; } @@ -639,6 +643,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } @Override + public boolean canPairWithoutPrompt( + String packageName, String deviceMacAddress, int userId) { + return CollectionUtils.any( + getAllAssociations(userId, packageName, deviceMacAddress), + a -> System.currentTimeMillis() - a.getTimeApprovedMs() + < PAIR_WITHOUT_PROMPT_WINDOW_MS); + } + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) throws RemoteException { @@ -877,6 +890,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind Boolean.toString( association.isNotifyOnDeviceNearby())); } + tag.attribute(null, XML_ATTR_TIME_APPROVED, + Long.toString(association.getTimeApprovedMs())); tag.endTag(null, XML_TAG_ASSOCIATION); }); @@ -921,7 +936,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } - @Nullable private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) { return CollectionUtils.filter( getAllAssociations(userId), @@ -941,6 +955,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } + private Set<Association> getAllAssociations( + int userId, @Nullable String packageFilter, @Nullable String addressFilter) { + return CollectionUtils.filter( + getAllAssociations(userId), + a -> Objects.equals(packageFilter, a.getPackageName()) + && Objects.equals(addressFilter, a.getDeviceMacAddress())); + } + private Set<Association> readAllAssociations(int userId) { final AtomicFile file = getStorageFileForUser(userId); @@ -962,12 +984,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE); final boolean persistentGrants = Boolean.valueOf( parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY)); + final long timeApproved = parseLongOrDefault( + parser.getAttributeValue(null, XML_ATTR_TIME_APPROVED), 0L); if (appPackage == null || deviceAddress == null) continue; result = ArrayUtils.add(result, new Association(userId, deviceAddress, appPackage, - profile, persistentGrants)); + profile, persistentGrants, timeApproved)); } return result; } catch (XmlPullParserException | IOException e) { @@ -1293,6 +1317,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return result; } + private static long parseLongOrDefault(String str, long def) { + try { + return Long.parseLong(str); + } catch (NumberFormatException e) { + Log.w(LOG_TAG, "Failed to parse", e); + return def; + } + } + private class ShellCmd extends ShellCommand { public static final String USAGE = "help\n" + "list USER_ID\n" @@ -1321,7 +1354,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind int userId = getNextArgInt(); String pkg = getNextArgRequired(); String address = getNextArgRequired(); - addAssociation(new Association(userId, address, pkg, null, false)); + addAssociation(new Association(userId, address, pkg, null, false, + System.currentTimeMillis())); } break; diff --git a/services/core/Android.bp b/services/core/Android.bp index 37d2cdc16926..83a5036f83a9 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -103,8 +103,7 @@ java_library_static { "android.hardware.gnss-V1-java", "android.hardware.power-V1-java", "android.hardware.power-V1.0-java", - "android.hardware.vibrator-V1-java", - "android.net.ipsec.ike.stubs.module_lib", + "android.hardware.vibrator-V2-java", "app-compat-annotations", "framework-tethering.stubs.module_lib", "service-permission.stubs.system_server", diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 6886cdefc28a..737a9e4ff46c 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.ComponentInfoFlags; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.component.ParsedMainComponent; import android.os.Bundle; import android.os.Handler; @@ -49,8 +50,8 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -535,17 +536,17 @@ public abstract class PackageManagerInternal { * Set which overlay to use for a package. * @param userId The user for which to update the overlays. * @param targetPackageName The package name of the package for which to update the overlays. - * @param overlayPackageNames The complete list of overlay packages that should be enabled for - * the target. Previously enabled overlays not specified in the list - * will be disabled. Pass in null or an empty list to disable - * all overlays. The order of the items is significant if several - * overlays modify the same resource. + * @param overlayPaths The complete list of overlay paths that should be enabled for + * the target. Previously enabled overlays not specified in the list + * will be disabled. Pass in null or empty paths to disable all overlays. + * The order of the items is significant if several overlays modify the + * same resource. * @param outUpdatedPackageNames An output list that contains the package names of packages * affected by the update of enabled overlays. * @return true if all packages names were known by the package manager, false otherwise */ public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName, - List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames); + @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames); /** * Resolves an activity intent, allowing instant apps to be resolved. diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 958c15c8d432..e996eb4ef674 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -17,6 +17,7 @@ package android.os; import com.android.internal.os.BinderCallsStats; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import java.util.Collection; @@ -37,6 +38,9 @@ public abstract class BatteryStatsInternal { */ public abstract String[] getMobileIfaces(); + /** Returns CPU times for system server thread groups. */ + public abstract SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes(); + /** * Inform battery stats how many deferred jobs existed when the app got launched and how * long ago was the last job execution for the app. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 2f883517a378..42b6a7f1aa87 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -132,12 +132,14 @@ import android.net.RouteInfo; import android.net.RouteInfoParcel; import android.net.SocketKeepalive; import android.net.TetheringManager; +import android.net.TransportInfo; import android.net.UidRange; import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; import android.net.VpnService; +import android.net.VpnTransportInfo; import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; @@ -5747,6 +5749,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new SecurityException("Insufficient permissions to specify legacy type"); } } + final NetworkCapabilities defaultNc = mDefaultRequest.mRequests.get(0).networkCapabilities; final int callingUid = mDeps.getCallingUid(); final NetworkRequest.Type reqType; try { @@ -5757,11 +5760,15 @@ public class ConnectivityService extends IConnectivityManager.Stub switch (reqType) { case TRACK_DEFAULT: // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities} - // is unused and will be replaced by the one from the default network request. - // This allows callers to keep track of the system default network. + // is unused and will be replaced by ones appropriate for the caller. + // This allows callers to keep track of the default network for their app. networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid); enforceAccessPermission(); break; + case TRACK_SYSTEM_DEFAULT: + enforceSettingsPermission(); + networkCapabilities = new NetworkCapabilities(defaultNc); + break; case BACKGROUND_REQUEST: enforceNetworkStackOrSettingsPermission(); // Fall-through since other checks are the same with normal requests. @@ -5780,6 +5787,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureRequestableCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, Binder.getCallingPid(), callingUid, callingPackageName); + // Set the UID range for this request to the single UID of the requester, or to an empty // set of UIDs if the caller has the appropriate permission and UIDs have not been set. // This will overwrite any allowed UIDs in the requested capabilities. Though there @@ -5799,6 +5807,16 @@ public class ConnectivityService extends IConnectivityManager.Stub new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag); if (DBG) log("requestNetwork for " + nri); + // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were + // copied from the default request above. (This is necessary to ensure, for example, that + // the callback does not leak sensitive information to unprivileged apps.) Check that the + // changes don't alter request matching. + if (reqType == NetworkRequest.Type.TRACK_SYSTEM_DEFAULT && + (!networkCapabilities.equalRequestableCapabilities(defaultNc))) { + Log.wtf(TAG, "TRACK_SYSTEM_DEFAULT capabilities don't match default request: " + + networkCapabilities + " vs. " + defaultNc); + } + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); if (timeoutMs > 0) { mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST, @@ -8462,22 +8480,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - /** - * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission - * for testing. - */ - private Vpn enforceActiveVpnOrNetworkStackPermission() { - if (checkNetworkStackPermission()) { - return null; - } - synchronized (mVpns) { - Vpn vpn = getVpnIfOwner(); - if (vpn != null) { - return vpn; - } - } - throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK " - + "permission"); + private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) { + if (vpn == null) return VpnManager.TYPE_VPN_NONE; + final TransportInfo ti = vpn.networkCapabilities.getTransportInfo(); + if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE; + return ((VpnTransportInfo) ti).type; } /** @@ -8487,14 +8494,6 @@ public class ConnectivityService extends IConnectivityManager.Stub * connection is not found. */ public int getConnectionOwnerUid(ConnectionInfo connectionInfo) { - final Vpn vpn = enforceActiveVpnOrNetworkStackPermission(); - - // Only VpnService based VPNs should be able to get this information. - if (vpn != null && vpn.getActiveAppVpnType() != VpnManager.TYPE_VPN_SERVICE) { - throw new SecurityException( - "getConnectionOwnerUid() not allowed for non-VpnService VPNs"); - } - if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) { throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol); } @@ -8502,8 +8501,15 @@ public class ConnectivityService extends IConnectivityManager.Stub final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol, connectionInfo.local, connectionInfo.remote); - /* Filter out Uids not associated with the VPN. */ - if (vpn != null && !vpn.appliesToUid(uid)) { + if (uid == INVALID_UID) return uid; // Not found. + + // Connection owner UIDs are visible only to the network stack and to the VpnService-based + // VPN, if any, that applies to the UID that owns the connection. + if (checkNetworkStackPermission()) return uid; + + final NetworkAgentInfo vpn = getVpnForUid(uid); + if (vpn == null || getVpnType(vpn) != VpnManager.TYPE_VPN_SERVICE + || vpn.networkCapabilities.getOwnerUid() != Binder.getCallingUid()) { return INVALID_UID; } diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java index c56cef2d58dc..a83c981235df 100644 --- a/services/core/java/com/android/server/EntropyMixer.java +++ b/services/core/java/com/android/server/EntropyMixer.java @@ -16,12 +16,6 @@ package com.android.server; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,10 +27,15 @@ import android.os.Message; import android.os.SystemProperties; import android.util.Slog; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; + /** * A service designed to load and periodically save "randomness" - * for the Linux kernel RNG and to mix in data from Hardware RNG (if present) - * into the Linux RNG. + * for the Linux kernel RNG. * * <p>When a Linux system starts up, the entropy pool associated with * {@code /dev/random} may be in a fairly predictable state. Applications which @@ -45,15 +44,8 @@ import android.util.Slog; * this effect, it's helpful to carry the entropy pool information across * shutdowns and startups. * - * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW - * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever - * EntropyMixer periodically runs to save a block of output from Linux RNG on - * disk. This mixing is done in a way that does not increase the Linux RNG's - * entropy estimate is not increased. This is to avoid having to trust/verify - * the quality and authenticity of the "randomness" of the HW RNG. - * * <p>This class was modeled after the script in the - * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html"> + * <a href="https://man7.org/linux/man-pages/man4/random.4.html"> * random(4) manual page</a>. */ public class EntropyMixer extends Binder { @@ -64,7 +56,6 @@ public class EntropyMixer extends Binder { private static final long START_NANOTIME = System.nanoTime(); private final String randomDevice; - private final String hwRandomDevice; private final String entropyFile; /** @@ -80,7 +71,6 @@ public class EntropyMixer extends Binder { Slog.e(TAG, "Will not process invalid message"); return; } - addHwRandomEntropy(); writeEntropy(); scheduleEntropyWriter(); } @@ -94,25 +84,21 @@ public class EntropyMixer extends Binder { }; public EntropyMixer(Context context) { - this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random"); + this(context, getSystemDir() + "/entropy.dat", "/dev/urandom"); } /** Test only interface, not for public use */ public EntropyMixer( Context context, String entropyFile, - String randomDevice, - String hwRandomDevice) { + String randomDevice) { if (randomDevice == null) { throw new NullPointerException("randomDevice"); } - if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); } if (entropyFile == null) { throw new NullPointerException("entropyFile"); } this.randomDevice = randomDevice; - this.hwRandomDevice = hwRandomDevice; this.entropyFile = entropyFile; loadInitialEntropy(); addDeviceSpecificEntropy(); - addHwRandomEntropy(); writeEntropy(); scheduleEntropyWriter(); IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); @@ -192,23 +178,6 @@ public class EntropyMixer extends Binder { } } - /** - * Mixes in the output from HW RNG (if present) into the Linux RNG. - */ - private void addHwRandomEntropy() { - if (!new File(hwRandomDevice).exists()) { - // HW RNG not present/exposed -- ignore - return; - } - - try { - RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false); - Slog.i(TAG, "Added HW RNG output to entropy pool"); - } catch (IOException e) { - Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e); - } - } - private static String getSystemDir() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 7d6515600c2a..1ad0176d3c5b 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -890,7 +890,7 @@ class StorageManagerService extends IStorageManager.Stub ZramWriteback.scheduleZramWriteback(mContext); } - updateTranscodeEnabled(); + configureTranscoding(); } /** @@ -922,7 +922,7 @@ class StorageManagerService extends IStorageManager.Stub } } - private void updateTranscodeEnabled() { + private void configureTranscoding() { // See MediaProvider TranscodeHelper#getBooleanProperty for more information boolean transcodeEnabled = false; boolean defaultValue = true; @@ -935,6 +935,15 @@ class StorageManagerService extends IStorageManager.Stub "transcode_enabled", defaultValue); } SystemProperties.set("sys.fuse.transcode_enabled", String.valueOf(transcodeEnabled)); + + if (transcodeEnabled) { + LocalServices.getService(ActivityManagerInternal.class) + .registerAnrController((packageName, uid) -> { + // TODO: Retrieve delay from ExternalStorageService that can check + // transcoding status + return SystemProperties.getInt("sys.fuse.transcode_anr_delay_ms", 0); + }); + } } /** diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 4dce59f23a79..27210daac241 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -641,21 +641,34 @@ public class VcnManagementService extends IVcnManagementService.Stub { } boolean isVcnManagedNetwork = false; + boolean isRestrictedCarrierWifi = false; if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { synchronized (mLock) { ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId); Vcn vcn = mVcns.get(subGroup); - if (vcn != null && vcn.isActive()) { - isVcnManagedNetwork = true; + if (vcn != null) { + if (vcn.isActive()) { + isVcnManagedNetwork = true; + } + + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + // Carrier WiFi always restricted if VCN exists (even in safe mode). + isRestrictedCarrierWifi = true; + } } } } + if (isVcnManagedNetwork) { networkCapabilities.removeCapability( NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); } + if (isRestrictedCarrierWifi) { + networkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + } + return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); } diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java index 6a816af68959..e7e5d67ff9f4 100644 --- a/services/core/java/com/android/server/VibratorManagerService.java +++ b/services/core/java/com/android/server/VibratorManagerService.java @@ -111,6 +111,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private final AppOpsManager mAppOps; private final NativeWrapper mNativeWrapper; private final VibratorManagerRecords mVibratorManagerRecords; + private final long mCapabilities; private final int[] mVibratorIds; private final SparseArray<VibratorController> mVibrators; private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks(); @@ -127,18 +128,28 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private VibrationScaler mVibrationScaler; private InputDeviceDelegate mInputDeviceDelegate; - static native long nativeInit(); + static native long nativeInit(OnSyncedVibrationCompleteListener listener); static native long nativeGetFinalizer(); + static native long nativeGetCapabilities(long nativeServicePtr); + static native int[] nativeGetVibratorIds(long nativeServicePtr); + static native boolean nativePrepareSynced(long nativeServicePtr, int[] vibratorIds); + + static native boolean nativeTriggerSynced(long nativeServicePtr, long vibrationId); + + static native void nativeCancelSynced(long nativeServicePtr); + @VisibleForTesting VibratorManagerService(Context context, Injector injector) { mContext = context; mHandler = injector.createHandler(Looper.myLooper()); + + VibrationCompleteListener listener = new VibrationCompleteListener(this); mNativeWrapper = injector.getNativeWrapper(); - mNativeWrapper.init(); + mNativeWrapper.init(listener); int dumpLimit = mContext.getResources().getInteger( com.android.internal.R.integer.config_previousVibrationsDumpLimit); @@ -153,6 +164,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); mWakeLock.setReferenceCounted(true); + mCapabilities = mNativeWrapper.getCapabilities(); int[] vibratorIds = mNativeWrapper.getVibratorIds(); if (vibratorIds == null) { mVibratorIds = new int[0]; @@ -161,11 +173,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // Keep original vibrator id order, which might be meaningful. mVibratorIds = vibratorIds; mVibrators = new SparseArray<>(mVibratorIds.length); - VibrationCompleteListener listener = new VibrationCompleteListener(this); for (int vibratorId : vibratorIds) { mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener)); } } + + // Reset the hardware to a default state, in case this is a runtime restart instead of a + // fresh boot. + mNativeWrapper.cancelSynced(); + for (int i = 0; i < mVibrators.size(); i++) { + mVibrators.valueAt(i).off(); + } } /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */ @@ -502,6 +520,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } + private void onSyncedVibrationComplete(long vibrationId) { + synchronized (mLock) { + if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) { + if (DEBUG) { + Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread"); + } + mCurrentVibration.syncedVibrationComplete(); + } + } + } + private void onVibrationComplete(int vibratorId, long vibrationId) { synchronized (mLock) { if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) { @@ -839,13 +868,22 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks { @Override - public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) { - // TODO(b/167946816): call IVibratorManager to prepare + public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) { + if ((mCapabilities & requiredCapabilities) != requiredCapabilities) { + // This sync step requires capabilities this device doesn't have, skipping sync... + return false; + } + return mNativeWrapper.prepareSynced(vibratorIds); } @Override - public void triggerSyncedVibration(long vibrationId) { - // TODO(b/167946816): call IVibratorManager to trigger + public boolean triggerSyncedVibration(long vibrationId) { + return mNativeWrapper.triggerSynced(vibrationId); + } + + @Override + public void cancelSyncedVibration() { + mNativeWrapper.cancelSynced(); } @Override @@ -868,12 +906,19 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } + /** Listener for synced vibration completion callbacks from native. */ + @VisibleForTesting + public interface OnSyncedVibrationCompleteListener { + + /** Callback triggered when synced vibration is complete. */ + void onComplete(long vibrationId); + } + /** - * Implementation of {@link VibratorController.OnVibrationCompleteListener} with a weak - * reference to this service. + * Implementation of listeners to native vibrators with a weak reference to this service. */ private static final class VibrationCompleteListener implements - VibratorController.OnVibrationCompleteListener { + VibratorController.OnVibrationCompleteListener, OnSyncedVibrationCompleteListener { private WeakReference<VibratorManagerService> mServiceRef; VibrationCompleteListener(VibratorManagerService service) { @@ -881,6 +926,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @Override + public void onComplete(long vibrationId) { + VibratorManagerService service = mServiceRef.get(); + if (service != null) { + service.onSyncedVibrationComplete(vibrationId); + } + } + + @Override public void onComplete(int vibratorId, long vibrationId) { VibratorManagerService service = mServiceRef.get(); if (service != null) { @@ -952,9 +1005,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private long mNativeServicePtr = 0; /** Returns native pointer to newly created controller and connects with HAL service. */ - public void init() { - mNativeServicePtr = VibratorManagerService.nativeInit(); - long finalizerPtr = VibratorManagerService.nativeGetFinalizer(); + public void init(OnSyncedVibrationCompleteListener listener) { + mNativeServicePtr = nativeInit(listener); + long finalizerPtr = nativeGetFinalizer(); if (finalizerPtr != 0) { NativeAllocationRegistry registry = @@ -964,9 +1017,29 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } + /** Returns manager capabilities. */ + public long getCapabilities() { + return nativeGetCapabilities(mNativeServicePtr); + } + /** Returns vibrator ids. */ public int[] getVibratorIds() { - return VibratorManagerService.nativeGetVibratorIds(mNativeServicePtr); + return nativeGetVibratorIds(mNativeServicePtr); + } + + /** Prepare vibrators for triggering vibrations in sync. */ + public boolean prepareSynced(@NonNull int[] vibratorIds) { + return nativePrepareSynced(mNativeServicePtr, vibratorIds); + } + + /** Trigger prepared synced vibration. */ + public boolean triggerSynced(long vibrationId) { + return nativeTriggerSynced(mNativeServicePtr, vibrationId); + } + + /** Cancel prepared synced vibration. */ + public void cancelSynced() { + nativeCancelSynced(mNativeServicePtr); } } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 026eb630d59a..2ac365d6d11b 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -119,11 +119,17 @@ public class VibratorService extends IVibratorService.Stub { private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks { @Override - public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) { + public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) { + return false; + } + + @Override + public boolean triggerSyncedVibration(long vibrationId) { + return false; } @Override - public void triggerSyncedVibration(long vibrationId) { + public void cancelSyncedVibration() { } @Override diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9382e1aa2a9e..f0f29a9be7a7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10700,13 +10700,13 @@ public class ActivityManagerService extends IActivityManager.Stub long kernelUsed = memInfo.getKernelUsedSizeKb(); final long ionHeap = Debug.getIonHeapsSizeKb(); final long ionPool = Debug.getIonPoolsSizeKb(); + final long dmabufMapped = Debug.getDmabufMappedSizeKb(); if (ionHeap >= 0 && ionPool >= 0) { - final long ionMapped = Debug.getIonMappedSizeKb(); - final long ionUnmapped = ionHeap - ionMapped; + final long ionUnmapped = ionHeap - dmabufMapped; pw.print(" ION: "); pw.print(stringifyKBSize(ionHeap + ionPool)); pw.print(" ("); - pw.print(stringifyKBSize(ionMapped)); + pw.print(stringifyKBSize(dmabufMapped)); pw.print(" mapped + "); pw.print(stringifyKBSize(ionUnmapped)); pw.print(" unmapped + "); @@ -10715,11 +10715,34 @@ public class ActivityManagerService extends IActivityManager.Stub // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being // set on ION VMAs, therefore consider the entire ION heap as used kernel memory kernelUsed += ionHeap; + } else { + final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb(); + if (totalExportedDmabuf >= 0) { + final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped; + pw.print("DMA-BUF: "); + pw.print(stringifyKBSize(totalExportedDmabuf)); + pw.print(" ("); + pw.print(stringifyKBSize(dmabufMapped)); + pw.print(" mapped + "); + pw.print(stringifyKBSize(dmabufUnmapped)); + pw.println(" unmapped)"); + kernelUsed += totalExportedDmabuf; + } + final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb(); + if (totalDmabufHeapPool >= 0) { + pw.print("DMA-BUF Heaps pool: "); + pw.println(stringifyKBSize(totalDmabufHeapPool)); + } } final long gpuUsage = Debug.getGpuTotalUsageKb(); if (gpuUsage >= 0) { pw.print(" GPU: "); pw.println(stringifyKBSize(gpuUsage)); } + + /* + * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of + * memInfo.getCachedSizeKb(). + */ final long lostRAM = memInfo.getTotalSizeKb() - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS]) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index 8f11a5ab900b..3ff58729f807 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1512,15 +1512,29 @@ public class AppProfiler { final long ionHeap = Debug.getIonHeapsSizeKb(); final long ionPool = Debug.getIonPoolsSizeKb(); if (ionHeap >= 0 && ionPool >= 0) { - final long ionMapped = Debug.getIonMappedSizeKb(); - final long ionUnmapped = ionHeap - ionMapped; memInfoBuilder.append(" ION: "); memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool)); memInfoBuilder.append("\n"); // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being // set on ION VMAs, therefore consider the entire ION heap as used kernel memory kernelUsed += ionHeap; + } else { + final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb(); + if (totalExportedDmabuf >= 0) { + memInfoBuilder.append("DMA-BUF: "); + memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf)); + memInfoBuilder.append("\n"); + kernelUsed += totalExportedDmabuf; + } + + final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb(); + if (totalDmabufHeapPool >= 0) { + memInfoBuilder.append("DMA-BUF Heaps pool: "); + memInfoBuilder.append(stringifyKBSize(totalDmabufHeapPool)); + memInfoBuilder.append("\n"); + } } + final long gpuUsage = Debug.getGpuTotalUsageKb(); if (gpuUsage >= 0) { memInfoBuilder.append(" GPU: "); @@ -1531,6 +1545,11 @@ public class AppProfiler { memInfoBuilder.append(stringifyKBSize( totalPss - cachedPss + kernelUsed)); memInfoBuilder.append("\n"); + + /* + * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of + * memInfo.getCachedSizeKb(). + */ memInfoBuilder.append(" Lost RAM: "); memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb() - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 773e3137c247..6500f6d1b6d3 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -72,6 +72,7 @@ import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ParseUtils; @@ -126,15 +127,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .replaceWith("?"); - private static final int MAX_LOW_POWER_STATS_SIZE = 4096; + private static final int MAX_LOW_POWER_STATS_SIZE = 8192; private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000; + private static final String EMPTY = "Empty"; private final HandlerThread mHandlerThread; private final Handler mHandler; private final Object mLock = new Object(); + private final Object mPowerStatsLock = new Object(); + @GuardedBy("mPowerStatsLock") private PowerStatsInternal mPowerStatsInternal = null; + @GuardedBy("mPowerStatsLock") private Map<Integer, String> mEntityNames = new HashMap(); + @GuardedBy("mPowerStatsLock") private Map<Integer, Map<Integer, String>> mStateNames = new HashMap(); @GuardedBy("mStats") @@ -172,13 +178,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub }; private void populatePowerEntityMaps() { - if (mPowerStatsInternal == null) { - // PowerStatsInternal unavailable, don't bother populating maps. - mEntityNames = null; - mStateNames = null; - return; - } - PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo(); if (entities == null) { return; @@ -202,6 +201,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub */ @Override public void fillLowPowerStats(RpmStats rpmStats) { + synchronized (mPowerStatsLock) { + if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) { + return; + } + } + final StateResidencyResult[] results; try { results = mPowerStatsInternal.getStateResidencyAsync(new int[0]) @@ -237,16 +242,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public String getSubsystemLowPowerStats() { + synchronized (mPowerStatsLock) { + if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) { + return EMPTY; + } + } + final StateResidencyResult[] results; try { results = mPowerStatsInternal.getStateResidencyAsync(new int[0]) .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } catch (Exception e) { Slog.e(TAG, "Failed to getStateResidencyAsync", e); - return "Empty"; + return EMPTY; } - if (results.length == 0) return "Empty"; + if (results.length == 0) return EMPTY; int charsLeft = MAX_LOW_POWER_STATS_SIZE; StringBuilder builder = new StringBuilder("SubsystemPowerState"); @@ -322,9 +333,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub } catch (RemoteException e) { Slog.e(TAG, "Could not register INetworkManagement event observer " + e); } - mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class); - if (mPowerStatsInternal != null) { - populatePowerEntityMaps(); + + synchronized (mPowerStatsLock) { + mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class); + if (mPowerStatsInternal != null) { + populatePowerEntityMaps(); + } else { + Slog.e(TAG, "Could not register PowerStatsInternal"); + } } Watchdog.getInstance().addMonitor(this); @@ -342,6 +358,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override + public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() { + return mStats.getSystemServiceCpuThreadTimes(); + } + + @Override public void noteJobsDeferred(int uid, int numDeferred, long sinceLast) { if (DBG) Slog.d(TAG, "Jobs deferred " + uid + ": " + numDeferred + " " + sinceLast); BatteryStatsService.this.noteJobsDeferred(uid, numDeferred, sinceLast); diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index c18031fd6de6..7bdf43c9c744 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -538,6 +538,12 @@ public final class CachedAppOptimizer { private static native int getBinderFreezeInfo(int pid); /** + * Returns the path to be checked to verify whether the freezer is supported by this system. + * @return absolute path to the file + */ + private static native String getFreezerCheckPath(); + + /** * Determines whether the freezer is supported by this system */ public static boolean isFreezerSupported() { @@ -545,11 +551,15 @@ public final class CachedAppOptimizer { FileReader fr = null; try { - fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze"); + fr = new FileReader(getFreezerCheckPath()); char state = (char) fr.read(); if (state == '1' || state == '0') { supported = true; + // This is a workaround after reverting the cgroup v2 uid/pid hierarchy due to + // http://b/179006802. + // TODO: remove once the uid/pid hierarchy is restored + enableFreezerInternal(true); } else { Slog.e(TAG_AM, "unexpected value in cgroup.freeze"); } @@ -1149,7 +1159,7 @@ public final class CachedAppOptimizer { } return; } - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid + "): " + e); return; @@ -1244,7 +1254,7 @@ public final class CachedAppOptimizer { } } } - } catch (IOException e) { + } catch (Exception e) { Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e); synchronized (mAm) { synchronized (mProcLock) { diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 3acad49496c5..76c34678d589 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -16,17 +16,23 @@ package com.android.server.app; +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.app.ActivityManager; import android.app.GameManager; import android.app.GameManager.GameMode; import android.app.IGameManagerService; import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Process; import android.util.ArrayMap; +import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -81,6 +87,14 @@ public final class GameManagerService extends IGameManagerService.Stub { switch (msg.what) { case WRITE_SETTINGS: { final int userId = (int) msg.obj; + if (userId < 0) { + Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId); + synchronized (mLock) { + removeMessages(WRITE_SETTINGS, msg.obj); + } + break; + } + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mLock) { removeMessages(WRITE_SETTINGS, msg.obj); @@ -94,6 +108,15 @@ public final class GameManagerService extends IGameManagerService.Stub { } case REMOVE_SETTINGS: { final int userId = (int) msg.obj; + if (userId < 0) { + Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId); + synchronized (mLock) { + removeMessages(WRITE_SETTINGS, msg.obj); + removeMessages(REMOVE_SETTINGS, msg.obj); + } + break; + } + synchronized (mLock) { // Since the user was removed, ignore previous write message // and do write here. @@ -146,9 +169,23 @@ public final class GameManagerService extends IGameManagerService.Stub { } } - //TODO(b/178111358) Add proper permission check and multi-user handling + private boolean hasPermission(String permission) { + return mContext.checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED; + } + @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public @GameMode int getGameMode(String packageName, int userId) { + if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) { + Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE")); + return GameManager.GAME_MODE_UNSUPPORTED; + } + + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "getGameMode", + "com.android.server.app.GameManagerService"); + synchronized (mLock) { if (!mSettings.containsKey(userId)) { return GameManager.GAME_MODE_UNSUPPORTED; @@ -158,9 +195,18 @@ public final class GameManagerService extends IGameManagerService.Stub { } } - //TODO(b/178111358) Add proper permission check and multi-user handling @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) public void setGameMode(String packageName, @GameMode int gameMode, int userId) { + if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) { + Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE")); + return; + } + + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "setGameMode", + "com.android.server.app.GameManagerService"); + synchronized (mLock) { if (!mSettings.containsKey(userId)) { return; diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 14292d9c5f8d..c9560437799e 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -686,7 +686,8 @@ public final class AuthSession implements IBinder.DeathRecipient { * @return true if this AuthSession is finished, e.g. should be set to null */ boolean onCancelAuthSession(boolean force) { - final boolean authStarted = mState == STATE_AUTH_STARTED + final boolean authStarted = mState == STATE_AUTH_CALLED + || mState == STATE_AUTH_STARTED || mState == STATE_AUTH_STARTED_UI_SHOWING; if (authStarted && !force) { diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 0536e78e58f6..b31a54b8b15e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -311,4 +311,9 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public int getProtoEnum() { return BiometricsProto.CM_AUTHENTICATE; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 8fa3bbbf615a..81ce2d535237 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -82,12 +82,18 @@ public abstract class BaseClientMonitor extends LoggableMonitor @NonNull protected Callback mCallback; /** - * Returns a ClientMonitorEnum constant defined in biometrics.proto - * @return + * @return A ClientMonitorEnum constant defined in biometrics.proto */ public abstract int getProtoEnum(); /** + * @return True if the ClientMonitor should cancel any current and pending interruptable clients + */ + public boolean interruptsPrecedingClients() { + return false; + } + + /** * @param context system_server context * @param token a unique token for the client * @param listener recipient of related events (e.g. authentication) diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index c5237ab8c8e7..20c25c35535a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -475,14 +475,17 @@ public class BiometricScheduler { */ public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback clientCallback) { - // Mark any interruptable pending clients as canceling. Once they reach the head of the - // queue, the scheduler will send ERROR_CANCELED and skip the operation. - for (Operation operation : mPendingOperations) { - if (operation.mClientMonitor instanceof Interruptable - && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) { - Slog.d(getTag(), "New client incoming, marking pending client as canceling: " - + operation.mClientMonitor); - operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; + // If the incoming operation should interrupt preceding clients, mark any interruptable + // pending clients as canceling. Once they reach the head of the queue, the scheduler will + // send ERROR_CANCELED and skip the operation. + if (clientMonitor.interruptsPrecedingClients()) { + for (Operation operation : mPendingOperations) { + if (operation.mClientMonitor instanceof Interruptable + && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) { + Slog.d(getTag(), "New client incoming, marking pending client as canceling: " + + operation.mClientMonitor); + operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; + } } } @@ -490,8 +493,11 @@ public class BiometricScheduler { Slog.d(getTag(), "[Added] " + clientMonitor + ", new queue size: " + mPendingOperations.size()); - // If the current operation is cancellable, start the cancellation process. - if (mCurrentOperation != null && mCurrentOperation.mClientMonitor instanceof Interruptable + // If the new operation should interrupt preceding clients, and if the current operation is + // cancellable, start the cancellation process. + if (clientMonitor.interruptsPrecedingClients() + && mCurrentOperation != null + && mCurrentOperation.mClientMonitor instanceof Interruptable && mCurrentOperation.mState == Operation.STATE_STARTED) { Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation); cancelInternal(mCurrentOperation); diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java index da76af800d3d..25b7add0a7d8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java @@ -16,9 +16,12 @@ package com.android.server.biometrics.sensors; +import android.annotation.NonNull; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticationFrame; +import android.hardware.face.FaceEnrollFrame; import android.hardware.face.IFaceServiceReceiver; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.IFingerprintServiceReceiver; @@ -174,4 +177,33 @@ public class ClientMonitorCallbackConverter { mFingerprintServiceReceiver.onUdfpsPointerUp(sensorId); } } + + // Face-specific callbacks for FaceManager only + + /** + * Called each time a new frame is received during face authentication. + * + * @param frame Information about the current frame. + * + * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails. + */ + public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) + throws RemoteException { + if (mFaceServiceReceiver != null) { + mFaceServiceReceiver.onAuthenticationFrame(frame); + } + } + + /** + * Called each time a new frame is received during face enrollment. + * + * @param frame Information about the current frame. + * + * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails. + */ + public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) throws RemoteException { + if (mFaceServiceReceiver != null) { + mFaceServiceReceiver.onEnrollmentFrame(frame); + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index 8d81016dab59..e1320d8e1a4f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -113,4 +113,9 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> { public int getProtoEnum() { return BiometricsProto.CM_ENROLL; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index ce24e5efdc07..de571863dbd4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -69,6 +69,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide final List<BiometricAuthenticator.Identifier> unknownHALTemplates = ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates(); + Slog.d(TAG, "Enumerate onClientFinished: " + clientMonitor + ", success: " + success); + if (!unknownHALTemplates.isEmpty()) { Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion"); } @@ -90,6 +92,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide private final Callback mRemoveCallback = new Callback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { + Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success); mCallback.onClientFinished(InternalCleanupClient.this, success); } }; @@ -115,6 +118,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide } private void startCleanupUnknownHalTemplates() { + Slog.d(TAG, "startCleanupUnknownHalTemplates, size: " + mUnknownHALTemplates.size()); + UserTemplate template = mUnknownHALTemplates.get(0); mUnknownHALTemplates.remove(template); mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(), @@ -138,6 +143,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide // Start enumeration. Removal will start if necessary, when enumeration is completed. mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(), getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId()); + + Slog.d(TAG, "Starting enumerate: " + mCurrentTask); mCurrentTask.start(mEnumerateCallback); } @@ -165,6 +172,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide + mCurrentTask.getClass().getSimpleName()); return; } + Slog.d(TAG, "onEnumerated, remaining: " + remaining); ((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining); } diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 9d19fdf4868d..e3feb74248ec 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -82,6 +82,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T> private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) { if (identifier == null) { + Slog.d(TAG, "Null identifier"); return; } Slog.v(TAG, "handleEnumeratedTemplate: " + identifier.getBiometricId()); @@ -103,6 +104,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T> private void doTemplateCleanup() { if (mEnrolledList == null) { + Slog.d(TAG, "Null enrolledList"); return; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java index d2673d2969c9..897ebd719da4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java @@ -24,6 +24,8 @@ import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.face.AuthenticationFrame; import android.hardware.biometrics.face.BaseFrame; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticationFrame; +import android.hardware.face.FaceEnrollFrame; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; import android.util.Slog; @@ -117,6 +119,16 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { public void onChallengeInterruptFinished(int sensorId) { } + + @Override + public void onAuthenticationFrame(FaceAuthenticationFrame frame) { + + } + + @Override + public void onEnrollmentFrame(FaceEnrollFrame frame) { + + } }; BiometricTestSessionImpl(@NonNull Context context, int sensorId, @@ -183,7 +195,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed(); } - // TODO(b/174619156): replace with notifyAuthenticationFrame and notifyEnrollmentFrame. + // TODO(b/178414967): replace with notifyAuthenticationFrame and notifyEnrollmentFrame. @Override public void notifyAcquired(int userId, int acquireInfo) { Utils.checkPermission(mContext, TEST_BIOMETRIC); @@ -194,7 +206,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { AuthenticationFrame authenticationFrame = new AuthenticationFrame(); authenticationFrame.data = data; - // TODO(b/174619156): Currently onAuthenticationFrame and onEnrollmentFrame are the same. + // TODO(b/178414967): Currently onAuthenticationFrame and onEnrollmentFrame are the same. // This will need to call the correct callback once the onAcquired callback is removed. mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFrame( authenticationFrame); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 30577667e5e4..8f554028ebfd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -29,7 +29,6 @@ import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.face.FaceAuthenticationFrame; -import android.hardware.face.FaceDataFrame; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; @@ -179,19 +178,16 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements return isBiometricPrompt() ? mBiometricPromptIgnoreListVendor : mKeyguardIgnoreListVendor; } - private boolean shouldSend(int acquireInfo, int vendorCode) { - if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) { - return !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode); - } else { - return !Utils.listContains(getAcquireIgnorelist(), acquireInfo); - } + private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) { + return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR + ? !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode) + : !Utils.listContains(getAcquireIgnorelist(), acquireInfo); } @Override public void onAcquired(int acquireInfo, int vendorCode) { mLastAcquire = acquireInfo; - - final boolean shouldSend = shouldSend(acquireInfo, vendorCode); + final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode); onAcquiredInternal(acquireInfo, vendorCode, shouldSend); } @@ -201,9 +197,21 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements * @param frame Information about the current frame. */ public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) { - // TODO(b/178414967): Send additional frame data to the client callback. - final FaceDataFrame data = frame.getData(); - onAcquired(data.getAcquiredInfo(), data.getVendorCode()); + // Log acquisition but don't send it to the client yet, since that's handled below. + final int acquireInfo = frame.getData().getAcquiredInfo(); + final int vendorCode = frame.getData().getVendorCode(); + mLastAcquire = acquireInfo; + onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */); + + final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode); + if (shouldSend && getListener() != null) { + try { + getListener().onAuthenticationFrame(frame); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to send authentication frame", e); + mCallback.onClientFinished(this, false /* success */); + } + } } @Override public void onLockoutTimed(long durationMillis) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index da657b96afd5..898d81b0c8c4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -27,7 +27,6 @@ import android.hardware.biometrics.face.Feature; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; -import android.hardware.face.FaceDataFrame; import android.hardware.face.FaceEnrollFrame; import android.hardware.face.FaceManager; import android.os.IBinder; @@ -101,14 +100,15 @@ public class FaceEnrollClient extends EnrollClient<ISession> { getTargetUserId()).size() >= mMaxTemplatesPerUser; } + private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) { + return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR + ? !Utils.listContains(mEnrollIgnoreListVendor, vendorCode) + : !Utils.listContains(mEnrollIgnoreList, acquireInfo); + } + @Override public void onAcquired(int acquireInfo, int vendorCode) { - final boolean shouldSend; - if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) { - shouldSend = !Utils.listContains(mEnrollIgnoreListVendor, vendorCode); - } else { - shouldSend = !Utils.listContains(mEnrollIgnoreList, acquireInfo); - } + final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode); onAcquiredInternal(acquireInfo, vendorCode, shouldSend); } @@ -118,9 +118,20 @@ public class FaceEnrollClient extends EnrollClient<ISession> { * @param frame Information about the current frame. */ public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) { - // TODO(b/178414967): Send additional frame data to the client callback. - final FaceDataFrame data = frame.getData(); - onAcquired(data.getAcquiredInfo(), data.getVendorCode()); + // Log acquisition but don't send it to the client yet, since that's handled below. + final int acquireInfo = frame.getData().getAcquiredInfo(); + final int vendorCode = frame.getData().getVendorCode(); + onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */); + + final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode); + if (shouldSend && getListener() != null) { + try { + getListener().onEnrollmentFrame(frame); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to send enrollment frame", e); + mCallback.onClientFinished(this, false /* success */); + } + } } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java index 4142a52c9253..d519d60881c0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java @@ -22,6 +22,8 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.ITestSession; import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticationFrame; +import android.hardware.face.FaceEnrollFrame; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; import android.util.Slog; @@ -106,6 +108,16 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { public void onChallengeInterruptFinished(int sensorId) { } + + @Override + public void onAuthenticationFrame(FaceAuthenticationFrame frame) { + + } + + @Override + public void onEnrollmentFrame(FaceEnrollFrame frame) { + + } }; BiometricTestSessionImpl(@NonNull Context context, int sensorId, @NonNull Face10 face10, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 9611192fa13e..bcd1b8bc9976 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -98,4 +98,9 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> { public int getProtoEnum() { return BiometricsProto.CM_DETECT_INTERACTION; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 7989dca3e06f..8acb284667c7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -130,4 +130,9 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> public int getProtoEnum() { return BiometricsProto.CM_DETECT_INTERACTION; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index bff1a5c99dd2..c05e25367d03 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -717,8 +717,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mNumBackgroundNetworkRequests += delta; break; - case TRACK_DEFAULT: case LISTEN: + case TRACK_DEFAULT: + case TRACK_SYSTEM_DEFAULT: break; case NONE: diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index fc2c7e01efde..33c19b1ca2b2 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -74,6 +74,7 @@ import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.VpnManager; import android.net.VpnService; +import android.net.VpnTransportInfo; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionParams; @@ -435,6 +436,7 @@ public class Vpn { mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); + mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE)); loadAlwaysOnPackage(keyStore); } @@ -929,6 +931,7 @@ public class Vpn { jniReset(mInterface); mInterface = null; mNetworkCapabilities.setUids(null); + mNetworkCapabilities.setTransportInfo(null); } // Revoke the connection or stop the VpnRunner. @@ -999,6 +1002,8 @@ public class Vpn { case VpnManager.TYPE_VPN_SERVICE: toChange = new String[] {AppOpsManager.OPSTR_ACTIVATE_VPN}; break; + case VpnManager.TYPE_VPN_LEGACY: + return false; default: Log.wtf(TAG, "Unrecognized VPN type while granting authorization"); return false; @@ -1029,6 +1034,8 @@ public class Vpn { return isVpnServicePreConsented(context, packageName); case VpnManager.TYPE_VPN_PLATFORM: return isVpnProfilePreConsented(context, packageName); + case VpnManager.TYPE_VPN_LEGACY: + return VpnConfig.LEGACY_VPN.equals(packageName); default: return false; } @@ -1211,6 +1218,8 @@ public class Vpn { mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId, mConfig.allowedApplications, mConfig.disallowedApplications)); + mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType())); + // Only apps targeting Q and above can explicitly declare themselves as metered. // These VPNs are assumed metered unless they state otherwise. if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) { @@ -1736,6 +1745,7 @@ public class Vpn { private void cleanupVpnStateLocked() { mStatusIntent = null; mNetworkCapabilities.setUids(null); + mNetworkCapabilities.setTransportInfo(null); mConfig = null; mInterface = null; @@ -1846,22 +1856,18 @@ public class Vpn { } /** - * Gets the currently running App-based VPN type + * Gets the currently running VPN type * - * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running an - * app-based VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always + * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running a + * VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always * Settings-based, the Platform VPNs can be initiated by both apps and Settings. */ - public synchronized int getActiveAppVpnType() { - if (VpnConfig.LEGACY_VPN.equals(mPackage)) { - return VpnManager.TYPE_VPN_NONE; - } - - if (mVpnRunner != null && mVpnRunner instanceof IkeV2VpnRunner) { - return VpnManager.TYPE_VPN_PLATFORM; - } else { - return VpnManager.TYPE_VPN_SERVICE; - } + public synchronized int getActiveVpnType() { + if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE; + if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE; + return mVpnRunner instanceof IkeV2VpnRunner + ? VpnManager.TYPE_VPN_PLATFORM + : VpnManager.TYPE_VPN_LEGACY; } private void updateAlwaysOnNotification(DetailedState networkState) { diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java index 1a0a639c3c66..7b646b36124b 100644 --- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java +++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; */ import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback; import android.hardware.hdmi.IHdmiControlCallback; @@ -65,6 +66,20 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { @Override boolean start() { + HdmiControlService service = localDevice().mService; + if (service.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) { + HdmiDeviceInfo deviceInfo = service.getHdmiCecNetwork().getCecDeviceInfo( + mTargetAddress); + if (deviceInfo != null + && deviceInfo.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) { + int powerStatus = deviceInfo.getDevicePowerStatus(); + if (powerStatus != HdmiControlManager.POWER_STATUS_UNKNOWN) { + invokeCallback(powerStatus); + finish(); + return true; + } + } + } queryDevicePowerStatus(); mState = STATE_WAITING_FOR_REPORT_POWER_STATUS; addTimer(mState, HdmiConfig.TIMEOUT_MS); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 75b52f95d5bb..299525207a60 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -239,6 +239,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected void onActiveSourceLost() { assertRunOnServiceThread(); + mService.pauseActiveMediaSessions(); switch (mService.getHdmiCecConfig().getStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)) { case HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW: diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index bdf92ca4a7ef..fa1fb48ce124 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -56,6 +56,8 @@ import android.hardware.hdmi.IHdmiVendorCommandListener; import android.hardware.tv.cec.V1_0.OptionKey; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.media.AudioManager; +import android.media.session.MediaController; +import android.media.session.MediaSessionManager; import android.media.tv.TvInputManager; import android.media.tv.TvInputManager.TvInputCallback; import android.net.Uri; @@ -3294,6 +3296,16 @@ public class HdmiControlService extends SystemService { } } + @VisibleForTesting + void pauseActiveMediaSessions() { + MediaSessionManager mediaSessionManager = getContext() + .getSystemService(MediaSessionManager.class); + List<MediaController> mediaControllers = mediaSessionManager.getActiveSessions(null); + for (MediaController mediaController : mediaControllers) { + mediaController.getTransportControls().pause(); + } + } + void setActiveSource(int logicalAddress, int physicalAddress, String caller) { synchronized (mLock) { mActiveSource.logicalAddress = logicalAddress; diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index ea759bf500dd..28dc5167487d 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -79,6 +79,7 @@ import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import android.stats.location.LocationStatsEnums; +import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Log; @@ -87,6 +88,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.location.eventlog.LocationEventLog; import com.android.server.location.geofence.GeofenceManager; import com.android.server.location.geofence.GeofenceProxy; import com.android.server.location.gnss.GnssConfiguration; @@ -98,7 +100,6 @@ import com.android.server.location.injector.AppOpsHelper; import com.android.server.location.injector.EmergencyHelper; import com.android.server.location.injector.Injector; import com.android.server.location.injector.LocationAttributionHelper; -import com.android.server.location.injector.LocationEventLog; import com.android.server.location.injector.LocationPermissionsHelper; import com.android.server.location.injector.LocationPowerSaveModeHelper; import com.android.server.location.injector.LocationUsageLogger; @@ -147,9 +148,10 @@ public class LocationManagerService extends ILocationManager.Stub { public Lifecycle(Context context) { super(context); + LocationEventLog eventLog = new LocationEventLog(); mUserInfoHelper = new LifecycleUserInfoHelper(context); - mSystemInjector = new SystemInjector(context, mUserInfoHelper); - mService = new LocationManagerService(context, mSystemInjector); + mSystemInjector = new SystemInjector(context, mUserInfoHelper, eventLog); + mService = new LocationManagerService(context, mSystemInjector, eventLog); } @Override @@ -159,7 +161,7 @@ public class LocationManagerService extends ILocationManager.Stub { // client caching behavior is only enabled after seeing the first invalidate LocationManager.invalidateLocalLocationEnabledCaches(); // disable caching for our own process - Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class)) + Objects.requireNonNull(getContext().getSystemService(LocationManager.class)) .disableLocalLocationEnabledCaches(); } @@ -221,6 +223,7 @@ public class LocationManagerService extends ILocationManager.Stub { private final Context mContext; private final Injector mInjector; + private final LocationEventLog mEventLog; private final LocalService mLocalService; private final GeofenceManager mGeofenceManager; @@ -245,10 +248,10 @@ public class LocationManagerService extends ILocationManager.Stub { private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = new CopyOnWriteArrayList<>(); - LocationManagerService(Context context, Injector injector) { + LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); mInjector = injector; - + mEventLog = eventLog; mLocalService = new LocalService(); LocalServices.addService(LocationManagerInternal.class, mLocalService); @@ -256,7 +259,7 @@ public class LocationManagerService extends ILocationManager.Stub { // set up passive provider first since it will be required for all other location providers, // which are loaded later once the system is ready. - mPassiveManager = new PassiveLocationProviderManager(mContext, injector); + mPassiveManager = new PassiveLocationProviderManager(mContext, injector, mEventLog); addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext)); // TODO: load the gps provider here as well, which will require refactoring @@ -297,7 +300,7 @@ public class LocationManagerService extends ILocationManager.Stub { } LocationProviderManager manager = new LocationProviderManager(mContext, mInjector, - providerName, mPassiveManager); + mEventLog, providerName, mPassiveManager); addLocationProviderManager(manager, null); return manager; } @@ -341,7 +344,7 @@ public class LocationManagerService extends ILocationManager.Stub { com.android.internal.R.string.config_networkLocationProviderPackageName); if (networkProvider != null) { LocationProviderManager networkManager = new LocationProviderManager(mContext, - mInjector, NETWORK_PROVIDER, mPassiveManager); + mInjector, mEventLog, NETWORK_PROVIDER, mPassiveManager); addLocationProviderManager(networkManager, networkProvider); } else { Log.w(TAG, "no network location provider found"); @@ -360,7 +363,7 @@ public class LocationManagerService extends ILocationManager.Stub { com.android.internal.R.string.config_fusedLocationProviderPackageName); if (fusedProvider != null) { LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector, - FUSED_PROVIDER, mPassiveManager); + mEventLog, FUSED_PROVIDER, mPassiveManager); addLocationProviderManager(fusedManager, fusedProvider); } else { Log.wtf(TAG, "no fused location provider found"); @@ -375,7 +378,7 @@ public class LocationManagerService extends ILocationManager.Stub { mGnssManagerService.onSystemReady(); LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector, - GPS_PROVIDER, mPassiveManager); + mEventLog, GPS_PROVIDER, mPassiveManager); addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider()); } @@ -431,7 +434,7 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "[u" + userId + "] location enabled = " + enabled); } - mInjector.getLocationEventLog().logLocationEnabled(userId, enabled); + mEventLog.logLocationEnabled(userId, enabled); Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION) .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled) @@ -1193,9 +1196,27 @@ public class LocationManagerService extends ILocationManager.Stub { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) { - mGnssManagerService.dump(fd, ipw, args); - return; + if (args.length > 0) { + LocationProviderManager manager = getLocationProviderManager(args[0]); + if (manager != null) { + ipw.println("Provider:"); + ipw.increaseIndent(); + manager.dump(fd, ipw, args); + ipw.decreaseIndent(); + + ipw.println("Event Log:"); + ipw.increaseIndent(); + mEventLog.iterate(manager.getName(), ipw::println); + ipw.decreaseIndent(); + return; + } + + if ("--gnssmetrics".equals(args[0])) { + if (mGnssManagerService != null) { + mGnssManagerService.dump(fd, ipw, args); + } + return; + } } ipw.println("Location Manager State:"); @@ -1227,6 +1248,25 @@ public class LocationManagerService extends ILocationManager.Stub { } ipw.decreaseIndent(); + ipw.println("Historical Aggregate Location Provider Data:"); + ipw.increaseIndent(); + ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats = + mEventLog.copyAggregateStats(); + for (int i = 0; i < aggregateStats.size(); i++) { + ipw.println(aggregateStats.keyAt(i)); + ipw.increaseIndent(); + ArrayMap<String, LocationEventLog.AggregateStats> providerStats = + aggregateStats.valueAt(i); + for (int j = 0; j < providerStats.size(); j++) { + ipw.print(providerStats.keyAt(j)); + ipw.print(": "); + providerStats.valueAt(j).updateTotals(); + ipw.println(providerStats.valueAt(j)); + } + ipw.decreaseIndent(); + } + ipw.decreaseIndent(); + if (mGnssManagerService != null) { ipw.println("GNSS Manager:"); ipw.increaseIndent(); @@ -1241,7 +1281,7 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("Event Log:"); ipw.increaseIndent(); - mInjector.getLocationEventLog().iterate(ipw::println); + mEventLog.iterate(ipw::println); ipw.decreaseIndent(); } @@ -1320,7 +1360,6 @@ public class LocationManagerService extends ILocationManager.Stub { private final Context mContext; private final UserInfoHelper mUserInfoHelper; - private final LocationEventLog mLocationEventLog; private final AlarmHelper mAlarmHelper; private final SystemAppOpsHelper mAppOpsHelper; private final SystemLocationPermissionsHelper mLocationPermissionsHelper; @@ -1339,19 +1378,17 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("this") private boolean mSystemReady; - SystemInjector(Context context, UserInfoHelper userInfoHelper) { + SystemInjector(Context context, UserInfoHelper userInfoHelper, LocationEventLog eventLog) { mContext = context; mUserInfoHelper = userInfoHelper; - mLocationEventLog = new LocationEventLog(); mAlarmHelper = new SystemAlarmHelper(context); mAppOpsHelper = new SystemAppOpsHelper(context); mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context, mAppOpsHelper); mSettingsHelper = new SystemSettingsHelper(context); mAppForegroundHelper = new SystemAppForegroundHelper(context); - mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, - mLocationEventLog); + mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, eventLog); mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context); mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper); mLocationUsageLogger = new LocationUsageLogger(); @@ -1430,11 +1467,6 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public LocationEventLog getLocationEventLog() { - return mLocationEventLog; - } - - @Override public LocationUsageLogger getLocationUsageLogger() { return mLocationUsageLogger; } diff --git a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java index b5746bbf310a..12dd3e6a4a4c 100644 --- a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java +++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java @@ -16,12 +16,13 @@ package com.android.server.location.eventlog; +import android.annotation.Nullable; import android.os.SystemClock; import android.util.TimeUtils; import com.android.internal.util.Preconditions; -import java.util.ListIterator; +import java.util.Iterator; import java.util.NoSuchElementException; import java.util.function.Consumer; @@ -35,6 +36,7 @@ public abstract class LocalEventLog { boolean isFiller(); long getTimeDeltaMs(); String getLogString(); + boolean filter(@Nullable String filter); } private static final class FillerEvent implements Log { @@ -62,6 +64,11 @@ public abstract class LocalEventLog { public String getLogString() { throw new AssertionError(); } + + @Override + public boolean filter(String filter) { + return false; + } } /** @@ -87,6 +94,11 @@ public abstract class LocalEventLog { public final long getTimeDeltaMs() { return Integer.toUnsignedLong(mTimeDelta); } + + @Override + public boolean filter(String filter) { + return false; + } } // circular buffer of log entries @@ -198,6 +210,17 @@ public abstract class LocalEventLog { } } + /** + * Iterates over the event log, passing each filter-matching log string to the given + * consumer. + */ + public synchronized void iterate(String filter, Consumer<String> consumer) { + LogIterator it = new LogIterator(filter); + while (it.hasNext()) { + consumer.accept(it.next()); + } + } + // returns the index of the first element private int startIndex() { return wrapIndex(mLogEndIndex - mLogSize); @@ -205,12 +228,13 @@ public abstract class LocalEventLog { // returns the index after this one private int incrementIndex(int index) { - return wrapIndex(index + 1); - } - - // returns the index before this one - private int decrementIndex(int index) { - return wrapIndex(index - 1); + if (index == -1) { + return startIndex(); + } else if (index >= 0) { + return wrapIndex(index + 1); + } else { + throw new IllegalArgumentException(); + } } // rolls over the given index if necessary @@ -219,7 +243,9 @@ public abstract class LocalEventLog { return (index % mLog.length + mLog.length) % mLog.length; } - private class LogIterator implements ListIterator<String> { + private class LogIterator implements Iterator<String> { + + private final @Nullable String mFilter; private final long mSystemTimeDeltaMs; @@ -228,10 +254,17 @@ public abstract class LocalEventLog { private int mCount; LogIterator() { + this(null); + } + + LogIterator(@Nullable String filter) { + mFilter = filter; mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime(); mCurrentRealtimeMs = mStartRealtimeMs; - mIndex = startIndex(); - mCount = 0; + mIndex = -1; + mCount = -1; + + increment(); } @Override @@ -239,75 +272,17 @@ public abstract class LocalEventLog { return mCount < mLogSize; } - @Override - public boolean hasPrevious() { - return mCount > 0; - } - - @Override - // return then increment public String next() { if (!hasNext()) { throw new NoSuchElementException(); } Log log = mLog[mIndex]; - long nextDeltaMs = log.getTimeDeltaMs(); - long realtimeMs = mCurrentRealtimeMs + nextDeltaMs; - - // calculate next index, skipping filler events - do { - mCurrentRealtimeMs += nextDeltaMs; - mIndex = incrementIndex(mIndex); - if (++mCount < mLogSize) { - nextDeltaMs = mLog[mIndex].getTimeDeltaMs(); - } - } while (mCount < mLogSize && mLog[mIndex].isFiller()); - - return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString(); - } - - @Override - // decrement then return - public String previous() { - Log log; - long currentDeltaMs; - long realtimeMs; - - // calculate previous index, skipping filler events with MAX_TIME_DELTA - do { - if (!hasPrevious()) { - throw new NoSuchElementException(); - } - - mIndex = decrementIndex(mIndex); - mCount--; - - log = mLog[mIndex]; - realtimeMs = mCurrentRealtimeMs; - - if (mCount > 0) { - currentDeltaMs = log.getTimeDeltaMs(); - mCurrentRealtimeMs -= currentDeltaMs; - } - } while (mCount >= 0 && log.isFiller()); - - return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString(); - } - - @Override - public int nextIndex() { - throw new UnsupportedOperationException(); - } + long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs; - @Override - public int previousIndex() { - throw new UnsupportedOperationException(); - } + increment(); - @Override - public void add(String s) { - throw new UnsupportedOperationException(); + return getTimePrefix(timeMs) + log.getLogString(); } @Override @@ -315,9 +290,16 @@ public abstract class LocalEventLog { throw new UnsupportedOperationException(); } - @Override - public void set(String s) { - throw new UnsupportedOperationException(); + private void increment() { + long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs(); + do { + mCurrentRealtimeMs += nextDeltaMs; + mIndex = incrementIndex(mIndex); + if (++mCount < mLogSize) { + nextDeltaMs = mLog[mIndex].getTimeDeltaMs(); + } + } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null + && !mLog[mIndex].filter(mFilter)))); } } } diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java index 8d73518bced1..67060fc2c082 100644 --- a/services/core/java/com/android/server/location/injector/LocationEventLog.java +++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open 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,24 +14,32 @@ * limitations under the License. */ -package com.android.server.location.injector; +package com.android.server.location.eventlog; import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF; import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY; import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF; import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE; import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; +import static android.util.TimeUtils.formatDuration; import static com.android.server.location.LocationManagerService.D; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + import android.annotation.Nullable; import android.location.LocationRequest; import android.location.provider.ProviderRequest; import android.location.util.identity.CallerIdentity; import android.os.Build; import android.os.PowerManager.LocationPowerSaveMode; +import android.os.SystemClock; +import android.util.ArrayMap; -import com.android.server.location.eventlog.LocalEventLog; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; /** In memory event log for location events. */ public class LocationEventLog extends LocalEventLog { @@ -54,8 +62,39 @@ public class LocationEventLog extends LocalEventLog { private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8; private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9; + @GuardedBy("mAggregateStats") + private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats; + public LocationEventLog() { super(getLogSize()); + mAggregateStats = new ArrayMap<>(4); + } + + public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() { + synchronized (mAggregateStats) { + ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>( + mAggregateStats); + for (int i = 0; i < copy.size(); i++) { + copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i))); + } + return copy; + } + } + + private AggregateStats getAggregateStats(String provider, String packageName) { + synchronized (mAggregateStats) { + ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider); + if (packageMap == null) { + packageMap = new ArrayMap<>(2); + mAggregateStats.put(provider, packageMap); + } + AggregateStats stats = packageMap.get(packageName); + if (stats == null) { + stats = new AggregateStats(); + packageMap.put(packageName, stats); + } + return stats; + } } /** Logs a location enabled/disabled event. */ @@ -77,12 +116,34 @@ public class LocationEventLog extends LocalEventLog { public void logProviderClientRegistered(String provider, CallerIdentity identity, LocationRequest request) { addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request); + getAggregateStats(provider, identity.getPackageName()) + .markRequestAdded(request.getIntervalMillis()); } /** Logs a client unregistration for a location provider. */ - public void logProviderClientUnregistered(String provider, - CallerIdentity identity) { + public void logProviderClientUnregistered(String provider, CallerIdentity identity) { addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity); + getAggregateStats(provider, identity.getPackageName()).markRequestRemoved(); + } + + /** Logs a client for a location provider entering the active state. */ + public void logProviderClientActive(String provider, CallerIdentity identity) { + getAggregateStats(provider, identity.getPackageName()).markRequestActive(); + } + + /** Logs a client for a location provider leaving the active state. */ + public void logProviderClientInactive(String provider, CallerIdentity identity) { + getAggregateStats(provider, identity.getPackageName()).markRequestInactive(); + } + + /** Logs a client for a location provider entering the foreground state. */ + public void logProviderClientForeground(String provider, CallerIdentity identity) { + getAggregateStats(provider, identity.getPackageName()).markRequestForeground(); + } + + /** Logs a client for a location provider leaving the foreground state. */ + public void logProviderClientBackground(String provider, CallerIdentity identity) { + getAggregateStats(provider, identity.getPackageName()).markRequestBackground(); } /** Logs a change to the provider request for a location provider. */ @@ -143,16 +204,29 @@ public class LocationEventLog extends LocalEventLog { } } - private static class ProviderEnabledEvent extends LogEvent { + private abstract static class ProviderEvent extends LogEvent { + + protected final String mProvider; + + protected ProviderEvent(long timeDelta, String provider) { + super(timeDelta); + mProvider = provider; + } + + @Override + public boolean filter(String filter) { + return mProvider.equals(filter); + } + } + + private static final class ProviderEnabledEvent extends ProviderEvent { - private final String mProvider; private final int mUserId; private final boolean mEnabled; protected ProviderEnabledEvent(long timeDelta, String provider, int userId, boolean enabled) { - super(timeDelta); - mProvider = provider; + super(timeDelta, provider); mUserId = userId; mEnabled = enabled; } @@ -164,14 +238,12 @@ public class LocationEventLog extends LocalEventLog { } } - private static class ProviderMockedEvent extends LogEvent { + private static final class ProviderMockedEvent extends ProviderEvent { - private final String mProvider; private final boolean mMocked; protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) { - super(timeDelta); - mProvider = provider; + super(timeDelta, provider); mMocked = mocked; } @@ -185,17 +257,15 @@ public class LocationEventLog extends LocalEventLog { } } - private static class ProviderRegisterEvent extends LogEvent { + private static final class ProviderRegisterEvent extends ProviderEvent { - private final String mProvider; private final boolean mRegistered; private final CallerIdentity mIdentity; @Nullable private final LocationRequest mLocationRequest; private ProviderRegisterEvent(long timeDelta, String provider, boolean registered, CallerIdentity identity, @Nullable LocationRequest locationRequest) { - super(timeDelta); - mProvider = provider; + super(timeDelta, provider); mRegistered = registered; mIdentity = identity; mLocationRequest = locationRequest; @@ -212,14 +282,12 @@ public class LocationEventLog extends LocalEventLog { } } - private static class ProviderUpdateEvent extends LogEvent { + private static final class ProviderUpdateEvent extends ProviderEvent { - private final String mProvider; private final ProviderRequest mRequest; private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) { - super(timeDelta); - mProvider = provider; + super(timeDelta, provider); mRequest = request; } @@ -229,14 +297,12 @@ public class LocationEventLog extends LocalEventLog { } } - private static class ProviderReceiveLocationEvent extends LogEvent { + private static final class ProviderReceiveLocationEvent extends ProviderEvent { - private final String mProvider; private final int mNumLocations; private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) { - super(timeDelta); - mProvider = provider; + super(timeDelta, provider); mNumLocations = numLocations; } @@ -246,16 +312,14 @@ public class LocationEventLog extends LocalEventLog { } } - private static class ProviderDeliverLocationEvent extends LogEvent { + private static final class ProviderDeliverLocationEvent extends ProviderEvent { - private final String mProvider; private final int mNumLocations; @Nullable private final CallerIdentity mIdentity; private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations, @Nullable CallerIdentity identity) { - super(timeDelta); - mProvider = provider; + super(timeDelta, provider); mNumLocations = numLocations; mIdentity = identity; } @@ -267,7 +331,7 @@ public class LocationEventLog extends LocalEventLog { } } - private static class LocationPowerSaveModeEvent extends LogEvent { + private static final class LocationPowerSaveModeEvent extends LogEvent { @LocationPowerSaveMode private final int mLocationPowerSaveMode; @@ -305,7 +369,7 @@ public class LocationEventLog extends LocalEventLog { } } - private static class LocationEnabledEvent extends LogEvent { + private static final class LocationEnabledEvent extends LogEvent { private final int mUserId; private final boolean mEnabled; @@ -321,4 +385,118 @@ public class LocationEventLog extends LocalEventLog { return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled"); } } + + /** + * Aggregate statistics for a single package under a single provider. + */ + public static final class AggregateStats { + + @GuardedBy("this") + private int mAddedRequestCount; + @GuardedBy("this") + private int mActiveRequestCount; + @GuardedBy("this") + private int mForegroundRequestCount; + + @GuardedBy("this") + private long mFastestIntervalMs = Long.MAX_VALUE; + @GuardedBy("this") + private long mSlowestIntervalMs = 0; + + @GuardedBy("this") + private long mAddedTimeTotalMs; + @GuardedBy("this") + private long mAddedTimeLastUpdateRealtimeMs; + + @GuardedBy("this") + private long mActiveTimeTotalMs; + @GuardedBy("this") + private long mActiveTimeLastUpdateRealtimeMs; + + @GuardedBy("this") + private long mForegroundTimeTotalMs; + @GuardedBy("this") + private long mForegroundTimeLastUpdateRealtimeMs; + + AggregateStats() {} + + synchronized void markRequestAdded(long intervalMillis) { + if (mAddedRequestCount++ == 0) { + mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime(); + } + + mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs); + mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs); + } + + synchronized void markRequestRemoved() { + updateTotals(); + --mAddedRequestCount; + Preconditions.checkState(mAddedRequestCount >= 0); + + mActiveRequestCount = min(mAddedRequestCount, mActiveRequestCount); + mForegroundRequestCount = min(mAddedRequestCount, mForegroundRequestCount); + } + + synchronized void markRequestActive() { + Preconditions.checkState(mAddedRequestCount > 0); + if (mActiveRequestCount++ == 0) { + mActiveTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime(); + } + } + + synchronized void markRequestInactive() { + updateTotals(); + --mActiveRequestCount; + Preconditions.checkState(mActiveRequestCount >= 0); + } + + synchronized void markRequestForeground() { + Preconditions.checkState(mAddedRequestCount > 0); + if (mForegroundRequestCount++ == 0) { + mForegroundTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime(); + } + } + + synchronized void markRequestBackground() { + updateTotals(); + --mForegroundRequestCount; + Preconditions.checkState(mForegroundRequestCount >= 0); + } + + public synchronized void updateTotals() { + if (mAddedRequestCount > 0) { + long realtimeMs = SystemClock.elapsedRealtime(); + mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs; + mAddedTimeLastUpdateRealtimeMs = realtimeMs; + } + if (mActiveRequestCount > 0) { + long realtimeMs = SystemClock.elapsedRealtime(); + mActiveTimeTotalMs += realtimeMs - mActiveTimeLastUpdateRealtimeMs; + mActiveTimeLastUpdateRealtimeMs = realtimeMs; + } + if (mForegroundRequestCount > 0) { + long realtimeMs = SystemClock.elapsedRealtime(); + mForegroundTimeTotalMs += realtimeMs - mForegroundTimeLastUpdateRealtimeMs; + mForegroundTimeLastUpdateRealtimeMs = realtimeMs; + } + } + + @Override + public synchronized String toString() { + return "min/max interval = " + intervalToString(mFastestIntervalMs) + "/" + + intervalToString(mSlowestIntervalMs) + + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs) + + "/" + formatDuration(mActiveTimeTotalMs) + "/" + + formatDuration(mForegroundTimeTotalMs); + } + + private static String intervalToString(long intervalMs) { + if (intervalMs == LocationRequest.PASSIVE_INTERVAL) { + return "passive"; + } else { + return MILLISECONDS.toSeconds(intervalMs) + "s"; + } + } + } } diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java index 03938b2b8ba2..0e157c22a32b 100644 --- a/services/core/java/com/android/server/location/injector/Injector.java +++ b/services/core/java/com/android/server/location/injector/Injector.java @@ -56,7 +56,4 @@ public interface Injector { /** Returns a LocationUsageLogger. */ LocationUsageLogger getLocationUsageLogger(); - - /** Returns a LocationEventLog. */ - LocationEventLog getLocationEventLog(); } diff --git a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java index 532826a02ab0..cc00d5684991 100644 --- a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java +++ b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java @@ -24,6 +24,8 @@ import static com.android.server.location.LocationManagerService.TAG; import android.os.PowerManager.LocationPowerSaveMode; import android.util.Log; +import com.android.server.location.eventlog.LocationEventLog; + import java.util.concurrent.CopyOnWriteArrayList; /** diff --git a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java index 1b74865b268e..c47a64d6d9a7 100644 --- a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java @@ -25,6 +25,7 @@ import android.os.PowerSaveState; import com.android.internal.util.Preconditions; import com.android.server.FgThread; import com.android.server.LocalServices; +import com.android.server.location.eventlog.LocationEventLog; import java.util.Objects; import java.util.function.Consumer; diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 48a012e57a02..388b5a4dd54e 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -89,6 +89,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.location.LocationPermissions; import com.android.server.location.LocationPermissions.PermissionLevel; +import com.android.server.location.eventlog.LocationEventLog; import com.android.server.location.fudger.LocationFudger; import com.android.server.location.injector.AlarmHelper; import com.android.server.location.injector.AppForegroundHelper; @@ -96,7 +97,6 @@ import com.android.server.location.injector.AppForegroundHelper.AppForegroundLis import com.android.server.location.injector.AppOpsHelper; import com.android.server.location.injector.Injector; import com.android.server.location.injector.LocationAttributionHelper; -import com.android.server.location.injector.LocationEventLog; import com.android.server.location.injector.LocationPermissionsHelper; import com.android.server.location.injector.LocationPermissionsHelper.LocationPermissionsListener; import com.android.server.location.injector.LocationPowerSaveModeHelper; @@ -323,7 +323,7 @@ public class LocationProviderManager extends + getRequest()); } - mLocationEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest()); + mEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest()); // initialization order is important as there are ordering dependencies mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel, @@ -333,6 +333,10 @@ public class LocationProviderManager extends mIsUsingHighPower = isUsingHighPower(); onProviderListenerRegister(); + + if (mForeground) { + mEventLog.logProviderClientForeground(mName, getIdentity()); + } } @GuardedBy("mLock") @@ -344,7 +348,7 @@ public class LocationProviderManager extends onProviderListenerUnregister(); - mLocationEventLog.logProviderClientUnregistered(mName, getIdentity()); + mEventLog.logProviderClientUnregistered(mName, getIdentity()); if (D) { Log.d(TAG, mName + " provider removed registration from " + getIdentity()); @@ -369,6 +373,8 @@ public class LocationProviderManager extends Preconditions.checkState(Thread.holdsLock(mLock)); } + mEventLog.logProviderClientActive(mName, getIdentity()); + if (!getRequest().isHiddenFromAppOps()) { mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey()); } @@ -389,6 +395,8 @@ public class LocationProviderManager extends } onProviderListenerInactive(); + + mEventLog.logProviderClientInactive(mName, getIdentity()); } /** @@ -524,6 +532,12 @@ public class LocationProviderManager extends mForeground = foreground; + if (mForeground) { + mEventLog.logProviderClientForeground(mName, getIdentity()); + } else { + mEventLog.logProviderClientBackground(mName, getIdentity()); + } + // note that onProviderLocationRequestChanged() is always called return onProviderLocationRequestChanged() || mLocationPowerSaveModeHelper.getLocationPowerSaveMode() @@ -855,7 +869,7 @@ public class LocationProviderManager extends listener.deliverOnLocationChanged(deliverLocationResult, mUseWakeLock ? mWakeLock::release : null); - mLocationEventLog.logProviderDeliveredLocations(mName, locationResult.size(), + mEventLog.logProviderDeliveredLocations(mName, locationResult.size(), getIdentity()); } @@ -1154,7 +1168,7 @@ public class LocationProviderManager extends // we currently don't hold a wakelock for getCurrentLocation deliveries listener.deliverOnLocationChanged(deliverLocationResult, null); - mLocationEventLog.logProviderDeliveredLocations(mName, + mEventLog.logProviderDeliveredLocations(mName, locationResult != null ? locationResult.size() : 0, getIdentity()); } @@ -1223,6 +1237,7 @@ public class LocationProviderManager extends private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners; + protected final LocationEventLog mEventLog; protected final LocationManagerInternal mLocationManagerInternal; protected final SettingsHelper mSettingsHelper; protected final UserInfoHelper mUserHelper; @@ -1235,7 +1250,6 @@ public class LocationProviderManager extends protected final LocationAttributionHelper mLocationAttributionHelper; protected final LocationUsageLogger mLocationUsageLogger; protected final LocationFudger mLocationFudger; - protected final LocationEventLog mLocationEventLog; private final UserListener mUserChangedListener = this::onUserChanged; private final UserSettingChangedListener mLocationEnabledChangedListener = @@ -1273,8 +1287,8 @@ public class LocationProviderManager extends @GuardedBy("mLock") private @Nullable OnAlarmListener mDelayedRegister; - public LocationProviderManager(Context context, Injector injector, String name, - @Nullable PassiveLocationProviderManager passiveManager) { + public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog, + String name, @Nullable PassiveLocationProviderManager passiveManager) { mContext = context; mName = Objects.requireNonNull(name); mPassiveManager = passiveManager; @@ -1285,6 +1299,7 @@ public class LocationProviderManager extends mEnabledListeners = new ArrayList<>(); mProviderRequestListeners = new CopyOnWriteArrayList<>(); + mEventLog = eventLog; mLocationManagerInternal = Objects.requireNonNull( LocalServices.getService(LocationManagerInternal.class)); mSettingsHelper = injector.getSettingsHelper(); @@ -1297,7 +1312,6 @@ public class LocationProviderManager extends mScreenInteractiveHelper = injector.getScreenInteractiveHelper(); mLocationAttributionHelper = injector.getLocationAttributionHelper(); mLocationUsageLogger = injector.getLocationUsageLogger(); - mLocationEventLog = injector.getLocationEventLog(); mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM()); mProvider = new MockableLocationProvider(mLock); @@ -1437,7 +1451,7 @@ public class LocationProviderManager extends synchronized (mLock) { Preconditions.checkState(mState != STATE_STOPPED); - mLocationEventLog.logProviderMocked(mName, provider != null); + mEventLog.logProviderMocked(mName, provider != null); final long identity = Binder.clearCallingIdentity(); try { @@ -1925,7 +1939,7 @@ public class LocationProviderManager extends @GuardedBy("mLock") private void setProviderRequest(ProviderRequest request) { - mLocationEventLog.logProviderUpdateRequest(mName, request); + mEventLog.logProviderUpdateRequest(mName, request); mProvider.getController().setRequest(request); FgThread.getHandler().post(() -> { @@ -2261,7 +2275,7 @@ public class LocationProviderManager extends } // don't log location received for passive provider because it's spammy - mLocationEventLog.logProviderReceivedLocations(mName, filtered.size()); + mEventLog.logProviderReceivedLocations(mName, filtered.size()); } else { // passive provider should get already filtered results as input filtered = locationResult; @@ -2361,7 +2375,7 @@ public class LocationProviderManager extends if (D) { Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled); } - mLocationEventLog.logProviderEnabled(mName, userId, enabled); + mEventLog.logProviderEnabled(mName, userId, enabled); } // clear last locations if we become disabled diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java index b35af4f6475c..027f4e94f55b 100644 --- a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java @@ -24,6 +24,7 @@ import android.location.provider.ProviderRequest; import android.os.Binder; import com.android.internal.util.Preconditions; +import com.android.server.location.eventlog.LocationEventLog; import com.android.server.location.injector.Injector; import java.util.Collection; @@ -33,8 +34,9 @@ import java.util.Collection; */ public class PassiveLocationProviderManager extends LocationProviderManager { - public PassiveLocationProviderManager(Context context, Injector injector) { - super(context, injector, LocationManager.PASSIVE_PROVIDER, null); + public PassiveLocationProviderManager(Context context, Injector injector, + LocationEventLog eventLog) { + super(context, injector, eventLog, LocationManager.PASSIVE_PROVIDER, null); } @Override diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f7f1865f757e..4c3dfbff3f87 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -378,7 +378,9 @@ public class NotificationManagerService extends SystemService { static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] { Adjustment.KEY_CONTEXTUAL_ACTIONS, Adjustment.KEY_TEXT_REPLIES, - Adjustment.KEY_NOT_CONVERSATION}; + Adjustment.KEY_NOT_CONVERSATION, + Adjustment.KEY_IMPORTANCE, + Adjustment.KEY_RANKING_SCORE}; static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] { RoleManager.ROLE_DIALER, @@ -9048,7 +9050,8 @@ public class NotificationManagerService extends SystemService { public class NotificationAssistants extends ManagedServices { static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants"; - private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments"; + private static final String TAG_ALLOWED_ADJUSTMENT_TYPES_OLD = "q_allowed_adjustments"; + private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "s_allowed_adjustments"; private static final String ATT_TYPES = "types"; private final Object mLock = new Object(); @@ -9150,13 +9153,19 @@ public class NotificationManagerService extends SystemService { @Override protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException { - if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) { + if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag) + || TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) { final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); synchronized (mLock) { mAllowedAdjustments.clear(); if (!TextUtils.isEmpty(types)) { mAllowedAdjustments.addAll(Arrays.asList(types.split(","))); } + if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)) { + if (DEBUG) Slog.d(TAG, "Migrate allowed adjustments."); + mAllowedAdjustments.addAll( + Arrays.asList(DEFAULT_ALLOWED_ADJUSTMENTS)); + } } } } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 50fb176e3a5a..fd2fb1fcab39 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -50,6 +50,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.res.ApkAssets; import android.net.Uri; import android.os.Binder; @@ -1376,18 +1377,18 @@ public final class OverlayManagerService extends SystemService { targetPackageNames = pm.getTargetPackageNames(userId); } - final Map<String, List<String>> pendingChanges = + final Map<String, OverlayPaths> pendingChanges = new ArrayMap<>(targetPackageNames.size()); synchronized (mLock) { - final List<String> frameworkOverlays = - mImpl.getEnabledOverlayPackageNames("android", userId); + final OverlayPaths frameworkOverlays = + mImpl.getEnabledOverlayPaths("android", userId); for (final String targetPackageName : targetPackageNames) { - List<String> list = new ArrayList<>(); + final OverlayPaths.Builder list = new OverlayPaths.Builder(); if (!"android".equals(targetPackageName)) { list.addAll(frameworkOverlays); } - list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); - pendingChanges.put(targetPackageName, list); + list.addAll(mImpl.getEnabledOverlayPaths(targetPackageName, userId)); + pendingChanges.put(targetPackageName, list.build()); } } @@ -1395,7 +1396,7 @@ public final class OverlayManagerService extends SystemService { for (final String targetPackageName : targetPackageNames) { if (DEBUG) { Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" - + TextUtils.join(",", pendingChanges.get(targetPackageName)) + + pendingChanges.get(targetPackageName) + "] userId=" + userId); } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index e60411bb78c5..c547c36a8033 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -31,6 +31,7 @@ import android.annotation.Nullable; import android.content.om.OverlayInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.content.pm.overlay.OverlayPaths; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -697,19 +698,20 @@ final class OverlayManagerServiceImpl { removeIdmapIfPossible(oi); } - List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName, + OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName, final int userId) { final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId); - final List<String> paths = new ArrayList<>(overlays.size()); + final OverlayPaths.Builder paths = new OverlayPaths.Builder(); final int n = overlays.size(); for (int i = 0; i < n; i++) { final OverlayInfo oi = overlays.get(i); - if (oi.isEnabled()) { - paths.add(oi.packageName); + if (!oi.isEnabled()) { + continue; } + paths.addApkPath(oi.baseCodePath); } - return paths; + return paths.build(); } /** diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 15e1d5281bfa..7bf704299373 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -59,6 +59,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.utils.Snappable; import com.android.server.utils.Watchable; import com.android.server.utils.WatchableImpl; +import com.android.server.utils.Watched; import com.android.server.utils.WatchedSparseArray; import com.android.server.utils.WatchedSparseBooleanArray; import com.android.server.utils.Watcher; @@ -123,6 +124,7 @@ class InstantAppRegistry implements Watchable, Snappable { private final CookiePersistence mCookiePersistence; /** State for uninstalled instant apps */ + @Watched @GuardedBy("mService.mLock") private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps; @@ -132,10 +134,12 @@ class InstantAppRegistry implements Watchable, Snappable { * The value is a set of instant app UIDs. * UserID -> TargetAppId -> InstantAppId */ + @Watched @GuardedBy("mService.mLock") private final WatchedSparseArray<WatchedSparseArray<WatchedSparseBooleanArray>> mInstantGrants; /** The set of all installed instant apps. UserID -> AppID */ + @Watched @GuardedBy("mService.mLock") private final WatchedSparseArray<WatchedSparseBooleanArray> mInstalledInstantAppUids; @@ -189,6 +193,7 @@ class InstantAppRegistry implements Watchable, Snappable { mUninstalledInstantApps.registerObserver(mObserver); mInstantGrants.registerObserver(mObserver); mInstalledInstantAppUids.registerObserver(mObserver); + Watchable.verifyWatchedAttributes(this, mObserver); } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 965f68bdcaf3..0e6f5577bcd8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -193,6 +193,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; import android.content.pm.ModuleInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.PackageChangeEvent; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; @@ -234,6 +235,7 @@ import android.content.pm.VersionedPackage; import android.content.pm.dex.ArtManager; import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.IArtManager; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.ParsingPackageUtils; @@ -1747,6 +1749,11 @@ public class PackageManagerService extends IPackageManager.Stub public AndroidPackage getPackage(@NonNull String packageName) { return getPackageLocked(packageName); } + + @Override + public boolean filterAppAccess(String packageName, int callingUid, int userId) { + return mPmInternal.filterAppAccess(packageName, callingUid, userId); + } } /** @@ -5938,6 +5945,21 @@ public class PackageManagerService extends IPackageManager.Stub } } + // Link watchables to the class + private void registerObserver() { + mPackages.registerObserver(mWatcher); + mSharedLibraries.registerObserver(mWatcher); + mStaticLibsByDeclaringPackage.registerObserver(mWatcher); + mInstrumentation.registerObserver(mWatcher); + mWebInstantAppsDisabled.registerObserver(mWatcher); + mAppsFilter.registerObserver(mWatcher); + mInstantAppRegistry.registerObserver(mWatcher); + mSettings.registerObserver(mWatcher); + // If neither "build" attribute is true then this may be a mockito test, and verification + // can fail as a false positive. + Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild)); + } + /** * A extremely minimal constructor designed to start up a PackageManagerService instance for * testing. @@ -6021,15 +6043,7 @@ public class PackageManagerService extends IPackageManager.Stub sSnapshotCorked = true; mLiveComputer = createLiveComputer(); mSnapshotComputer = mLiveComputer; - - // Link up the watchers - mPackages.registerObserver(mWatcher); - mSharedLibraries.registerObserver(mWatcher); - mStaticLibsByDeclaringPackage.registerObserver(mWatcher); - mInstrumentation.registerObserver(mWatcher); - mWebInstantAppsDisabled.registerObserver(mWatcher); - mAppsFilter.registerObserver(mWatcher); - Watchable.verifyWatchedAttributes(this, mWatcher); + registerObserver(); mPackages.putAll(testParams.packages); mEnableFreeCacheV2 = testParams.enableFreeCacheV2; @@ -6183,15 +6197,6 @@ public class PackageManagerService extends IPackageManager.Stub mDomainVerificationManager = injector.getDomainVerificationManagerInternal(); mDomainVerificationManager.setConnection(mDomainVerificationConnection); - // Link up the watchers - mPackages.registerObserver(mWatcher); - mSharedLibraries.registerObserver(mWatcher); - mStaticLibsByDeclaringPackage.registerObserver(mWatcher); - mInstrumentation.registerObserver(mWatcher); - mWebInstantAppsDisabled.registerObserver(mWatcher); - mAppsFilter.registerObserver(mWatcher); - Watchable.verifyWatchedAttributes(this, mWatcher); - // Create the computer as soon as the state objects have been installed. The // cached computer is the same as the live computer until the end of the // constructor, at which time the invalidation method updates it. The cache is @@ -6200,6 +6205,7 @@ public class PackageManagerService extends IPackageManager.Stub sSnapshotCorked = true; mLiveComputer = createLiveComputer(); mSnapshotComputer = mLiveComputer; + registerObserver(); // CHECKSTYLE:OFF IndentationCheck synchronized (mInstallLock) { @@ -16156,8 +16162,7 @@ public class PackageManagerService extends IPackageManager.Stub @Deprecated @Override public boolean updateIntentVerificationStatus(String packageName, int status, int userId) { - mDomainVerificationManager.setLegacyUserState(packageName, userId, status); - return true; + return mDomainVerificationManager.setLegacyUserState(packageName, userId, status); } @Deprecated @@ -18119,11 +18124,8 @@ public class PackageManagerService extends IPackageManager.Stub if (libPs == null) { continue; } - final String[] overlayPaths = libPs.getOverlayPaths(currentUserId); - if (overlayPaths != null) { - ps.setOverlayPathsForLibrary(sharedLib.getName(), - Arrays.asList(overlayPaths), currentUserId); - } + ps.setOverlayPathsForLibrary(sharedLib.getName(), + libPs.getOverlayPaths(currentUserId), currentUserId); } } } @@ -25947,6 +25949,13 @@ public class PackageManagerService extends IPackageManager.Stub public String getModuleMetadataPackageName() throws RemoteException { return PackageManagerService.this.mModuleInfoProvider.getPackageName(); } + + @Override + public boolean hasSha256SigningCertificate(String packageName, byte[] certificate) + throws RemoteException { + return PackageManagerService.this.hasSigningCertificate( + packageName, certificate, CERT_INPUT_SHA256); + } } private AndroidPackage getPackage(String packageName) { @@ -26614,34 +26623,19 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName, - @Nullable List<String> overlayPackageNames, - @NonNull Collection<String> outUpdatedPackageNames) { + @Nullable OverlayPaths overlayPaths, + @NonNull Set<String> outUpdatedPackageNames) { + boolean modified = false; synchronized (mLock) { final AndroidPackage targetPkg = mPackages.get(targetPackageName); if (targetPackageName == null || targetPkg == null) { Slog.e(TAG, "failed to find package " + targetPackageName); return false; } - ArrayList<String> overlayPaths = null; - if (overlayPackageNames != null && overlayPackageNames.size() > 0) { - final int N = overlayPackageNames.size(); - overlayPaths = new ArrayList<>(N); - for (int i = 0; i < N; i++) { - final String packageName = overlayPackageNames.get(i); - final AndroidPackage pkg = mPackages.get(packageName); - if (pkg == null) { - Slog.e(TAG, "failed to find package " + packageName); - return false; - } - overlayPaths.add(pkg.getBaseApkPath()); - } - } - ArraySet<String> updatedPackageNames = null; if (targetPkg.getLibraryNames() != null) { // Set the overlay paths for dependencies of the shared library. - updatedPackageNames = new ArraySet<>(); - for (String libName : targetPkg.getLibraryNames()) { + for (final String libName : targetPkg.getLibraryNames()) { final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName, SharedLibraryInfo.VERSION_UNDEFINED); if (info == null) { @@ -26652,28 +26646,30 @@ public class PackageManagerService extends IPackageManager.Stub if (dependents == null) { continue; } - for (VersionedPackage dependent : dependents) { + for (final VersionedPackage dependent : dependents) { final PackageSetting ps = mSettings.getPackageLPr( dependent.getPackageName()); if (ps == null) { continue; } - ps.setOverlayPathsForLibrary(libName, overlayPaths, userId); - updatedPackageNames.add(dependent.getPackageName()); + if (ps.setOverlayPathsForLibrary(libName, overlayPaths, userId)) { + outUpdatedPackageNames.add(dependent.getPackageName()); + modified = true; + } } } } final PackageSetting ps = mSettings.getPackageLPr(targetPackageName); - ps.setOverlayPaths(overlayPaths, userId); - - outUpdatedPackageNames.add(targetPackageName); - if (updatedPackageNames != null) { - outUpdatedPackageNames.addAll(updatedPackageNames); + if (ps.setOverlayPaths(overlayPaths, userId)) { + outUpdatedPackageNames.add(targetPackageName); + modified = true; } } - invalidatePackageInfoCache(); + if (modified) { + invalidatePackageInfoCache(); + } return true; } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 3a142837e063..5364cbfede86 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -31,6 +31,7 @@ import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; +import android.content.pm.overlay.OverlayPaths; import android.os.PersistableBundle; import android.os.incremental.IncrementalManager; import android.service.pm.PackageProto; @@ -44,7 +45,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -327,21 +327,20 @@ public abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).uninstallReason = uninstallReason; } - void setOverlayPaths(List<String> overlayPaths, int userId) { - modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null : - overlayPaths.toArray(new String[overlayPaths.size()])); + boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) { + return modifyUserState(userId).setOverlayPaths(overlayPaths); } - String[] getOverlayPaths(int userId) { + OverlayPaths getOverlayPaths(int userId) { return readUserState(userId).getOverlayPaths(); } - void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) { - modifyUserState(userId).setSharedLibraryOverlayPaths(libName, - overlayPaths == null ? null : overlayPaths.toArray(new String[0])); + boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths, + int userId) { + return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths); } - Map<String, String[]> getOverlayPathsForLibrary(int userId) { + Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) { return readUserState(userId).getSharedLibraryOverlayPaths(); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index fb033e6594b8..a8a6bcec2313 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -40,6 +40,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageUserState; @@ -49,6 +50,7 @@ import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.content.pm.parsing.component.ParsedComponent; import android.content.pm.parsing.component.ParsedIntentInfo; @@ -105,9 +107,6 @@ import com.android.permission.persistence.RuntimePermissionsState; import com.android.server.LocalServices; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.verify.domain.DomainVerificationLegacySettings; -import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; -import com.android.server.pm.verify.domain.DomainVerificationPersistence; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; @@ -115,6 +114,9 @@ import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.permission.LegacyPermissionState.PermissionState; +import com.android.server.pm.verify.domain.DomainVerificationLegacySettings; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationPersistence; import com.android.server.utils.Snappable; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.utils.Watchable; @@ -487,7 +489,7 @@ public final class Settings implements Watchable, Snappable { // App-link priority tracking, per-user @NonNull @Watched - final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray(); + private final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray(); final StringBuilder mReadMessages = new StringBuilder(); @@ -552,6 +554,7 @@ public final class Settings implements Watchable, Snappable { mAppIds.registerObserver(mObserver); mOtherAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); Watchable.verifyWatchedAttributes(this, mObserver); @@ -602,6 +605,7 @@ public final class Settings implements Watchable, Snappable { mAppIds.registerObserver(mObserver); mOtherAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); Watchable.verifyWatchedAttributes(this, mObserver); @@ -649,6 +653,7 @@ public final class Settings implements Watchable, Snappable { mPastSignatures.addAll(r.mPastSignatures); mKeySetRefs.putAll(r.mKeySetRefs); mRenamedPackages.snapshot(r.mRenamedPackages); + mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp); // mReadMessages mPendingPackages.addAll(r.mPendingPackages); @@ -2707,7 +2712,6 @@ public final class Settings implements Watchable, Snappable { writeSigningKeySetLPr(serializer, pkg.keySetData); writeUpgradeKeySetsLPr(serializer, pkg.keySetData); writeKeySetAliasesLPr(serializer, pkg.keySetData); - mDomainVerificationManager.writeLegacySettings(serializer, pkg.name); writeMimeGroupLPr(serializer, pkg.mimeGroups); serializer.endTag(null, "package"); @@ -4686,26 +4690,58 @@ public final class Settings implements Watchable, Snappable { } } - String[] overlayPaths = ps.getOverlayPaths(user.id); - if (overlayPaths != null && overlayPaths.length > 0) { - pw.print(prefix); pw.println(" overlay paths:"); - for (String path : overlayPaths) { - pw.print(prefix); pw.print(" "); pw.println(path); + final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id); + if (overlayPaths != null) { + if (!overlayPaths.getOverlayPaths().isEmpty()) { + pw.print(prefix); + pw.println(" overlay paths:"); + for (String path : overlayPaths.getOverlayPaths()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + if (!overlayPaths.getResourceDirs().isEmpty()) { + pw.print(prefix); + pw.println(" legacy overlay paths:"); + for (String path : overlayPaths.getResourceDirs()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } } } - Map<String, String[]> sharedLibraryOverlayPaths = + final Map<String, OverlayPaths> sharedLibraryOverlayPaths = ps.getOverlayPathsForLibrary(user.id); if (sharedLibraryOverlayPaths != null) { - for (Map.Entry<String, String[]> libOverlayPaths : + for (Map.Entry<String, OverlayPaths> libOverlayPaths : sharedLibraryOverlayPaths.entrySet()) { - if (libOverlayPaths.getValue() == null) { + final OverlayPaths paths = libOverlayPaths.getValue(); + if (paths == null) { continue; } - pw.print(prefix); pw.print(" "); - pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:"); - for (String path : libOverlayPaths.getValue()) { - pw.print(prefix); pw.print(" "); pw.println(path); + if (!paths.getOverlayPaths().isEmpty()) { + pw.print(prefix); + pw.println(" "); + pw.print(libOverlayPaths.getKey()); + pw.println(" overlay paths:"); + for (String path : paths.getOverlayPaths()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + if (!paths.getResourceDirs().isEmpty()) { + pw.print(prefix); + pw.println(" "); + pw.print(libOverlayPaths.getKey()); + pw.println(" legacy overlay paths:"); + for (String path : paths.getResourceDirs()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } } } } diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java new file mode 100644 index 000000000000..6cdd4df824a8 --- /dev/null +++ b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.pm.parsing.library; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.parsing.pkg.ParsedPackage; + +/** + * Updates a package to remove dependency on android.net.ipsec.ike library. + * + * @hide + */ +@VisibleForTesting +public class AndroidNetIpSecIkeUpdater extends PackageSharedLibraryUpdater { + + private static final String LIBRARY_NAME = "android.net.ipsec.ike"; + + @Override + public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) { + removeLibrary(parsedPackage, LIBRARY_NAME); + } +} diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java index 1405a7d613f1..8a8a302734b1 100644 --- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java +++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java @@ -45,6 +45,9 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { static { final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>(); + // Remove android.net.ipsec.ike library, it is added to boot classpath since Android S. + packageUpdaters.add(new AndroidNetIpSecIkeUpdater()); + // Remove com.google.android.maps library. packageUpdaters.add(new ComGoogleAndroidMapsUpdater()); diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java index c521f828ade9..275dd053fdde 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java @@ -18,8 +18,10 @@ package com.android.server.pm.verify.domain; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Binder; import android.os.Process; @@ -30,10 +32,17 @@ public class DomainVerificationEnforcer { @NonNull private final Context mContext; + @NonNull + private Callback mCallback; + public DomainVerificationEnforcer(@NonNull Context context) { mContext = context; } + public void setCallback(@NonNull Callback callback) { + mCallback = callback; + } + /** * Enforced when mutating any state from shell or internally in the system process. */ @@ -67,6 +76,11 @@ public class DomainVerificationEnforcer { "Caller " + callingUid + " is not allowed to query domain verification state"); } + + mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES, + Binder.getCallingPid(), callingUid, + "Caller " + callingUid + " does not hold " + + android.Manifest.permission.QUERY_ALL_PACKAGES); break; } } @@ -84,28 +98,42 @@ public class DomainVerificationEnforcer { isAllowed = true; break; default: - // TODO(b/159952358): Remove permission check? The component package should - // have been checked when the verifier component was first scanned in PMS. - mContext.enforcePermission( - android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, - Binder.getCallingPid(), callingUid, - "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT"); + final int callingPid = Binder.getCallingPid(); + boolean isLegacyVerificationAgent = false; + if (mContext.checkPermission( + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, callingPid, + callingUid) != PackageManager.PERMISSION_GRANTED) { + isLegacyVerificationAgent = mContext.checkPermission( + android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, + callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; + if (!isLegacyVerificationAgent) { + throw new SecurityException("Caller " + callingUid + " does not hold " + + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT); + } + } + + // If the caller isn't a legacy verifier, it needs the QUERY_ALL permission + if (!isLegacyVerificationAgent) { + mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES, + callingPid, callingUid, "Caller " + callingUid + " does not hold " + + android.Manifest.permission.QUERY_ALL_PACKAGES); + } + isAllowed = proxy.isCallerVerifier(callingUid); break; } if (!isAllowed) { throw new SecurityException("Caller " + callingUid - + " is not the approved domain verification agent, isVerifier = " - + proxy.isCallerVerifier(callingUid)); + + " is not the approved domain verification agent"); } } /** * Enforced when mutating user selection state inside an exposed API method. */ - public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId, - @UserIdInt int targetUserId) throws SecurityException { + public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId, + @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException { if (callingUserId != targetUserId) { mContext.enforcePermission( Manifest.permission.INTERACT_ACROSS_USERS, @@ -117,12 +145,51 @@ public class DomainVerificationEnforcer { android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION, Binder.getCallingPid(), callingUid, "Caller is not allowed to edit user selections"); + + if (packageName == null) { + return true; + } + + return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); } - public void callerIsLegacyUserSelector(int callingUid) { + public boolean callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId, + @NonNull String packageName, @UserIdInt int targetUserId) { mContext.enforcePermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, Binder.getCallingPid(), callingUid, "Caller is not allowed to edit user state"); + + if (callingUserId != targetUserId) { + if (mContext.checkPermission( + Manifest.permission.INTERACT_ACROSS_USERS, + Binder.getCallingPid(), callingUid) != PackageManager.PERMISSION_GRANTED) { + // Legacy API did not enforce this, so for backwards compatibility, fail silently + return false; + } + } + + return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); + } + + public boolean callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId, + @NonNull String packageName, @UserIdInt int targetUserId) { + if (callingUserId != targetUserId) { + // The legacy API enforces the _FULL variant, so maintain that here + mContext.enforcePermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Binder.getCallingPid(), callingUid, + "Caller is not allowed to edit other users"); + } + + return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); + } + + public interface Callback { + /** + * @return true if access to the given package should be filtered and the method failed as + * if the package was not installed + */ + boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId); } } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java index 0474d78a3e53..50fd6e3ddea1 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java @@ -174,8 +174,10 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan * Set aside a legacy user selection that will be restored to a pending * {@link DomainVerificationPkgState} once it's added through * {@link #addPackage(PackageSetting)}. + * + * @return true if state changed successfully */ - void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state); + boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state); /** * Until the legacy APIs are entirely removed, returns the legacy state from the previously @@ -184,12 +186,6 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan int getLegacyState(@NonNull String packageName, @UserIdInt int userId); /** - * Serialize a legacy setting that wasn't attached yet. - * TODO: Does this even matter? Should consider for removal. - */ - void writeLegacySettings(TypedXmlSerializer serializer, String name); - - /** * Print the verification state and user selection state of a package. * * @param packageName the package whose state to change, or all packages if none is @@ -235,7 +231,8 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan throws IllegalArgumentException, NameNotFoundException; - interface Connection extends Function<String, PackageSetting> { + interface Connection extends DomainVerificationEnforcer.Callback, + Function<String, PackageSetting> { /** * Notify that a settings change has been made and that eventually diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java index e24e5bbfa4f6..fa0327414914 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java @@ -47,12 +47,12 @@ import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.compat.PlatformCompat; import com.android.server.pm.PackageSetting; +import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; import com.android.server.pm.verify.domain.models.DomainVerificationUserState; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable; -import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; @@ -92,9 +92,9 @@ public class DomainVerificationService extends SystemService * immediately attached once its available. * <p> * Generally this should be not accessed directly. Prefer calling {@link - * #getAndValidateAttachedLocked(UUID, Set, boolean)}. + * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)}. * - * @see #getAndValidateAttachedLocked(UUID, Set, boolean) + * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer) **/ @GuardedBy("mLock") @NonNull @@ -160,6 +160,7 @@ public class DomainVerificationService extends SystemService @Override public void setConnection(@NonNull Connection connection) { mConnection = connection; + mEnforcer.setCallback(mConnection); } @NonNull @@ -285,7 +286,7 @@ public class DomainVerificationService extends SystemService mEnforcer.assertApprovedVerifier(callingUid, mProxy); synchronized (mLock) { DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, - true /* forAutoVerify */); + true /* forAutoVerify */, callingUid, null /* userId */); ArrayMap<String, Integer> stateMap = pkgState.getStateMap(); for (String domain : domains) { Integer previousState = stateMap.get(domain); @@ -389,8 +390,10 @@ public class DomainVerificationService extends SystemService public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed, @UserIdInt int userId) throws NameNotFoundException { - mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), - mConnection.getCallingUserId(), userId); + if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), + mConnection.getCallingUserId(), packageName, userId)) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } synchronized (mLock) { DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); if (pkgState == null) { @@ -455,11 +458,18 @@ public class DomainVerificationService extends SystemService public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId) throws InvalidDomainSetException, NameNotFoundException { - mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), - mConnection.getCallingUserId(), userId); synchronized (mLock) { + final int callingUid = mConnection.getCallingUid(); + // Pass null for package name here and do the app visibility enforcement inside + // getAndValidateAttachedLocked instead, since this has to fail with the same invalid + // ID reason if the target app is invisible + if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(), + null /* packageName */, userId)) { + throw new InvalidDomainSetException(domainSetId, null, + InvalidDomainSetException.REASON_ID_INVALID); + } DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, - false /* forAutoVerify */); + false /* forAutoVerify */, callingUid, userId); DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId); if (enabled) { userState.addHosts(domains); @@ -556,8 +566,10 @@ public class DomainVerificationService extends SystemService @Override public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { - mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), - mConnection.getCallingUserId(), userId); + if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), + mConnection.getCallingUserId(), packageName, userId)) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } synchronized (mLock) { DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName); if (pkgState == null) { @@ -844,23 +856,27 @@ public class DomainVerificationService extends SystemService } @Override - public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) { - mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid()); + public boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, + int state) { + if (!mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid(), + mConnection.getCallingUserId(), packageName, userId)) { + return false; + } mLegacySettings.add(packageName, userId, state); mConnection.scheduleWriteSettings(); + return true; } @Override public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) { + if (!mEnforcer.callerIsLegacyUserQuerent(mConnection.getCallingUid(), + mConnection.getCallingUserId(), packageName, userId)) { + return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + } return mLegacySettings.getUserState(packageName, userId); } @Override - public void writeLegacySettings(TypedXmlSerializer serializer, String name) { - - } - - @Override public void clearPackage(@NonNull String packageName) { synchronized (mLock) { mAttachedPkgStates.remove(packageName); @@ -935,10 +951,14 @@ public class DomainVerificationService extends SystemService * Validates parameters provided by an external caller. Checks that an ID is still live and that * any provided domains are valid. Should be called at the beginning of each API that takes in a * {@link UUID} domain set ID. + * + * @param userIdForFilter which user to filter app access to, or null if the caller has already + * validated package visibility */ @GuardedBy("mLock") private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId, - @NonNull Set<String> domains, boolean forAutoVerify) + @NonNull Set<String> domains, boolean forAutoVerify, int callingUid, + @Nullable Integer userIdForFilter) throws InvalidDomainSetException, NameNotFoundException { if (domainSetId == null) { throw new InvalidDomainSetException(null, null, @@ -952,6 +972,13 @@ public class DomainVerificationService extends SystemService } String pkgName = pkgState.getPackageName(); + + if (userIdForFilter != null + && mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) { + throw new InvalidDomainSetException(domainSetId, null, + InvalidDomainSetException.REASON_ID_INVALID); + } + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName); if (pkgSetting == null || pkgSetting.getPkg() == null) { throw DomainVerificationUtils.throwPackageUnavailable(pkgName); diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java index 57cf986842da..e57d4ce9ec31 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java @@ -66,7 +66,7 @@ public class RotationResolverManagerService extends private static final String KEY_SERVICE_ENABLED = "service_enabled"; /** Default value in absence of {@link DeviceConfig} override. */ - private static final boolean DEFAULT_SERVICE_ENABLED = false; + private static final boolean DEFAULT_SERVICE_ENABLED = true; static final int ORIENTATION_UNKNOWN = FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNKNOWN; diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 5e681c674d8b..539b4138cc18 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -90,6 +90,7 @@ import android.net.NetworkTemplate; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.BatteryStats; +import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -152,6 +153,7 @@ import com.android.internal.os.LooperStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.StoragedUidIoStatsReader; +import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.internal.util.CollectionUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.role.RoleManagerLocal; @@ -457,6 +459,8 @@ public class StatsPullAtomService extends SystemService { synchronized (mCpuTimePerUidFreqLock) { return pullCpuTimePerUidFreqLocked(atomTag, data); } + case FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER: + return pullCpuCyclesPerThreadGroupCluster(atomTag, data); case FrameworkStatsLog.CPU_ACTIVE_TIME: synchronized (mCpuActiveTimeLock) { return pullCpuActiveTimeLocked(atomTag, data); @@ -781,6 +785,7 @@ public class StatsPullAtomService extends SystemService { registerCpuTimePerUid(); registerCpuCyclesPerUidCluster(); registerCpuTimePerUidFreq(); + registerCpuCyclesPerThreadGroupCluster(); registerCpuActiveTime(); registerCpuClusterTime(); registerWifiActivityInfo(); @@ -1510,6 +1515,7 @@ public class StatsPullAtomService extends SystemService { } int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) { + // TODO(b/179485697): Remove power profile dependency. PowerProfile powerProfile = new PowerProfile(mContext); // Frequency index to frequency mapping. long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); @@ -1653,6 +1659,81 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerCpuCyclesPerThreadGroupCluster() { + // TODO(b/173227907): Register only when supported. + int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3, 4}) + .build(); + mStatsManager.setPullAtomCallback( + tagId, + metadata, + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + + int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) { + // TODO(b/179485697): Remove power profile dependency. + PowerProfile powerProfile = new PowerProfile(mContext); + // Frequency index to frequency mapping. + long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); + if (freqs == null) { + return StatsManager.PULL_SKIP; + } + // Frequency index to cluster mapping. + int[] freqClusters = new int[freqs.length]; + // Number of clusters. + int clusters; + + // Initialize frequency mappings. + { + int cluster = 0; + long lastFreq = -1; + for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex) { + long currFreq = freqs[freqIndex]; + if (currFreq <= lastFreq) { + cluster++; + } + freqClusters[freqIndex] = cluster; + lastFreq = currFreq; + } + + clusters = cluster + 1; + } + + SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class) + .getSystemServiceCpuThreadTimes(); + if (times == null) { + return StatsManager.PULL_SKIP; + } + + addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, + FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER, + times.threadCpuTimesUs, clusters, freqs, freqClusters); + addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, + FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER, + times.binderThreadCpuTimesUs, clusters, freqs, freqClusters); + + return StatsManager.PULL_SUCCESS; + } + + private static void addCpuCyclesPerThreadGroupClusterAtoms( + int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs, + int clusters, long[] freqs, int[] freqClusters) { + long[] aggregatedCycles = new long[clusters]; + long[] aggregatedTimesUs = new long[clusters]; + for (int i = 0; i < cpuTimesUs.length; ++i) { + aggregatedCycles[freqClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000; + aggregatedTimesUs[freqClusters[i]] += cpuTimesUs[i]; + } + for (int cluster = 0; cluster < clusters; ++cluster) { + pulledData.add(FrameworkStatsLog.buildStatsEvent( + atomTag, threadGroup, cluster, aggregatedCycles[cluster], + aggregatedTimesUs[cluster] / 1_000)); + } + } + private void registerCpuActiveTime() { // the throttling is 3sec, handled in // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java index a54288f73018..e463ee22452d 100644 --- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java @@ -33,9 +33,11 @@ import java.util.Objects; */ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment { - private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5); + // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented + private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1); + // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = - Duration.ofMinutes(1); + Duration.ofSeconds(20); private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5); @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal; diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java index f936693bd621..44a74593cda2 100644 --- a/services/core/java/com/android/server/utils/Watchable.java +++ b/services/core/java/com/android/server/utils/Watchable.java @@ -19,8 +19,8 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Build; +import android.util.Log; -import java.lang.annotation.Annotation; import java.lang.reflect.Field; /** @@ -61,40 +61,54 @@ public interface Watchable { public void dispatchChange(@Nullable Watchable what); /** - * Return true if the field is tagged with @Watched - */ - private static boolean isWatched(Field f) { - for (Annotation a : f.getDeclaredAnnotations()) { - if (a.annotationType().equals(Watched.class)) { - return true; - } - } - return false; - } - - /** * Verify that all @Watched {@link Watchable} attributes are being watched by this * class. This requires reflection and only runs in engineering or user debug * builds. + * @param base The object that contains watched attributes. + * @param observer The {@link Watcher} that should be watching these attributes. + * @param logOnly If true then log errors; if false then throw an RuntimeExecption on error. */ - static void verifyWatchedAttributes(Object base, Watcher observer) { - if (Build.IS_ENG || Build.IS_USERDEBUG) { - for (Field f : base.getClass().getDeclaredFields()) { + static void verifyWatchedAttributes(Object base, Watcher observer, boolean logOnly) { + if (!(Build.IS_ENG || Build.IS_USERDEBUG)) { + return; + } + for (Field f : base.getClass().getDeclaredFields()) { + if (f.getAnnotation(Watched.class) != null) { + final String fn = base.getClass().getName() + "." + f.getName(); try { - final boolean flagged = isWatched(f); + f.setAccessible(true); final Object o = f.get(base); - final boolean watchable = o instanceof Watchable; - if (flagged && watchable) { - Watchable attr = (Watchable) f.get(base); + if (o instanceof Watchable) { + Watchable attr = (Watchable) (o); if (attr != null && !attr.isRegisteredObserver(observer)) { - throw new RuntimeException(f.getName() + " missing an observer"); + if (logOnly) { + Log.e("Watchable", fn + " missing an observer"); + } else { + throw new RuntimeException("Watchable " + fn + + " missing an observer"); + } } } } catch (IllegalAccessException e) { // The field is protected; ignore it. Other exceptions that may be thrown by // Field.get() are allowed to roll up. + if (logOnly) { + Log.e("Watchable", fn + " not visible"); + } else { + throw new RuntimeException("Watchable " + fn + " not visible"); + } } } } } + + /** + * Verify that all @Watched {@link Watchable} attributes are being watched by this + * class. This calls verifyWatchedAttributes() with logOnly set to false. + * @param base The object that contains watched attributes. + * @param observer The {@link Watcher} that should be watching these attributes. + */ + static void verifyWatchedAttributes(Object base, Watcher observer) { + verifyWatchedAttributes(base, observer, false); + } } diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index 4f2fc86df06c..bee66637fb2f 100644 --- a/services/core/java/com/android/server/vibrator/VibrationThread.java +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -17,6 +17,7 @@ package com.android.server.vibrator; import android.annotation.Nullable; +import android.hardware.vibrator.IVibratorManager; import android.os.CombinedVibrationEffect; import android.os.IBinder; import android.os.PowerManager; @@ -65,10 +66,13 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi * IVibratorManager.CAP_MIXED_TRIGGER_*. * @param vibratorIds The id of the vibrators to be prepared. */ - void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds); + boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds); /** Callback triggered after synchronized vibrations were prepared. */ - void triggerSyncedVibration(long vibrationId); + boolean triggerSyncedVibration(long vibrationId); + + /** Callback triggered to cancel a prepared synced vibration. */ + void cancelSyncedVibration(); /** Callback triggered when vibration thread is complete. */ void onVibrationEnded(long vibrationId, Vibration.Status status); @@ -146,6 +150,20 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi } } + /** Notify current vibration that a synced step has completed. */ + public void syncedVibrationComplete() { + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "Synced vibration complete reported by vibrator manager"); + } + if (mCurrentVibrateStep != null) { + for (int i = 0; i < mVibrators.size(); i++) { + mCurrentVibrateStep.vibratorComplete(mVibrators.keyAt(i)); + } + } + } + } + /** Notify current vibration that a step has completed on given vibrator. */ public void vibratorComplete(int vibratorId) { synchronized (mLock) { @@ -530,7 +548,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi /** Represent a synchronized vibration step on multiple vibrators. */ private final class SyncedVibrateStep implements VibrateStep { private final SparseArray<VibrationEffect> mEffects; - private final int mRequiredCapabilities; + private final long mRequiredCapabilities; private final int[] mVibratorIds; @GuardedBy("mLock") @@ -539,8 +557,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi SyncedVibrateStep(SparseArray<VibrationEffect> effects) { mEffects = effects; mActiveVibratorCounter = mEffects.size(); - // TODO(b/159207608): Calculate required capabilities for syncing this step. - mRequiredCapabilities = 0; + mRequiredCapabilities = calculateRequiredSyncCapabilities(effects); mVibratorIds = new int[effects.size()]; for (int i = 0; i < effects.size(); i++) { mVibratorIds[i] = effects.keyAt(i); @@ -574,18 +591,21 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi @Override public Vibration.Status play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep"); - long timeout = -1; + long duration = -1; try { if (DEBUG) { Slog.d(TAG, "SyncedVibrateStep starting..."); } final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size()); long startTime = SystemClock.uptimeMillis(); - mCallbacks.prepareSyncedVibration(mRequiredCapabilities, mVibratorIds); - timeout = startVibrating(startTime, nextSteps); - mCallbacks.triggerSyncedVibration(mVibration.id); - noteVibratorOn(timeout); + duration = startVibratingSynced(startTime, nextSteps); + if (duration <= 0) { + // Vibrate step failed, vibrator could not be turned on for this step. + return Vibration.Status.IGNORED; + } + + noteVibratorOn(duration); while (!nextSteps.isEmpty()) { AmplitudeStep step = nextSteps.poll(); if (!waitUntil(step.startTime)) { @@ -607,7 +627,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi synchronized (mLock) { // All OneShot and Waveform effects have finished. Just wait for the other // effects to end via native callbacks before finishing this synced step. - final long wakeUpTime = startTime + timeout + CALLBACKS_EXTRA_TIMEOUT; + final long wakeUpTime = startTime + duration + CALLBACKS_EXTRA_TIMEOUT; if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) { return Vibration.Status.FINISHED; } @@ -617,7 +637,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED; } } finally { - if (timeout > 0) { + if (duration > 0) { noteVibratorOff(); } if (DEBUG) { @@ -628,14 +648,48 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi } /** + * Starts playing effects on designated vibrators, in sync. + * + * @return A positive duration, in millis, to wait for the completion of this effect. + * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform + * returns the duration of a single run to be used as timeout for callbacks. + */ + private long startVibratingSynced(long startTime, PriorityQueue<AmplitudeStep> nextSteps) { + // This synchronization of vibrators should be executed one at a time, even if we are + // vibrating different sets of vibrators in parallel. The manager can only prepareSynced + // one set of vibrators at a time. + synchronized (mLock) { + boolean hasPrepared = false; + boolean hasTriggered = false; + try { + hasPrepared = mCallbacks.prepareSyncedVibration(mRequiredCapabilities, + mVibratorIds); + long timeout = startVibrating(startTime, nextSteps); + + // Check if preparation was successful, otherwise devices area already vibrating + if (hasPrepared) { + hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id); + } + return timeout; + } finally { + if (hasPrepared && !hasTriggered) { + mCallbacks.cancelSyncedVibration(); + return 0; + } + } + } + } + + /** * Starts playing effects on designated vibrators. * * <p>This includes the {@link VibrationEffect.OneShot} and {@link VibrationEffect.Waveform} * effects, that should start in sync with all other effects in this step. The waveforms are * controlled by {@link AmplitudeStep} added to the {@code nextSteps} queue. * - * @return A duration, in millis, to wait for the completion of all vibrations. This ignores - * any repeating waveform duration and returns the duration of a single run. + * @return A positive duration, in millis, to wait for the completion of this effect. + * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform + * returns the duration of a single run to be used as timeout for callbacks. */ private long startVibrating(long startTime, PriorityQueue<AmplitudeStep> nextSteps) { long maxDuration = 0; @@ -651,9 +705,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi /** * Play a single effect on a single vibrator. * - * @return A duration, in millis, to wait for the completion of this effect. This ignores - * any repeating waveform duration and returns the duration of a single run to be used as - * timeout for callbacks. + * @return A positive duration, in millis, to wait for the completion of this effect. + * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform + * returns the duration of a single run to be used as timeout for callbacks. */ private long startVibrating(VibratorController controller, VibrationEffect effect, long startTime, PriorityQueue<AmplitudeStep> nextSteps) { @@ -698,6 +752,49 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi } } } + + /** + * Return all capabilities required from the {@link IVibratorManager} to prepare and + * trigger all given effects in sync. + * + * @return {@link IVibratorManager#CAP_SYNC} together with all required + * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities. + */ + private long calculateRequiredSyncCapabilities(SparseArray<VibrationEffect> effects) { + long prepareCap = 0; + for (int i = 0; i < effects.size(); i++) { + VibrationEffect effect = effects.valueAt(i); + if (effect instanceof VibrationEffect.OneShot + || effect instanceof VibrationEffect.Waveform) { + prepareCap |= IVibratorManager.CAP_PREPARE_ON; + } else if (effect instanceof VibrationEffect.Prebaked) { + prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM; + } else if (effect instanceof VibrationEffect.Composed) { + prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE; + } + } + int triggerCap = 0; + if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) { + triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON; + } + if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) { + triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM; + } + if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) { + triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE; + } + return IVibratorManager.CAP_SYNC | prepareCap | triggerCap; + } + + /** + * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with + * different ones, requiring a mixed trigger capability from the vibrator manager for + * syncing all effects. + */ + private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) { + return (prepareCapabilities & capability) != 0 + && (prepareCapabilities & ~capability) != 0; + } } /** Represent a step to set amplitude on a single vibrator. */ @@ -794,12 +891,12 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi // Waveform has ended, no more steps to run. return null; } - long nextWakeUpTime = startTime + waveform.getTimings()[currentIndex]; + long nextStartTime = startTime + waveform.getTimings()[currentIndex]; int nextIndex = currentIndex + 1; if (nextIndex >= waveform.getTimings().length) { nextIndex = waveform.getRepeatIndex(); } - return new AmplitudeStep(vibratorId, waveform, nextIndex, nextWakeUpTime, + return new AmplitudeStep(vibratorId, waveform, nextIndex, nextStartTime, nextVibratorStopTime()); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f97af6259c9c..f16a646d00a1 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -788,7 +788,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean sizeCompatFreeform = Settings.Global.getInt( resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0; final boolean supportsNonResizableMultiWindow = Settings.Global.getInt( - resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0; + resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0; // Transfer any global setting for forcing RTL layout, into a System Property DisplayProperties.debug_force_rtl(forceRtl); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 61fe023ae1b9..5460e36a5f1b 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2086,6 +2086,7 @@ public class DisplayPolicy { pi.getResDir(), null /* splitResDirs */, pi.getOverlayDirs(), + pi.getOverlayPaths(), pi.getApplicationInfo().sharedLibraryFiles, mDisplayContent.getDisplayId(), null /* overrideConfig */, diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 737f8107f83f..8fcdf2e96889 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -109,12 +109,13 @@ class DisplayWindowSettingsProvider implements SettingsProvider { */ void setBaseSettingsFilePath(@Nullable String path) { AtomicFile settingsFile; - if (path != null) { - settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG); + File file = path != null ? new File(path) : null; + if (file != null && file.exists()) { + settingsFile = new AtomicFile(file, WM_DISPLAY_COMMIT_TAG); } else { + Slog.w(TAG, "display settings " + path + " does not exist, using vendor defaults"); settingsFile = getVendorSettingsFile(); } - setBaseSettingsStorage(new AtomicFileStorage(settingsFile)); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ceebe9550846..bd93e045cdc7 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -847,6 +847,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.openSurfaceTransaction(); try { applySurfaceChangesTransaction(); + // Send any pending task-info changes that were queued-up during a layout deferment + mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mSyncEngine.onSurfacePlacement(); } catch (RuntimeException e) { Slog.wtf(TAG, "Unhandled exception in Window Manager", e); @@ -859,8 +861,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - // Send any pending task-info changes that were queued-up during a layout deferment - mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 1f8daf6f361f..8b186796db8d 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -104,9 +104,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final boolean mCanAddInternalSystemWindow; private final boolean mCanStartTasksFromRecents; - // If non-system overlays from this process can be hidden by the user or app using - // HIDE_NON_SYSTEM_OVERLAY_WINDOWS. - final boolean mOverlaysCanBeHidden; final boolean mCanCreateSystemApplicationOverlay; final boolean mCanHideNonSystemOverlayWindows; final boolean mCanAcquireSleepToken; @@ -136,8 +133,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { == PERMISSION_GRANTED; mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission( START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED; - mOverlaysCanBeHidden = !mCanAddInternalSystemWindow - && !mService.mAtmInternal.isCallerRecents(mUid); mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER) == PERMISSION_GRANTED; mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications; @@ -253,11 +248,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public void setTransparentRegion(IWindow window, Region region) { - mService.setTransparentRegionWindow(this, window, region); - } - - @Override public void setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableArea) { mService.setInsetsWindow(this, window, touchableInsets, contentInsets, @@ -682,7 +672,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { boolean changed; - if (mOverlaysCanBeHidden && !mCanCreateSystemApplicationOverlay) { + if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) { // We want to track non-system apps adding alert windows so we can post an // on-going notification for the user to control their visibility. if (visible) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3c7bab3da279..c54c9786c00d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -977,7 +977,7 @@ public class WindowManagerService extends IWindowManager.Stub void updateSupportsNonResizableMultiWindow() { ContentResolver resolver = mContext.getContentResolver(); final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(resolver, - DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0; + DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0; mAtmService.mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow; } @@ -2150,23 +2150,6 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(tag, s, e); } - void setTransparentRegionWindow(Session session, IWindow client, Region region) { - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - WindowState w = windowForClientLocked(session, client, false); - ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE transparentRegionHint=%s: %s", - region, w); - - if ((w != null) && w.mHasSurface) { - w.mWinAnimator.setTransparentRegionHintLocked(region); - } - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableRegion) { int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index fd3d9ba499b7..a94b0aa9b72f 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3100,7 +3100,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) { - if (!mSession.mOverlaysCanBeHidden + if (mSession.mCanAddInternalSystemWindow || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) { return; } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index fe70dc12d3a2..ece256e8c591 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -678,14 +678,6 @@ class WindowStateAnimator { } } - void setTransparentRegionHintLocked(final Region region) { - if (mSurfaceController == null) { - Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true"); - return; - } - mSurfaceController.setTransparentRegionHint(region); - } - boolean setWallpaperOffset(int dx, int dy, float scale) { if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) { return false; diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 82ba3c188b76..636f0bb6086f 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -194,22 +194,6 @@ class WindowSurfaceController { return true; } - void setTransparentRegionHint(final Region region) { - if (mSurfaceControl == null) { - Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true"); - return; - } - if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion"); - mService.openSurfaceTransaction(); - try { - getGlobalTransaction().setTransparentRegionHint(mSurfaceControl, region); - } finally { - mService.closeSurfaceTransaction("setTransparentRegion"); - if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, - "<<< CLOSE TRANSACTION setTransparentRegion"); - } - } - void setOpaque(boolean isOpaque) { ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, title); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 91be0564a26f..1c4b03498144 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -56,10 +56,10 @@ cc_library_static { "com_android_server_vibrator_VibratorController.cpp", "com_android_server_VibratorManagerService.cpp", "com_android_server_PersistentDataBlockService.cpp", - "com_android_server_am_CachedAppOptimizer.cpp", "com_android_server_am_LowMemDetector.cpp", "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp", "onload.cpp", + ":lib_cachedAppOptimizer_native", ":lib_networkStatsFactory_native", ], @@ -193,3 +193,10 @@ filegroup { "com_android_server_net_NetworkStatsFactory.cpp", ], } + +filegroup { + name: "lib_cachedAppOptimizer_native", + srcs: [ + "com_android_server_am_CachedAppOptimizer.cpp", + ], +} diff --git a/services/core/jni/com_android_server_VibratorManagerService.cpp b/services/core/jni/com_android_server_VibratorManagerService.cpp index 71de9bda3c4f..5dbb71a4976c 100644 --- a/services/core/jni/com_android_server_VibratorManagerService.cpp +++ b/services/core/jni/com_android_server_VibratorManagerService.cpp @@ -24,27 +24,47 @@ #include <utils/Log.h> #include <utils/misc.h> -#include <vibratorservice/VibratorManagerHalWrapper.h> +#include <vibratorservice/VibratorManagerHalController.h> #include "com_android_server_VibratorManagerService.h" namespace android { +static JavaVM* sJvm = nullptr; +static jmethodID sMethodIdOnComplete; static std::mutex gManagerMutex; -static vibrator::ManagerHalWrapper* gManager GUARDED_BY(gManagerMutex) = nullptr; +static vibrator::ManagerHalController* gManager GUARDED_BY(gManagerMutex) = nullptr; class NativeVibratorManagerService { public: - NativeVibratorManagerService() : mHal(std::make_unique<vibrator::LegacyManagerHalWrapper>()) {} - ~NativeVibratorManagerService() = default; + NativeVibratorManagerService(JNIEnv* env, jobject callbackListener) + : mHal(std::make_unique<vibrator::ManagerHalController>()), + mCallbackListener(env->NewGlobalRef(callbackListener)) { + LOG_ALWAYS_FATAL_IF(mHal == nullptr, "Unable to find reference to vibrator manager hal"); + LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr, + "Unable to create global reference to vibration callback handler"); + } + + ~NativeVibratorManagerService() { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jniEnv->DeleteGlobalRef(mCallbackListener); + } + + vibrator::ManagerHalController* hal() const { return mHal.get(); } - vibrator::ManagerHalWrapper* hal() const { return mHal.get(); } + std::function<void()> createCallback(jlong vibrationId) { + return [vibrationId, this]() { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId); + }; + } private: - const std::unique_ptr<vibrator::ManagerHalWrapper> mHal; + const std::unique_ptr<vibrator::ManagerHalController> mHal; + const jobject mCallbackListener; }; -vibrator::ManagerHalWrapper* android_server_VibratorManagerService_getManager() { +vibrator::ManagerHalController* android_server_VibratorManagerService_getManager() { std::lock_guard<std::mutex> lock(gManagerMutex); return gManager; } @@ -58,9 +78,9 @@ static void destroyNativeService(void* ptr) { } } -static jlong nativeInit(JNIEnv* /* env */, jclass /* clazz */) { +static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject callbackListener) { std::unique_ptr<NativeVibratorManagerService> service = - std::make_unique<NativeVibratorManagerService>(); + std::make_unique<NativeVibratorManagerService>(env, callbackListener); { std::lock_guard<std::mutex> lock(gManagerMutex); gManager = service->hal(); @@ -72,6 +92,17 @@ static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService)); } +static jlong nativeGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorManagerService* service = + reinterpret_cast<NativeVibratorManagerService*>(servicePtr); + if (service == nullptr) { + ALOGE("nativeGetCapabilities failed because native service was not initialized"); + return 0; + } + auto result = service->hal()->getCapabilities(); + return result.isOk() ? static_cast<jlong>(result.value()) : 0; +} + static jintArray nativeGetVibratorIds(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { NativeVibratorManagerService* service = reinterpret_cast<NativeVibratorManagerService*>(servicePtr); @@ -89,13 +120,61 @@ static jintArray nativeGetVibratorIds(JNIEnv* env, jclass /* clazz */, jlong ser return ids; } +static jboolean nativePrepareSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr, + jintArray vibratorIds) { + NativeVibratorManagerService* service = + reinterpret_cast<NativeVibratorManagerService*>(servicePtr); + if (service == nullptr) { + ALOGE("nativePrepareSynced failed because native service was not initialized"); + return JNI_FALSE; + } + jsize size = env->GetArrayLength(vibratorIds); + std::vector<int32_t> ids(size); + env->GetIntArrayRegion(vibratorIds, 0, size, reinterpret_cast<jint*>(ids.data())); + return service->hal()->prepareSynced(ids).isOk() ? JNI_TRUE : JNI_FALSE; +} + +static jboolean nativeTriggerSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr, + jlong vibrationId) { + NativeVibratorManagerService* service = + reinterpret_cast<NativeVibratorManagerService*>(servicePtr); + if (service == nullptr) { + ALOGE("nativeTriggerSynced failed because native service was not initialized"); + return JNI_FALSE; + } + auto callback = service->createCallback(vibrationId); + return service->hal()->triggerSynced(callback).isOk() ? JNI_TRUE : JNI_FALSE; +} + +static void nativeCancelSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorManagerService* service = + reinterpret_cast<NativeVibratorManagerService*>(servicePtr); + if (service == nullptr) { + ALOGE("nativeCancelSynced failed because native service was not initialized"); + return; + } + service->hal()->cancelSynced(); +} + static const JNINativeMethod method_table[] = { - {"nativeInit", "()J", (void*)nativeInit}, + {"nativeInit", + "(Lcom/android/server/VibratorManagerService$OnSyncedVibrationCompleteListener;)J", + (void*)nativeInit}, {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer}, + {"nativeGetCapabilities", "(J)J", (void*)nativeGetCapabilities}, {"nativeGetVibratorIds", "(J)[I", (void*)nativeGetVibratorIds}, + {"nativePrepareSynced", "(J[I)Z", (void*)nativePrepareSynced}, + {"nativeTriggerSynced", "(JJ)Z", (void*)nativeTriggerSynced}, + {"nativeCancelSynced", "(J)V", (void*)nativeCancelSynced}, }; -int register_android_server_VibratorManagerService(JNIEnv* env) { +int register_android_server_VibratorManagerService(JavaVM* jvm, JNIEnv* env) { + sJvm = jvm; + auto listenerClassName = + "com/android/server/VibratorManagerService$OnSyncedVibrationCompleteListener"; + jclass listenerClass = FindClassOrDie(env, listenerClassName); + sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V"); + return jniRegisterNativeMethods(env, "com/android/server/VibratorManagerService", method_table, NELEM(method_table)); } diff --git a/services/core/jni/com_android_server_VibratorManagerService.h b/services/core/jni/com_android_server_VibratorManagerService.h index 3f2a322b19ee..22950c5036e4 100644 --- a/services/core/jni/com_android_server_VibratorManagerService.h +++ b/services/core/jni/com_android_server_VibratorManagerService.h @@ -17,11 +17,11 @@ #ifndef _ANDROID_SERVER_VIBRATOR_MANAGER_SERVICE_H #define _ANDROID_SERVER_VIBRATOR_MANAGER_SERVICE_H -#include <vibratorservice/VibratorManagerHalWrapper.h> +#include <vibratorservice/VibratorManagerHalController.h> namespace android { -extern vibrator::ManagerHalWrapper* android_server_VibratorManagerService_getManager(); +extern vibrator::ManagerHalController* android_server_VibratorManagerService_getManager(); } // namespace android diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 31cc295406a5..4551d49d9e58 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -278,6 +278,11 @@ static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv return retVal; } +static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env, + jobject clazz) { + return env->NewStringUTF(CGROUP_FREEZE_PATH); +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, @@ -286,7 +291,9 @@ static const JNINativeMethod sMethods[] = { (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal}, {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, {"getBinderFreezeInfo", "(I)I", - (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}}; + (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}, + {"getFreezerCheckPath", "()Ljava/lang/String;", + (void*)com_android_server_am_CachedAppOptimizer_getFreezerCheckPath}}; int register_android_server_am_CachedAppOptimizer(JNIEnv* env) { diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index 7ed37481f8cb..4e47984fa75c 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -77,7 +77,7 @@ static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) if (vibratorId < 0) { return std::move(std::make_unique<vibrator::HalController>()); } - vibrator::ManagerHalWrapper* manager = android_server_VibratorManagerService_getManager(); + vibrator::ManagerHalController* manager = android_server_VibratorManagerService_getManager(); if (manager == nullptr) { return nullptr; } diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index c5394f3aba69..34f604895721 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -39,7 +39,7 @@ int register_android_server_UsbMidiDevice(JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_vr_VrManagerService(JNIEnv* env); int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env); -int register_android_server_VibratorManagerService(JNIEnv* env); +int register_android_server_VibratorManagerService(JavaVM* vm, JNIEnv* env); int register_android_server_location_GnssLocationProvider(JNIEnv* env); int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*); int register_android_server_tv_TvUinputBridge(JNIEnv* env); @@ -90,7 +90,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_UsbHostManager(env); register_android_server_vr_VrManagerService(env); register_android_server_vibrator_VibratorController(vm, env); - register_android_server_VibratorManagerService(env); + register_android_server_VibratorManagerService(vm, env); register_android_server_SystemServer(env); register_android_server_location_GnssLocationProvider(env); register_android_server_devicepolicy_CryptoTestHelper(env); diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt index d863194d6889..c2e0b776fc60 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt @@ -18,6 +18,7 @@ package com.android.server.pm.test.verify.domain import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.content.pm.PackageUserState import android.content.pm.verify.domain.DomainVerificationManager import android.content.pm.parsing.component.ParsedActivity @@ -25,7 +26,6 @@ import android.content.pm.parsing.component.ParsedIntentInfo import android.os.Build import android.os.Process import android.util.ArraySet -import android.util.Singleton import android.util.SparseArray import androidx.test.platform.app.InstrumentationRegistry import com.android.server.pm.PackageSetting @@ -46,14 +46,11 @@ import org.mockito.Mockito.anyLong import org.mockito.Mockito.anyString import org.mockito.Mockito.eq import org.mockito.Mockito.verifyNoMoreInteractions -import org.testng.Assert.assertThrows import java.io.File import java.util.UUID import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger -private typealias Enforcer = DomainVerificationEnforcer - @RunWith(Parameterized::class) class DomainVerificationEnforcerTest { @@ -64,64 +61,27 @@ class DomainVerificationEnforcerTest { private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1 private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2 - private const val TEST_PKG = "com.test" + private const val VISIBLE_PKG = "com.test.visible" + private val VISIBLE_UUID = UUID.fromString("8db01272-270d-4606-a3db-bb35228ff9a2") + private const val INVISIBLE_PKG = "com.test.invisible" + private val INVISIBLE_UUID = UUID.fromString("16dcb029-d96c-4a19-833a-4c9d72e2ebc3") @JvmStatic @Parameterized.Parameters(name = "{0}") fun parameters(): Array<Any> { - val makeEnforcer: (Context) -> DomainVerificationEnforcer = { - DomainVerificationEnforcer(it) - } + val visiblePkg = mockPkg(VISIBLE_PKG) + val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID) + val invisiblePkg = mockPkg(INVISIBLE_PKG) + val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID) - val mockPkg = mockThrowOnUnmocked<AndroidPackage> { - whenever(packageName) { TEST_PKG } - whenever(targetSdkVersion) { Build.VERSION_CODES.S } - whenever(activities) { - listOf( - ParsedActivity().apply { - addIntent( - ParsedIntentInfo().apply { - autoVerify = true - addAction(Intent.ACTION_VIEW) - addCategory(Intent.CATEGORY_BROWSABLE) - addCategory(Intent.CATEGORY_DEFAULT) - addDataScheme("https") - addDataAuthority("example.com", null) - } - ) + val makeEnforcer: (Context) -> DomainVerificationEnforcer = { + DomainVerificationEnforcer(it).apply { + setCallback(mockThrowOnUnmocked { + whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false } + whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) { + true } - ) - } - } - - val uuid = UUID.randomUUID() - - // TODO: PackageSetting field encapsulation to move to whenever(name) - val mockPkgSetting = spyThrowOnUnmocked( - PackageSetting( - TEST_PKG, - TEST_PKG, - File("/test"), - null, - null, - null, - null, - 1, - 0, - 0, - 0, - null, - null, - null, - uuid - ) - ) { - whenever(getPkg()) { mockPkg } - whenever(domainSetId) { uuid } - whenever(userState) { - SparseArray<PackageUserState>().apply { - this[0] = PackageUserState() - } + }) } } @@ -129,142 +89,160 @@ class DomainVerificationEnforcerTest { { val callingUidInt = AtomicInteger(-1) val callingUserIdInt = AtomicInteger(-1) - Triple( - callingUidInt, callingUserIdInt, DomainVerificationService( - it, - mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } }, - mockThrowOnUnmocked { - whenever( - isChangeEnabled( - anyLong(), - any() - ) - ) { true } - }).apply { - setConnection(mockThrowOnUnmocked { - whenever(callingUid) { callingUidInt.get() } - whenever(callingUserId) { callingUserIdInt.get() } - whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting } - whenever(getPackageLocked(TEST_PKG)) { mockPkg } - whenever(schedule(anyInt(), any())) - whenever(scheduleWriteSettings()) - }) + + val connection: DomainVerificationManagerInternal.Connection = + mockThrowOnUnmocked { + whenever(callingUid) { callingUidInt.get() } + whenever(callingUserId) { callingUserIdInt.get() } + whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting } + whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg } + whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting } + whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg } + whenever(schedule(anyInt(), any())) + whenever(scheduleWriteSettings()) + whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false } + whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) { + true + } } - ) + val service = DomainVerificationService( + it, + mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } }, + mockThrowOnUnmocked { + whenever( + isChangeEnabled( + anyLong(), + any() + ) + ) { true } + }).apply { + setConnection(connection) + } + + Triple(callingUidInt, callingUserIdInt, service) } fun enforcer( type: Type, name: String, - block: DomainVerificationEnforcer.( - callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy - ) -> Unit - ) = Params( - type, - makeEnforcer, - name - ) { enforcer, callingUid, callingUserId, userId, proxy -> - enforcer.block(callingUid, callingUserId, userId, proxy) + block: DomainVerificationEnforcer.(Params.Input<DomainVerificationEnforcer>) -> Any? + ) = Params(type, makeEnforcer, name) { + it.target.block(it) } fun service( type: Type, name: String, - block: DomainVerificationService.( - callingUid: Int, callingUserId: Int, userId: Int - ) -> Unit - ) = Params( - type, - makeService, - name - ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy -> - val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService - callingUidInt.set(callingUid) - callingUserIdInt.set(callingUserId) - service.setProxy(proxy) - service.addPackage(mockPkgSetting) - service.block(callingUid, callingUserId, userId) + block: DomainVerificationService.(Params.Input<Triple<AtomicInteger, AtomicInteger, DomainVerificationService>>) -> Any? + ) = Params(type, makeService, name) { + val (callingUidInt, callingUserIdInt, service) = it.target + callingUidInt.set(it.callingUid) + callingUserIdInt.set(it.callingUserId) + service.proxy = it.proxy + service.addPackage(visiblePkgSetting) + service.addPackage(invisiblePkgSetting) + service.block(it) } return arrayOf( - enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ -> - assertInternal(callingUid) + enforcer(Type.INTERNAL, "internal") { + assertInternal(it.callingUid) }, - enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy -> - assertApprovedQuerent(callingUid, proxy) + enforcer(Type.QUERENT, "approvedQuerent") { + assertApprovedQuerent(it.callingUid, it.proxy) }, - enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy -> - assertApprovedVerifier(callingUid, proxy) + enforcer(Type.VERIFIER, "approvedVerifier") { + assertApprovedVerifier(it.callingUid, it.proxy) }, enforcer( Type.SELECTOR, "approvedUserSelector" - ) { callingUid, callingUserId, userId, _ -> - assertApprovedUserSelector(callingUid, callingUserId, userId) + ) { + assertApprovedUserSelector( + it.callingUid, it.callingUserId, + it.targetPackageName, it.userId + ) }, - - service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ -> + service(Type.INTERNAL, "setStatusInternalPackageName") { setDomainVerificationStatusInternal( - TEST_PKG, + it.targetPackageName, DomainVerificationManager.STATE_SUCCESS, ArraySet(setOf("example.com")) ) }, - service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId -> + service(Type.INTERNAL, "setUserSelectionInternal") { setDomainVerificationUserSelectionInternal( - userId, - TEST_PKG, + it.userId, + it.targetPackageName, false, ArraySet(setOf("example.com")) ) }, - service(Type.INTERNAL, "verifyPackages") { _, _, _ -> - verifyPackages(listOf(TEST_PKG), true) + service(Type.INTERNAL, "verifyPackages") { + verifyPackages(listOf(it.targetPackageName), true) }, - service(Type.INTERNAL, "clearState") { _, _, _ -> - clearDomainVerificationState(listOf(TEST_PKG)) + service(Type.INTERNAL, "clearState") { + clearDomainVerificationState(listOf(it.targetPackageName)) }, - service(Type.INTERNAL, "clearUserSelections") { _, _, userId -> - clearUserSelections(listOf(TEST_PKG), userId) + service(Type.INTERNAL, "clearUserSelections") { + clearUserSelections(listOf(it.targetPackageName), it.userId) }, - service(Type.VERIFIER, "getPackageNames") { _, _, _ -> + service(Type.VERIFIER, "getPackageNames") { validVerificationPackageNames }, - service(Type.QUERENT, "getInfo") { _, _, _ -> - getDomainVerificationInfo(TEST_PKG) + service(Type.QUERENT, "getInfo") { + getDomainVerificationInfo(it.targetPackageName) }, - service(Type.VERIFIER, "setStatus") { _, _, _ -> + service(Type.VERIFIER, "setStatus") { setDomainVerificationStatus( - uuid, + it.targetDomainSetId, setOf("example.com"), DomainVerificationManager.STATE_SUCCESS ) }, - service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ -> + service(Type.VERIFIER, "setStatusInternalUid") { setDomainVerificationStatusInternal( - callingUid, - uuid, + it.callingUid, + it.targetDomainSetId, setOf("example.com"), DomainVerificationManager.STATE_SUCCESS ) }, - service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ -> - setDomainVerificationLinkHandlingAllowed(TEST_PKG, true) + service(Type.SELECTOR, "setLinkHandlingAllowed") { + setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true) }, - service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId -> - setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId) + service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { + setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId) }, - service(Type.SELECTOR, "getUserSelection") { _, _, _ -> - getDomainVerificationUserSelection(TEST_PKG) + service(Type.SELECTOR, "getUserSelection") { + getDomainVerificationUserSelection(it.targetPackageName) }, - service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId -> - getDomainVerificationUserSelection(TEST_PKG, userId) + service(Type.SELECTOR_USER, "getUserSelectionUserId") { + getDomainVerificationUserSelection(it.targetPackageName, it.userId) }, - service(Type.SELECTOR, "setUserSelection") { _, _, _ -> - setDomainVerificationUserSelection(uuid, setOf("example.com"), true) + service(Type.SELECTOR, "setUserSelection") { + setDomainVerificationUserSelection( + it.targetDomainSetId, + setOf("example.com"), + true + ) + }, + service(Type.SELECTOR_USER, "setUserSelectionUserId") { + setDomainVerificationUserSelection( + it.targetDomainSetId, + setOf("example.com"), + true, + it.userId + ) }, - service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId -> - setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId) + service(Type.LEGACY_SELECTOR, "setLegacyUserState") { + setLegacyUserState( + it.targetPackageName, it.userId, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER + ) + }, + service(Type.LEGACY_QUERENT, "getLegacyUserState") { + getLegacyState(it.targetPackageName, it.userId) }, ) } @@ -273,9 +251,7 @@ class DomainVerificationEnforcerTest { val type: Type, val construct: (context: Context) -> T, val name: String, - private val method: ( - T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy - ) -> Unit + private val method: (Input<T>) -> Any? ) { override fun toString() = "${type}_$name" @@ -284,10 +260,79 @@ class DomainVerificationEnforcerTest { callingUid: Int, callingUserId: Int, userId: Int, + targetPackageName: String, + targetDomainSetId: UUID, proxy: DomainVerificationProxy - ) { - @Suppress("UNCHECKED_CAST") - method(target as T, callingUid, callingUserId, userId, proxy) + ): Any? = method( + Input( + @Suppress("UNCHECKED_CAST") + target as T, + callingUid, + callingUserId, + userId, + targetPackageName, + targetDomainSetId, + proxy + ) + ) + + data class Input<T>( + val target: T, + val callingUid: Int, + val callingUserId: Int, + val userId: Int, + val targetPackageName: String, + val targetDomainSetId: UUID, + val proxy: DomainVerificationProxy + ) + } + + fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackage> { + whenever(this.packageName) { packageName } + whenever(targetSdkVersion) { Build.VERSION_CODES.S } + whenever(activities) { + listOf( + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + autoVerify = true + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("https") + addDataAuthority("example.com", null) + } + ) + } + ) + } + } + + fun mockPkgSetting(packageName: String, domainSetId: UUID) = spyThrowOnUnmocked( + PackageSetting( + packageName, + packageName, + File("/test"), + null, + null, + null, + null, + 1, + 0, + 0, + 0, + null, + null, + null, + domainSetId + ) + ) { + whenever(getPkg()) { mockPkg(packageName) } + whenever(this.domainSetId) { domainSetId } + whenever(userState) { + SparseArray<PackageUserState>().apply { + this[0] = PackageUserState() + } } } } @@ -309,6 +354,8 @@ class DomainVerificationEnforcerTest { Type.VERIFIER -> approvedVerifier() Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false) Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true) + Type.LEGACY_QUERENT -> legacyQuerent() + Type.LEGACY_SELECTOR -> legacyUserSelector() }.run { /*exhaust*/ } } @@ -316,24 +363,30 @@ class DomainVerificationEnforcerTest { val context: Context = mockThrowOnUnmocked() val target = params.construct(context) - INTERNAL_UIDS.forEach { runMethod(target, it) } - assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) } - assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + // Internal doesn't care about visibility + listOf(true, false).forEach { visible -> + INTERNAL_UIDS.forEach { runMethod(target, it, visible) } + assertFails { runMethod(target, VERIFIER_UID, visible) } + assertFails { + runMethod(target, NON_VERIFIER_UID, visible) + } + } } fun approvedQuerent() { val allowUserSelection = AtomicBoolean(false) + val allowPreferredApps = AtomicBoolean(false) + val allowQueryAll = AtomicBoolean(false) val context: Context = mockThrowOnUnmocked { - whenever( - enforcePermission( - eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION), - anyInt(), anyInt(), anyString() - ) - ) { - if (!allowUserSelection.get()) { - throw SecurityException() - } - } + initPermission( + allowUserSelection, + android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION + ) + initPermission( + allowPreferredApps, + android.Manifest.permission.SET_PREFERRED_APPLICATIONS + ) + initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES) } val target = params.construct(context) @@ -341,27 +394,41 @@ class DomainVerificationEnforcerTest { verifyNoMoreInteractions(context) + assertFails { runMethod(target, VERIFIER_UID) } + assertFails { runMethod(target, NON_VERIFIER_UID) } + + // Check that the verifier only needs QUERY_ALL to pass + allowQueryAll.set(true) runMethod(target, VERIFIER_UID) - assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + allowQueryAll.set(false) + + allowPreferredApps.set(true) + + assertFails { runMethod(target, NON_VERIFIER_UID) } allowUserSelection.set(true) + assertFails { runMethod(target, NON_VERIFIER_UID) } + + allowQueryAll.set(true) + runMethod(target, NON_VERIFIER_UID) } fun approvedVerifier() { - val shouldThrow = AtomicBoolean(false) + val allowDomainVerificationAgent = AtomicBoolean(false) + val allowIntentVerificationAgent = AtomicBoolean(false) + val allowQueryAll = AtomicBoolean(false) val context: Context = mockThrowOnUnmocked { - whenever( - enforcePermission( - eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT), - anyInt(), anyInt(), anyString() - ) - ) { - if (shouldThrow.get()) { - throw SecurityException() - } - } + initPermission( + allowDomainVerificationAgent, + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT + ) + initPermission( + allowIntentVerificationAgent, + android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT + ) + initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES) } val target = params.construct(context) @@ -369,94 +436,241 @@ class DomainVerificationEnforcerTest { verifyNoMoreInteractions(context) + assertFails { runMethod(target, VERIFIER_UID) } + assertFails { runMethod(target, NON_VERIFIER_UID) } + + allowDomainVerificationAgent.set(true) + + assertFails { runMethod(target, VERIFIER_UID) } + assertFails { runMethod(target, NON_VERIFIER_UID) } + + allowQueryAll.set(true) + runMethod(target, VERIFIER_UID) - assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + assertFails { runMethod(target, NON_VERIFIER_UID) } - shouldThrow.set(true) + // Check that v1 verifiers are also allowed through + allowDomainVerificationAgent.set(false) + allowIntentVerificationAgent.set(true) - assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) } - assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) } + runMethod(target, VERIFIER_UID) + assertFails { runMethod(target, NON_VERIFIER_UID) } } fun approvedUserSelector(verifyCrossUser: Boolean) { - val allowUserSelection = AtomicBoolean(true) - val allowInteractAcrossUsers = AtomicBoolean(true) + val allowUserSelection = AtomicBoolean(false) + val allowInteractAcrossUsers = AtomicBoolean(false) val context: Context = mockThrowOnUnmocked { - whenever( - enforcePermission( - eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION), - anyInt(), anyInt(), anyString() - ) - ) { - if (!allowUserSelection.get()) { - throw SecurityException() + initPermission( + allowUserSelection, + android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION + ) + initPermission( + allowInteractAcrossUsers, + android.Manifest.permission.INTERACT_ACROSS_USERS + ) + } + val target = params.construct(context) + + fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { + // User selector makes no distinction by UID + val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID + if (throws) { + allUids.forEach { + assertFails { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } } - } - whenever( - enforcePermission( - eq(android.Manifest.permission.INTERACT_ACROSS_USERS), - anyInt(), anyInt(), anyString() - ) - ) { - if (!allowInteractAcrossUsers.get()) { - throw SecurityException() + } else { + allUids.forEach { + runMethod(target, it, visible = true, callingUserId, targetUserId) } } - } - val target = params.construct(context) - fun runEachTestCaseWrapped( - callingUserId: Int, - targetUserId: Int, - block: (testCase: () -> Unit) -> Unit = { it.invoke() } - ) { - block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) } - block { runMethod(target, NON_VERIFIER_UID, callingUserId, targetUserId) } + // User selector doesn't use QUERY_ALL, so the invisible package should always fail + allUids.forEach { + assertFails { + runMethod(target, it, visible = false, callingUserId, targetUserId) + } + } } val callingUserId = 0 val notCallingUserId = 1 - runEachTestCaseWrapped(callingUserId, callingUserId) + runTestCases(callingUserId, callingUserId, throws = true) if (verifyCrossUser) { - runEachTestCaseWrapped(callingUserId, notCallingUserId) + runTestCases(callingUserId, notCallingUserId, throws = true) } - allowInteractAcrossUsers.set(false) + allowUserSelection.set(true) + + runTestCases(callingUserId, callingUserId, throws = false) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = true) + } - runEachTestCaseWrapped(callingUserId, callingUserId) + allowInteractAcrossUsers.set(true) + runTestCases(callingUserId, callingUserId, throws = false) if (verifyCrossUser) { - runEachTestCaseWrapped(callingUserId, notCallingUserId) { - assertThrows(SecurityException::class.java, it) + runTestCases(callingUserId, notCallingUserId, throws = false) + } + } + + private fun legacyUserSelector() { + val allowInteractAcrossUsers = AtomicBoolean(false) + val allowPreferredApps = AtomicBoolean(false) + val context: Context = mockThrowOnUnmocked { + initPermission( + allowInteractAcrossUsers, + android.Manifest.permission.INTERACT_ACROSS_USERS + ) + initPermission( + allowPreferredApps, + android.Manifest.permission.SET_PREFERRED_APPLICATIONS + ) + } + val target = params.construct(context) + + fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { + // Legacy makes no distinction by UID + val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID + if (throws) { + allUids.forEach { + assertFails { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + } else { + allUids.forEach { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + + // Legacy doesn't use QUERY_ALL, so the invisible package should always fail + allUids.forEach { + assertFails { + runMethod(target, it, visible = false, callingUserId, targetUserId) + } } } - allowUserSelection.set(false) + val callingUserId = 0 + val notCallingUserId = 1 + + runTestCases(callingUserId, callingUserId, throws = true) + runTestCases(callingUserId, notCallingUserId, throws = true) + + allowPreferredApps.set(true) - runEachTestCaseWrapped(callingUserId, callingUserId) { - assertThrows(SecurityException::class.java, it) + runTestCases(callingUserId, callingUserId, throws = false) + runTestCases(callingUserId, notCallingUserId, throws = true) + + allowInteractAcrossUsers.set(true) + + runTestCases(callingUserId, callingUserId, throws = false) + runTestCases(callingUserId, notCallingUserId, throws = false) + } + + private fun legacyQuerent() { + val allowInteractAcrossUsers = AtomicBoolean(false) + val allowInteractAcrossUsersFull = AtomicBoolean(false) + val context: Context = mockThrowOnUnmocked { + initPermission( + allowInteractAcrossUsers, + android.Manifest.permission.INTERACT_ACROSS_USERS + ) + initPermission( + allowInteractAcrossUsersFull, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL + ) } - if (verifyCrossUser) { - runEachTestCaseWrapped(callingUserId, notCallingUserId) { - assertThrows(SecurityException::class.java, it) + val target = params.construct(context) + + fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { + // Legacy makes no distinction by UID + val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID + if (throws) { + allUids.forEach { + assertFails { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + } else { + allUids.forEach { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + + // Legacy doesn't use QUERY_ALL, so the invisible package should always fail + allUids.forEach { + assertFails { + runMethod(target, it, visible = false, callingUserId, targetUserId) + } } } + val callingUserId = 0 + val notCallingUserId = 1 + + runTestCases(callingUserId, callingUserId, throws = false) + runTestCases(callingUserId, notCallingUserId, throws = true) + + // Legacy requires the _FULL permission, so this should continue to fail allowInteractAcrossUsers.set(true) + runTestCases(callingUserId, callingUserId, throws = false) + runTestCases(callingUserId, notCallingUserId, throws = true) + + allowInteractAcrossUsersFull.set(true) + runTestCases(callingUserId, callingUserId, throws = false) + runTestCases(callingUserId, notCallingUserId, throws = false) + } - runEachTestCaseWrapped(callingUserId, callingUserId) { - assertThrows(SecurityException::class.java, it) + private fun Context.initPermission(boolean: AtomicBoolean, permission: String) { + whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) { + if (!boolean.get()) { + throw SecurityException() + } } - if (verifyCrossUser) { - runEachTestCaseWrapped(callingUserId, notCallingUserId) { - assertThrows(SecurityException::class.java, it) + whenever(checkPermission(eq(permission), anyInt(), anyInt())) { + if (boolean.get()) { + PackageManager.PERMISSION_GRANTED + } else { + PackageManager.PERMISSION_DENIED } } } - private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) { - params.runMethod(target, callingUid, callingUserId, userId, proxy) + private fun runMethod( + target: Any, + callingUid: Int, + visible: Boolean = true, + callingUserId: Int = 0, + userId: Int = 0 + ): Any? { + val packageName = if (visible) VISIBLE_PKG else INVISIBLE_PKG + val uuid = if (visible) VISIBLE_UUID else INVISIBLE_UUID + return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy) + } + + private fun assertFails(block: () -> Any?) { + try { + val value = block() + // Some methods return false rather than throwing, so check that as well + if ((value as? Boolean) != false) { + // Can also return default value if it's a legacy call + if ((value as? Int) + != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED + ) { + throw AssertionError("Expected call to return false, was $value") + } + } + } catch (e: SecurityException) { + } catch (e: PackageManager.NameNotFoundException) { + } catch (e: DomainVerificationManager.InvalidDomainSetException) { + // Any of these 3 exceptions are considered failures, which is expected + } } enum class Type { @@ -473,6 +687,12 @@ class DomainVerificationEnforcerTest { SELECTOR, // Holding the user setting permission, but targeting cross user - SELECTOR_USER + SELECTOR_USER, + + // Legacy required no permissions except when cross-user + LEGACY_QUERENT, + + // Holding the legacy preferred apps permission + LEGACY_SELECTOR } } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt index 5792e02e37a2..5629d1c1107d 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt @@ -267,5 +267,8 @@ class DomainVerificationSettingsMutationTest { whenever(getPackageLocked(TEST_PKG)) { mockPkg() } whenever(schedule(anyInt(), any())) whenever(scheduleWriteSettings()) + + // This doesn't check for visibility; that's done in the enforcer test + whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false } } } diff --git a/services/tests/inprocesstests/AndroidTest.xml b/services/tests/inprocesstests/AndroidTest.xml index 89abe3c0891c..b541512fdc9e 100644 --- a/services/tests/inprocesstests/AndroidTest.xml +++ b/services/tests/inprocesstests/AndroidTest.xml @@ -24,8 +24,8 @@ </target_preparer> <!-- Restart to clear test code from system server --> - <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> - <option name="teardown-command" value="am restart" /> + <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner"> + <option name="cleanup-action" value="REBOOT" /> </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 7c935d532f22..e7d56a070182 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -11,9 +11,16 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +java_defaults { + name: "FrameworkMockingServicesTests-jni-defaults", + jni_libs: [ + "libactivitymanagermockingservicestestjni", + ], +} android_test { name: "FrameworksMockingServicesTests", + defaults: ["FrameworkMockingServicesTests-jni-defaults"], srcs: ["src/**/*.java", "src/**/*.kt"], @@ -72,4 +79,4 @@ java_library { libs: [ "android.test.runner", ], -}
\ No newline at end of file +} diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp new file mode 100644 index 000000000000..928065a7ebd9 --- /dev/null +++ b/services/tests/mockingservicestests/jni/Android.bp @@ -0,0 +1,33 @@ +cc_library_shared { + name: "libactivitymanagermockingservicestestjni", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], + + srcs: [ + ":lib_cachedAppOptimizer_native", + "onload.cpp", + ], + + include_dirs: [ + "frameworks/base/libs", + "frameworks/native/services", + "system/memory/libmeminfo/include", + ], + + shared_libs: [ + "libandroid", + "libandroid_runtime", + "libbase", + "libbinder", + "liblog", + "libmeminfo", + "libnativehelper", + "libprocessgroup", + "libutils", + ], +} diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp new file mode 100644 index 000000000000..147cc479be18 --- /dev/null +++ b/services/tests/mockingservicestests/jni/onload.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * this is a mini native libaray for cached app optimizer tests to run properly. It + * loads all the native methods necessary. + */ +#include <nativehelper/JNIHelp.h> +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +namespace android { +int register_android_server_am_CachedAppOptimizer(JNIEnv* env); +}; + +using namespace android; + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) +{ + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + ALOGE("GetEnv failed!"); + return result; + } + ALOG_ASSERT(env, "Could not retrieve the env!"); + register_android_server_am_CachedAppOptimizer(env); + return JNI_VERSION_1_4; +} + diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index d86032622e43..56d30ccdf59f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -97,6 +97,7 @@ public final class CachedAppOptimizerTest { @Before public void setUp() { + System.loadLibrary("activitymanagermockingservicestestjni"); mHandlerThread = new HandlerThread(""); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java index 0311920477a1..059744388197 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java @@ -19,6 +19,8 @@ package com.android.server.location.injector; import android.os.IPowerManager; import android.os.PowerManager.LocationPowerSaveMode; +import com.android.server.location.eventlog.LocationEventLog; + /** * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no * change". diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java index c39a77eac23f..6156ba9359cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java @@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; +import com.android.server.location.eventlog.LocationEventLog; import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener; import org.junit.After; diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java index 8e5b16e1ee3a..2822d5c69091 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java @@ -16,9 +16,10 @@ package com.android.server.location.injector; +import com.android.server.location.eventlog.LocationEventLog; + public class TestInjector implements Injector { - private final LocationEventLog mLocationEventLog; private final FakeUserInfoHelper mUserInfoHelper; private final FakeAlarmHelper mAlarmHelper; private final FakeAppOpsHelper mAppOpsHelper; @@ -32,14 +33,17 @@ public class TestInjector implements Injector { private final LocationUsageLogger mLocationUsageLogger; public TestInjector() { - mLocationEventLog = new LocationEventLog(); + this(new LocationEventLog()); + } + + public TestInjector(LocationEventLog eventLog) { mUserInfoHelper = new FakeUserInfoHelper(); mAlarmHelper = new FakeAlarmHelper(); mAppOpsHelper = new FakeAppOpsHelper(); mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper); mSettingsHelper = new FakeSettingsHelper(); mAppForegroundHelper = new FakeAppForegroundHelper(); - mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(mLocationEventLog); + mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(eventLog); mScreenInteractiveHelper = new FakeScreenInteractiveHelper(); mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper); mEmergencyHelper = new FakeEmergencyHelper(); @@ -100,9 +104,4 @@ public class TestInjector implements Injector { public LocationUsageLogger getLocationUsageLogger() { return mLocationUsageLogger; } - - @Override - public LocationEventLog getLocationEventLog() { - return mLocationEventLog; - } } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 54fa89a1e24b..66b037d70a40 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -83,6 +83,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.FgThread; import com.android.server.LocalServices; +import com.android.server.location.eventlog.LocationEventLog; import com.android.server.location.injector.FakeUserInfoHelper; import com.android.server.location.injector.TestInjector; @@ -159,17 +160,19 @@ public class LocationProviderManagerTest { doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class); doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString()); - mInjector = new TestInjector(); + LocationEventLog eventLog = new LocationEventLog(); + + mInjector = new TestInjector(eventLog); mInjector.getUserInfoHelper().startUser(OTHER_USER); - mPassive = new PassiveLocationProviderManager(mContext, mInjector); + mPassive = new PassiveLocationProviderManager(mContext, mInjector, eventLog); mPassive.startManager(); mPassive.setRealProvider(new PassiveLocationProvider(mContext)); mProvider = new TestProvider(PROPERTIES, IDENTITY); mProvider.setProviderAllowed(true); - mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive); + mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive); mManager.startManager(); mManager.setRealProvider(mProvider); } diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 7a0cb8e5dead..31e2b645ac73 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -61,7 +61,7 @@ android_test { libs: [ "android.hardware.power-V1-java", "android.hardware.tv.cec-V1.0-java", - "android.hardware.vibrator-V1-java", + "android.hardware.vibrator-V2-java", "android.hidl.manager-V1.0-java", "android.test.mock", "android.test.base", diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java index 50e7a0395a2a..58d6dae1637a 100644 --- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java +++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java @@ -34,7 +34,7 @@ public class EntropyMixerTest extends AndroidTestCase { assertEquals(0, FileUtils.readTextFile(file, 0, null).length()); // The constructor has the side effect of writing to file - new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath(), "/dev/null"); + new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath()); assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0); } diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java index f7b24920f903..f0d7006633a2 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; @@ -32,6 +33,7 @@ import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -44,6 +46,7 @@ import android.content.pm.PackageManagerInternal; import android.hardware.input.IInputManager; import android.hardware.input.InputManager; import android.hardware.vibrator.IVibrator; +import android.hardware.vibrator.IVibratorManager; import android.media.AudioAttributes; import android.media.AudioManager; import android.os.CombinedVibrationEffect; @@ -204,7 +207,7 @@ public class VibratorManagerServiceTest { public void createService_initializesNativeManagerServiceAndVibrators() { mockVibrators(1, 2); createService(); - verify(mNativeWrapperMock).init(); + verify(mNativeWrapperMock).init(any()); assertTrue(mVibratorProviders.get(1).isInitialized()); assertTrue(mVibratorProviders.get(2).isInitialized()); } @@ -557,8 +560,6 @@ public class VibratorManagerServiceTest { @Test public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception { mockVibrators(1); - mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, - IVibrator.CAP_AMPLITUDE_CONTROL); mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); VibratorManagerService service = createService(); // The native callback will be dispatched manually in this test. @@ -577,6 +578,139 @@ public class VibratorManagerServiceTest { assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); } + @Test + public void vibrate_withTriggerCallback_finishesVibration() throws Exception { + mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE); + mockVibrators(1, 2); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorManagerService service = createService(); + // The native callback will be dispatched manually in this test. + mTestLooper.stopAutoDispatchAndIgnoreExceptions(); + + ArgumentCaptor<VibratorManagerService.OnSyncedVibrationCompleteListener> listenerCaptor = + ArgumentCaptor.forClass( + VibratorManagerService.OnSyncedVibrationCompleteListener.class); + verify(mNativeWrapperMock).init(listenerCaptor.capture()); + + // Mock trigger callback on registered listener. + when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true); + when(mNativeWrapperMock.triggerSynced(anyLong())).then(answer -> { + listenerCaptor.getValue().onComplete(answer.getArgument(0)); + return true; + }); + + VibrationEffect composed = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100) + .compose(); + CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(composed); + + // Wait for vibration to start, it should finish right away with trigger callback. + vibrate(service, effect, ALARM_ATTRS); + + // VibrationThread will start this vibration async, so wait until callback is triggered. + assertTrue(waitUntil(s -> !listenerCaptor.getAllValues().isEmpty(), service, + TEST_TIMEOUT_MILLIS)); + + verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2})); + verify(mNativeWrapperMock).triggerSynced(anyLong()); + assertEquals(Arrays.asList(composed), mVibratorProviders.get(1).getEffects()); + assertEquals(Arrays.asList(composed), mVibratorProviders.get(2).getEffects()); + } + + @Test + public void vibrate_withMultipleVibratorsAndCapabilities_prepareAndTriggerCalled() + throws Exception { + mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_PERFORM, + IVibratorManager.CAP_PREPARE_COMPOSE, IVibratorManager.CAP_MIXED_TRIGGER_PERFORM, + IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE); + mockVibrators(1, 2); + when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true); + when(mNativeWrapperMock.triggerSynced(anyLong())).thenReturn(true); + FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1); + fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorManagerService service = createService(); + + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose()) + .combine(); + vibrate(service, effect, ALARM_ATTRS); + assertTrue(waitUntil(s -> !fakeVibrator1.getEffects().isEmpty(), service, + TEST_TIMEOUT_MILLIS)); + + verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2})); + verify(mNativeWrapperMock).triggerSynced(anyLong()); + verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only. + } + + @Test + public void vibrate_withMultipleVibratorsWithoutCapabilities_skipPrepareAndTrigger() + throws Exception { + // Missing CAP_MIXED_TRIGGER_ON and CAP_MIXED_TRIGGER_PERFORM. + mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON, + IVibratorManager.CAP_PREPARE_PERFORM); + mockVibrators(1, 2); + FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1); + fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK); + VibratorManagerService service = createService(); + + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(2, VibrationEffect.createOneShot(10, 100)) + .combine(); + vibrate(service, effect, ALARM_ATTRS); + assertTrue(waitUntil(s -> !fakeVibrator1.getEffects().isEmpty(), service, + TEST_TIMEOUT_MILLIS)); + + verify(mNativeWrapperMock, never()).prepareSynced(any()); + verify(mNativeWrapperMock, never()).triggerSynced(anyLong()); + verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only. + } + + @Test + public void vibrate_withMultipleVibratorsPrepareFailed_skipTrigger() throws Exception { + mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON); + mockVibrators(1, 2); + when(mNativeWrapperMock.prepareSynced(any())).thenReturn(false); + VibratorManagerService service = createService(); + + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.createOneShot(10, 50)) + .addVibrator(2, VibrationEffect.createOneShot(10, 100)) + .combine(); + vibrate(service, effect, ALARM_ATTRS); + assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(), service, + TEST_TIMEOUT_MILLIS)); + + verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2})); + verify(mNativeWrapperMock, never()).triggerSynced(anyLong()); + verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only. + } + + @Test + public void vibrate_withMultipleVibratorsTriggerFailed_cancelPreparedSynced() throws Exception { + mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON); + mockVibrators(1, 2); + when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true); + when(mNativeWrapperMock.triggerSynced(anyLong())).thenReturn(false); + VibratorManagerService service = createService(); + + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.createOneShot(10, 50)) + .addVibrator(2, VibrationEffect.createOneShot(10, 100)) + .combine(); + vibrate(service, effect, ALARM_ATTRS); + assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(), service, + TEST_TIMEOUT_MILLIS)); + + verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2})); + verify(mNativeWrapperMock).triggerSynced(anyLong()); + verify(mNativeWrapperMock, times(2)).cancelSynced(); // Trigger on service creation too. + } @Test public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception { @@ -682,6 +816,11 @@ public class VibratorManagerServiceTest { assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); } + private void mockCapabilities(long... capabilities) { + when(mNativeWrapperMock.getCapabilities()).thenReturn( + Arrays.stream(capabilities).reduce(0, (a, b) -> a | b)); + } + private void mockVibrators(int... vibratorIds) { for (int vibratorId : vibratorIds) { mVibratorProviders.put(vibratorId, diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java index caa46da306fa..d039a9d6bfe1 100644 --- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -18,14 +18,23 @@ package com.android.server.app; import static org.junit.Assert.assertEquals; +import android.Manifest; import android.app.GameManager; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.pm.PackageManager; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.HashMap; +import java.util.function.Supplier; @RunWith(AndroidJUnit4.class) @SmallTest @@ -37,15 +46,88 @@ public class GameManagerServiceTests { private static final int USER_ID_1 = 1001; private static final int USER_ID_2 = 1002; + // Stolen from ConnectivityServiceTest.MockContext + class MockContext extends ContextWrapper { + private static final String TAG = "MockContext"; + + // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant + private final HashMap<String, Integer> mMockedPermissions = new HashMap<>(); + + MockContext(Context base) { + super(base); + } + + /** + * Mock checks for the specified permission, and have them behave as per {@code granted}. + * + * <p>Passing null reverts to default behavior, which does a real permission check on the + * test package. + * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or + * {@link PackageManager#PERMISSION_DENIED}. + */ + public void setPermission(String permission, Integer granted) { + mMockedPermissions.put(permission, granted); + } + + private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) { + final Integer granted = mMockedPermissions.get(permission); + return granted != null ? granted : ifAbsent.get(); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + return checkMockedPermission( + permission, () -> super.checkPermission(permission, pid, uid)); + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + return checkMockedPermission( + permission, () -> super.checkCallingOrSelfPermission(permission)); + } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + final Integer granted = mMockedPermissions.get(permission); + if (granted == null) { + super.enforceCallingOrSelfPermission(permission, message); + return; + } + + if (!granted.equals(PackageManager.PERMISSION_GRANTED)) { + throw new SecurityException("[Test] permission denied: " + permission); + } + } + } + + @Mock + private MockContext mMockContext; + + @Before + public void setUp() throws Exception { + mMockContext = new MockContext(InstrumentationRegistry.getContext()); + } + + private void mockModifyGameModeGranted() { + mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE, + PackageManager.PERMISSION_GRANTED); + } + + private void mockModifyGameModeDenied() { + mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE, + PackageManager.PERMISSION_DENIED); + } + /** * By default game mode is not supported. */ @Test public void testGameModeDefaultValue() { - GameManagerService gameManagerService = - new GameManagerService(InstrumentationRegistry.getContext()); + GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); + mockModifyGameModeGranted(); + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameManagerService.getGameMode(PACKAGE_NAME_0, USER_ID_1)); } @@ -55,10 +137,11 @@ public class GameManagerServiceTests { */ @Test public void testDefaultValueForNonexistentUser() { - GameManagerService gameManagerService = - new GameManagerService(InstrumentationRegistry.getContext()); + GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); + mockModifyGameModeGranted(); + gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_2); assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_2)); @@ -69,10 +152,11 @@ public class GameManagerServiceTests { */ @Test public void testGameMode() { - GameManagerService gameManagerService = - new GameManagerService(InstrumentationRegistry.getContext()); + GameManagerService gameManagerService = new GameManagerService(mMockContext); gameManagerService.onUserStarting(USER_ID_1); + mockModifyGameModeGranted(); + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1); @@ -83,4 +167,48 @@ public class GameManagerServiceTests { assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); } + + /** + * Test permission.MANAGE_GAME_MODE is checked + */ + @Test + public void testGetGameModePermissionDenied() { + GameManagerService gameManagerService = new GameManagerService(mMockContext); + gameManagerService.onUserStarting(USER_ID_1); + + // Update the game mode so we can read back something valid. + mockModifyGameModeGranted(); + gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1); + assertEquals(GameManager.GAME_MODE_STANDARD, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); + + // Deny permission.MANAGE_GAME_MODE and verify we get back GameManager.GAME_MODE_UNSUPPORTED + mockModifyGameModeDenied(); + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); + } + + /** + * Test permission.MANAGE_GAME_MODE is checked + */ + @Test + public void testSetGameModePermissionDenied() { + GameManagerService gameManagerService = new GameManagerService(mMockContext); + gameManagerService.onUserStarting(USER_ID_1); + + // Update the game mode so we can read back something valid. + mockModifyGameModeGranted(); + gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1); + assertEquals(GameManager.GAME_MODE_STANDARD, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); + + // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated. + mockModifyGameModeDenied(); + gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE, + USER_ID_1); + + mockModifyGameModeGranted(); + assertEquals(GameManager.GAME_MODE_STANDARD, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 8d890b9cd606..7a4b901bdc5e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -103,7 +103,8 @@ public class AuthSessionTest { @Test public void testNewAuthSession_eligibleSensorsSetToStateUnknown() throws RemoteException { setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR); - setupFace(1 /* id */, false /* confirmationAlwaysRequired */); + setupFace(1 /* id */, false /* confirmationAlwaysRequired */, + mock(IBiometricAuthenticator.class)); final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, @@ -118,7 +119,8 @@ public class AuthSessionTest { @Test public void testStartNewAuthSession() throws RemoteException { - setupFace(0 /* id */, false /* confirmationAlwaysRequired */); + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, + mock(IBiometricAuthenticator.class)); setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_REAR); final boolean requireConfirmation = true; @@ -181,9 +183,6 @@ public class AuthSessionTest { final long operationId = 123; final int userId = 10; - final int callingUid = 100; - final int callingPid = 1000; - final int callingUserId = 10000; final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, @@ -220,7 +219,25 @@ public class AuthSessionTest { assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState()); assertEquals(BiometricSensor.STATE_AUTHENTICATING, session.mPreAuthInfo.eligibleSensors.get(0).getSensorState()); + } + + @Test + public void testCancelAuthentication_whenStateAuthCalled_invokesCancel() + throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + 0 /* operationId */, + 0 /* userId */); + + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + session.onCancelAuthSession(false /* force */); + verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE)); } private PreAuthInfo createPreAuthInfo(List<BiometricSensor> sensors, int userId, @@ -282,14 +299,14 @@ public class AuthSessionTest { false /* resetLockoutRequiresHardwareAuthToken */)); } - private void setupFace(int id, boolean confirmationAlwaysRequired) throws RemoteException { - IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); - when(faceAuthenticator.isHardwareDetected(any())).thenReturn(true); - when(faceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + private void setupFace(int id, boolean confirmationAlwaysRequired, + IBiometricAuthenticator authenticator) throws RemoteException { + when(authenticator.isHardwareDetected(any())).thenReturn(true); + when(authenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); mSensors.add(new BiometricSensor(id, TYPE_FACE /* modality */, Authenticators.BIOMETRIC_STRONG /* strength */, - faceAuthenticator) { + authenticator) { @Override boolean confirmationAlwaysRequired(int userId) { return confirmationAlwaysRequired; diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 2d457260b8fe..7dd073499c73 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -27,6 +27,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import android.content.Context; import android.hardware.biometrics.BiometricConstants; @@ -159,8 +161,8 @@ public class BiometricSchedulerTest { // Client 1 cleans up properly verify(listener1).onError(eq(TEST_SENSOR_ID), anyInt(), - eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), eq(0)); - verify(callback1).onClientFinished(eq(client1), eq(true) /* success */); + eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(0)); + verify(callback1).onClientFinished(eq(client1), eq(false) /* success */); verify(callback1, never()).onClientStarted(any()); // Client 2 was able to start @@ -310,6 +312,37 @@ public class BiometricSchedulerTest { assertNull(mScheduler.getCurrentClient()); } + @Test + public void testInterruptPrecedingClients_whenExpected() { + final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class, + withSettings().extraInterfaces(Interruptable.class)); + + final BaseClientMonitor interrupter = mock(BaseClientMonitor.class); + when(interrupter.interruptsPrecedingClients()).thenReturn(true); + + mScheduler.scheduleClientMonitor(interruptableMonitor); + mScheduler.scheduleClientMonitor(interrupter); + waitForIdle(); + + verify((Interruptable) interruptableMonitor).cancel(); + mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */); + } + + @Test + public void testDoesNotInterruptPrecedingClients_whenNotExpected() { + final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class, + withSettings().extraInterfaces(Interruptable.class)); + + final BaseClientMonitor interrupter = mock(BaseClientMonitor.class); + when(interrupter.interruptsPrecedingClients()).thenReturn(false); + + mScheduler.scheduleClientMonitor(interruptableMonitor); + mScheduler.scheduleClientMonitor(interrupter); + waitForIdle(); + + verify((Interruptable) interruptableMonitor, never()).cancel(); + } + private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java index da9dcb79fb76..7cb72c414e52 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java @@ -16,6 +16,7 @@ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -28,6 +29,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.media.AudioManager; @@ -214,4 +216,75 @@ public class DevicePowerStatusActionTest { verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_UNKNOWN); } + + @Test + public void queryDisplayStatus_localDevice_2_0_targetDevice_1_4() throws Exception { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mPlaybackDevice.mAddress, ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(response); + mTestLooper.dispatchAll(); + + verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); + } + + @Test + public void queryDisplayStatus_localDevice_2_0_targetDevice_2_0() throws Exception { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder + .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + HdmiCecMessage reportPowerStatusBroadcast = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, ADDR_BROADCAST, HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(reportPowerStatusBroadcast); + mTestLooper.dispatchAll(); + mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mPlaybackDevice.mAddress, ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus); + + verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); + } + + @Test + public void queryDisplayStatus_localDevice_2_0_targetDevice_2_0_unknown() throws Exception { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder + .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + HdmiCecMessage reportPowerStatusBroadcast = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, ADDR_BROADCAST, HdmiControlManager.POWER_STATUS_UNKNOWN); + mNativeWrapper.onCecMessage(reportPowerStatusBroadcast); + mTestLooper.dispatchAll(); + mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction); + mTestLooper.dispatchAll(); + + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mPlaybackDevice.mAddress, ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + + HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus( + ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(response); + mTestLooper.dispatchAll(); + + verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index e6b56ca3418f..9f0d9829df01 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -67,6 +67,7 @@ public class HdmiCecLocalDevicePlaybackTest { private int mPlaybackLogicalAddress; private boolean mWokenUp; private boolean mStandby; + private boolean mActiveMediaSessionsPaused; @Mock private IPowerManager mIPowerManagerMock; @@ -97,6 +98,11 @@ public class HdmiCecLocalDevicePlaybackTest { } @Override + void pauseActiveMediaSessions() { + mActiveMediaSessionsPaused = true; + } + + @Override boolean isStandbyMessageReceived() { return mStandby; } @@ -392,6 +398,54 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleRoutingChange_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleRoutingChange_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingChange_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingChange_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void handleRoutingInformation_otherDevice_None() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, @@ -496,6 +550,52 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleRoutingInformation_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleRoutingInformation_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingInformation_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleRoutingInformation_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void handleSetStreamPath() { HdmiCecMessage message = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100); @@ -831,6 +931,52 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleActiveSource_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleActiveSource_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleActiveSource_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleActiveSource_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void losingActiveSource_standbyNow_verifyStandbyMessageIsSentOnNextStandby() { // As described in b/161097846. mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( @@ -1158,6 +1304,54 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void handleSetStreamPath_otherDevice_ActiveSource_mediaSessionsPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isTrue(); + } + + @Test + public void handleSetStreamPath_otherDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleSetStreamPath_sameDevice_ActiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test + public void handleSetStreamPath_sameDevice_InactiveSource_mediaSessionsNotPaused() { + mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000, + "HdmiCecLocalDevicePlaybackTest"); + mActiveMediaSessionsPaused = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress); + mHdmiCecLocalDevicePlayback.dispatchMessage(message); + mTestLooper.dispatchAll(); + assertThat(mActiveMediaSessionsPaused).isFalse(); + } + + @Test public void oneTouchPlay_SendStandbyOnSleepToTv() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index e6a238a775c6..ba6011140cf1 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -814,6 +814,7 @@ public class PackageParserTest { assertArrayEquals(a.splitSourceDirs, that.splitSourceDirs); assertArrayEquals(a.splitPublicSourceDirs, that.splitPublicSourceDirs); assertArrayEquals(a.resourceDirs, that.resourceDirs); + assertArrayEquals(a.overlayPaths, that.overlayPaths); assertEquals(a.seInfo, that.seInfo); assertArrayEquals(a.sharedLibraryFiles, that.sharedLibraryFiles); assertEquals(a.dataDir, that.dataDir); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index 938e4cc84e62..7297622b523f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -21,11 +21,14 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import android.content.pm.PackageManager; import android.content.pm.PackageUserState; import android.content.pm.SuspendDialogInfo; +import android.content.pm.overlay.OverlayPaths; import android.os.PersistableBundle; import android.util.ArrayMap; import android.util.ArraySet; @@ -346,4 +349,40 @@ public class PackageUserStateTest { assertLastPackageUsageSet( testState6, PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L); } + + @Test + public void testOverlayPaths() { + final PackageUserState testState = new PackageUserState(); + assertFalse(testState.setOverlayPaths(null)); + assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder().build())); + + assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + + assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build())); + assertFalse(testState.setOverlayPaths(null)); + } + @Test + public void testSharedLibOverlayPaths() { + final PackageUserState testState = new PackageUserState(); + final String LIB_ONE = "lib1"; + final String LIB_TW0 = "lib2"; + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null)); + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, + new OverlayPaths.Builder().build())); + + assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + assertTrue(testState.setSharedLibraryOverlayPaths(LIB_TW0, new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + + assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE, + new OverlayPaths.Builder().build())); + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null)); + } + } diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt index 61252c473530..ff0aec71a0df 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt @@ -248,6 +248,7 @@ open class AndroidPackageParsingTestBase { .ignored("Deferred pre-R, but assigned immediately in R")} requiresSmallestWidthDp=${this.requiresSmallestWidthDp} resourceDirs=${this.resourceDirs?.contentToString()} + overlayPaths=${this.overlayPaths?.contentToString()} roundIconRes=${this.roundIconRes} scanPublicSourceDir=${this.scanPublicSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java new file mode 100644 index 000000000000..70d85b6e0411 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.parsing.library; + +import android.os.Build; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.PackageImpl; +import com.android.server.pm.parsing.pkg.ParsedPackage; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test for {@link AndroidNetIpSecIkeUpdater} + */ +@Presubmit +@SmallTest +@RunWith(JUnit4.class) +public class AndroidNetIpSecIkeUpdaterTest extends PackageSharedLibraryUpdaterTest { + + @Test + public void otherUsesLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.O) + .addUsesLibrary("other") + .addUsesOptionalLibrary("optional") + .addUsesLibrary("android.net.ipsec.ike") + .hideAsParsed()); + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.O) + .addUsesLibrary("other") + .addUsesOptionalLibrary("optional") + .hideAsParsed()) + .hideAsFinal(); + checkBackwardsCompatibility(before, after); + } + + @Test + public void in_usesLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .addUsesLibrary("android.net.ipsec.ike") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .hideAsParsed()) + .hideAsFinal(); + + checkBackwardsCompatibility(before, after); + } + + @Test + public void in_usesOptionalLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .addUsesOptionalLibrary("android.net.ipsec.ike") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .hideAsParsed()) + .hideAsFinal(); + + checkBackwardsCompatibility(before, after); + } + + private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { + checkBackwardsCompatibility(before, after, AndroidNetIpSecIkeUpdater::new); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java index 09c8142105cc..9768f176ea85 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java @@ -165,6 +165,23 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); } + /** + * Ensures that the {@link PackageBackwardCompatibility} uses a + * {@link AndroidNetIpSecIkeUpdater}. + */ + @Test + public void android_net_ipsec_ike_in_usesLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .addUsesLibrary("android.net.ipsec.ike") + .hideAsParsed()); + + ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT); + + checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); + } + private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance); } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 7d208799ee88..3ff8e76a3707 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -19,6 +19,7 @@ package com.android.server.vibrator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -27,8 +28,10 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.hardware.vibrator.IVibrator; +import android.hardware.vibrator.IVibratorManager; import android.os.CombinedVibrationEffect; import android.os.IBinder; import android.os.PowerManager; @@ -388,6 +391,20 @@ public class VibrationThreadTest { } @Test + public void vibrate_singleVibrator_skipsSyncedCallbacks() { + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + waitForCompletion(startThreadAndDispatcher(vibrationId++, + VibrationEffect.createOneShot(10, 100))); + + verify(mThreadCallbacks).onVibrationEnded(anyLong(), eq(Vibration.Status.FINISHED)); + verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any()); + verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong()); + verify(mThreadCallbacks, never()).cancelSyncedVibration(); + } + + @Test public void vibrate_multipleExistingAndMissingVibrators_vibratesOnlyExistingOnes() throws Exception { mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_TICK); @@ -484,8 +501,7 @@ public class VibrationThreadTest { } @Test - public void vibrate_multipleSequential_runsVibrationInOrderWithDelays() - throws Exception { + public void vibrate_multipleSequential_runsVibrationInOrderWithDelays() throws Exception { mockVibrators(1, 2, 3); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); @@ -529,6 +545,126 @@ public class VibrationThreadTest { } @Test + public void vibrate_multipleSyncedCallbackTriggered_finishSteps() throws Exception { + int[] vibratorIds = new int[]{1, 2}; + long vibrationId = 1; + mockVibrators(vibratorIds); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + when(mThreadCallbacks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true); + when(mThreadCallbacks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true); + + VibrationEffect composed = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100) + .compose(); + CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(composed); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + assertTrue(waitUntil(t -> !mVibratorProviders.get(1).getEffects().isEmpty() + && !mVibratorProviders.get(2).getEffects().isEmpty(), thread, TEST_TIMEOUT_MILLIS)); + thread.syncedVibrationComplete(); + waitForCompletion(thread); + + long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_COMPOSE; + verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); + verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId)); + verify(mThreadCallbacks, never()).cancelSyncedVibration(); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + + assertEquals(Arrays.asList(composed), mVibratorProviders.get(1).getEffects()); + assertEquals(Arrays.asList(composed), mVibratorProviders.get(2).getEffects()); + } + + @Test + public void vibrate_multipleSynced_callsPrepareAndTriggerCallbacks() { + int[] vibratorIds = new int[]{1, 2, 3, 4}; + mockVibrators(vibratorIds); + mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true); + when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(true); + + long vibrationId = 1; + VibrationEffect composed = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addVibrator(2, VibrationEffect.createOneShot(10, 100)) + .addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1)) + .addVibrator(4, composed) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + long expectedCap = IVibratorManager.CAP_SYNC + | IVibratorManager.CAP_PREPARE_ON + | IVibratorManager.CAP_PREPARE_PERFORM + | IVibratorManager.CAP_PREPARE_COMPOSE + | IVibratorManager.CAP_MIXED_TRIGGER_ON + | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM + | IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE; + verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); + verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId)); + verify(mThreadCallbacks, never()).cancelSyncedVibration(); + verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + } + + @Test + public void vibrate_multipleSyncedPrepareFailed_skipTriggerStepAndVibrates() { + int[] vibratorIds = new int[]{1, 2}; + mockVibrators(vibratorIds); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(false); + + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.createOneShot(10, 100)) + .addVibrator(2, VibrationEffect.createWaveform(new long[]{5}, new int[]{200}, -1)) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON; + verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); + verify(mThreadCallbacks, never()).triggerSyncedVibration(eq(vibrationId)); + verify(mThreadCallbacks, never()).cancelSyncedVibration(); + + assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(1).getEffects()); + assertEquals(Arrays.asList(100), mVibratorProviders.get(1).getAmplitudes()); + assertEquals(Arrays.asList(expectedOneShot(5)), mVibratorProviders.get(2).getEffects()); + assertEquals(Arrays.asList(200), mVibratorProviders.get(2).getAmplitudes()); + } + + @Test + public void vibrate_multipleSyncedTriggerFailed_cancelPreparedVibrationAndSkipSetAmplitude() { + int[] vibratorIds = new int[]{1, 2}; + mockVibrators(vibratorIds); + mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true); + when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(false); + + long vibrationId = 1; + CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced() + .addVibrator(1, VibrationEffect.createOneShot(10, 100)) + .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .combine(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + long expectedCap = IVibratorManager.CAP_SYNC + | IVibratorManager.CAP_PREPARE_ON + | IVibratorManager.CAP_PREPARE_PERFORM + | IVibratorManager.CAP_MIXED_TRIGGER_ON + | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM; + verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); + verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId)); + verify(mThreadCallbacks).cancelSyncedVibration(); + assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty()); + } + + @Test public void vibrate_multipleWaveforms_playsWaveformsInParallel() throws Exception { mockVibrators(1, 2, 3); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); @@ -623,6 +759,7 @@ public class VibrationThreadTest { assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread, TEST_TIMEOUT_MILLIS)); + assertTrue(vibrationThread.isAlive()); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index b3116d970725..17c941179853 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -15,6 +15,9 @@ */ package com.android.server.notification; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; @@ -138,6 +141,30 @@ public class NotificationAssistantsTest extends UiServiceTestCase { } @Test + public void testXmlMigratingAllowedAdjustments() throws Exception { + // Old tag, need migration + String xml = "<q_allowed_adjustments types=\"adj_1\"/>"; + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml.toString().getBytes())), null); + parser.nextTag(); + mAssistants.readExtraTag("q_allowed_adjustments", parser); + assertTrue(mAssistants.isAdjustmentAllowed("adj_1")); + assertEquals(mNm.DEFAULT_ALLOWED_ADJUSTMENTS.length + 1, + mAssistants.getAllowedAssistantAdjustments().size()); + + // New TAG + xml = "<s_allowed_adjustments types=\"adj_2\"/>"; + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml.toString().getBytes())), null); + parser.nextTag(); + mAssistants.readExtraTag("s_allowed_adjustments", parser); + assertTrue(mAssistants.isAdjustmentAllowed("adj_2")); + assertEquals(1, mAssistants.getAllowedAssistantAdjustments().size()); + } + + @Test public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception { ComponentName component1 = ComponentName.unflattenFromString("package/Component1"); ComponentName component2 = ComponentName.unflattenFromString("package/Component2"); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 0cb1255c6830..9e419d4c3f3b 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1526,15 +1526,19 @@ public class UsageStatsService extends SystemService implements @Override public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime, - long endTime, String callingPackage) { + long endTime, String callingPackage, int userId) { if (!hasPermission(callingPackage)) { return null; } + final int callingUid = Binder.getCallingUid(); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, + userId, false, true, "queryUsageStats", callingPackage); + + // Check the caller's userId for obfuscation decision, not the user being queried final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( - Binder.getCallingUid(), UserHandle.getCallingUserId()); + callingUid, UserHandle.getCallingUserId()); - final int userId = UserHandle.getCallingUserId(); final long token = Binder.clearCallingIdentity(); try { final List<UsageStats> results = UsageStatsService.this.queryUsageStats( diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index be36f82cce7d..f687e4bb9567 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -581,17 +581,24 @@ public class VoiceInteractionManagerService extends SystemService { } } - VoiceInteractionServiceInfo findAvailInteractor(int userHandle, String packageName) { - List<ResolveInfo> available = - mContext.getPackageManager().queryIntentServicesAsUser( - new Intent(VoiceInteractionService.SERVICE_INTERFACE) - .setPackage(packageName), - PackageManager.GET_META_DATA - | PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); + private List<ResolveInfo> queryInteractorServices( + @UserIdInt int user, + @Nullable String packageName) { + return mContext.getPackageManager().queryIntentServicesAsUser( + new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(packageName), + PackageManager.GET_META_DATA + | PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + user); + } + + VoiceInteractionServiceInfo findAvailInteractor( + @UserIdInt int user, + @Nullable String packageName) { + List<ResolveInfo> available = queryInteractorServices(user, packageName); int numAvailable = available.size(); if (numAvailable == 0) { - Slog.w(TAG, "no available voice interaction services found for user " + userHandle); + Slog.w(TAG, "no available voice interaction services found for user " + user); return null; } // Find first system package. We never want to allow third party services to @@ -1643,13 +1650,7 @@ public class VoiceInteractionManagerService extends SystemService { String pkg = roleHolders.get(0); // Try to set role holder as VoiceInteractionService - List<ResolveInfo> services = mPm.queryIntentServicesAsUser( - new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg), - PackageManager.GET_META_DATA - | PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); - - for (ResolveInfo resolveInfo : services) { + for (ResolveInfo resolveInfo : queryInteractorServices(userId, pkg)) { ServiceInfo serviceInfo = resolveInfo.serviceInfo; VoiceInteractionServiceInfo voiceInteractionServiceInfo = diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ac584c175c59..21cf3e57115d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4748,6 +4748,21 @@ public class CarrierConfigManager { public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL = "network_temp_not_metered_supported_bool"; + /* + * Boolean indicating whether the SIM PIN can be stored and verified + * seamlessly after an unattended reboot. + * + * The device configuration value {@code config_allow_pin_storage_for_unattended_reboot} + * ultimately controls whether this carrier configuration option is used. Where + * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of the + * {@link #KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL} carrier configuration option is + * ignored. + * + * @hide + */ + public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = + "store_sim_pin_for_unattended_reboot_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -5304,6 +5319,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true); sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); + sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true); } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 65b8de2b2216..16ffd9e4a44e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -11255,16 +11255,21 @@ public class TelephonyManager { * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) - * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * Additionally, depending on the level of location permissions the caller holds (i.e. no + * location permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, or + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}), location-sensitive fields will + * be cleared from the return value. + * + * <p>Note also that if the caller holds any sort of location permission, a usage event for the + * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} or + * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} + * will be logged against the caller when calling this method. * * May return {@code null} when the subscription is inactive or when there was an error * communicating with the phone process. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(allOf = { - Manifest.permission.READ_PHONE_STATE, - Manifest.permission.ACCESS_COARSE_LOCATION - }) + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) public @Nullable ServiceState getServiceState() { return getServiceStateForSubscriber(getSubId()); } @@ -14994,6 +14999,7 @@ public class TelephonyManager { } return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR; } + /** * Registers a listener object to receive notification of changes * in specified telephony states. @@ -15350,4 +15356,66 @@ public class TelephonyManager { return PhoneCapability.DEFAULT_SSSS_CAPABILITY; } } + + /** + * The unattended reboot was prepared successfully. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; + + /** + * The unattended reboot was prepared, but the user will need to manually + * enter the PIN code of at least one SIM card present in the device. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; + + /** + * The unattended reboot was not prepared due to generic error. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"}, + value = { + PREPARE_UNATTENDED_REBOOT_SUCCESS, + PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED, + PREPARE_UNATTENDED_REBOOT_ERROR + }) + public @interface PrepareUnattendedRebootResult {} + + /** + * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done + * shortly (e.g. within 15 seconds) after the API is invoked. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#REBOOT} + * + * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success. + * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains + * at least one SIM card for which the user needs to manually enter the PIN + * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case + * of error. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.REBOOT) + @PrepareUnattendedRebootResult + public int prepareForUnattendedReboot() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.prepareForUnattendedReboot(); + } + } catch (RemoteException e) { + Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e); + e.rethrowFromSystemServer(); + } + return PREPARE_UNATTENDED_REBOOT_ERROR; + } } diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl new file mode 100644 index 000000000000..0830ff2ff050 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.ims; + +parcelable ImsRegistrationAttributes; diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java new file mode 100644 index 000000000000..ccb3231526dd --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.ims; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.AccessNetworkConstants; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.util.ArraySet; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * Contains the attributes associated with the current IMS registration. + */ +public final class ImsRegistrationAttributes implements Parcelable { + + /** + * Attribute to specify if an EPDG tunnel is setup over the cellular internet APN. + * <p> + * If IMS is registered through an EPDG tunnel is setup over the cellular internet APN then this + * bit will be set. If IMS is registered through the IMS APN, then this bit will not be set. + * + */ + public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1 << 0; + + /** @hide */ + // Defines the underlying radio technology type that we have registered for IMS over. + @IntDef(prefix = "ATTR_", + value = { + ATTR_EPDG_OVER_CELL_INTERNET, + }, + flag = true) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsAttributeFlag {} + + /** + * Builder for creating {@link ImsRegistrationAttributes} instances. + * @hide + */ + @SystemApi + public static final class Builder { + private final int mRegistrationTech; + private Set<String> mFeatureTags = Collections.emptySet(); + + /** + * Build a new instance of {@link ImsRegistrationAttributes}. + * + * @param registrationTech The Radio Access Technology that IMS is registered on. + */ + public Builder(@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) { + mRegistrationTech = registrationTech; + } + + /** + * Optional IMS feature tags included in this IMS registration. + * @param tags A set of Strings containing the MMTEL and RCS feature tags associated with + * the IMS registration. This information is used for services such as the UCE + * service to ascertain the complete IMS registration state to ensure the SIP + * PUBLISH is accurate. The format of the set of feature tags must be one feature + * tag key and value per entry. Each feature tag will contain the feature tag name + * and string value (if applicable), even if they have the same feature tag name. + * For example, + * {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg, + * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} must + * be split into three feature tag entries: + * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg", + * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", + * +g.gsma.callcomposer}}. + */ + public @NonNull Builder setFeatureTags(@NonNull Set<String> tags) { + if (tags == null) { + throw new IllegalArgumentException("feature tag set must not be null"); + } + mFeatureTags = new ArraySet<>(tags); + return this; + } + + /** + * @return A new instance created from this builder. + */ + public @NonNull ImsRegistrationAttributes build() { + return new ImsRegistrationAttributes(mRegistrationTech, + RegistrationManager.getAccessType(mRegistrationTech), + getAttributeFlags(mRegistrationTech), + mFeatureTags); + } + + /** + * @return attribute flags from the registration technology. + */ + private static int getAttributeFlags(int imsRadioTech) { + int attributes = 0; + if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { + attributes |= ATTR_EPDG_OVER_CELL_INTERNET; + } + return attributes; + } + } + + private final int mRegistrationTech; + private final int mTransportType; + private final int mImsAttributeFlags; + private final ArrayList<String> mFeatureTags; + + /** + * Create a new {@link ImsRegistrationAttributes} instance. + * + * @param registrationTech The technology that IMS has been registered on. + * @param transportType The transport type that IMS has been registered on. + * @param imsAttributeFlags The attributes associated with the IMS registration. + * @param featureTags The feature tags included in the IMS registration. + * @see Builder + * @hide + */ + public ImsRegistrationAttributes( + @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech, + @AccessNetworkConstants.TransportType int transportType, + @ImsAttributeFlag int imsAttributeFlags, + @Nullable Set<String> featureTags) { + mRegistrationTech = registrationTech; + mTransportType = transportType; + mImsAttributeFlags = imsAttributeFlags; + mFeatureTags = new ArrayList<>(featureTags); + } + + /**@hide*/ + public ImsRegistrationAttributes(Parcel source) { + mRegistrationTech = source.readInt(); + mTransportType = source.readInt(); + mImsAttributeFlags = source.readInt(); + mFeatureTags = new ArrayList<>(); + source.readList(mFeatureTags, null /*classloader*/); + } + + /** + * @return The Radio Access Technology that the IMS registration has been registered over. + * @hide + */ + @SystemApi + public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTechnology() { + return mRegistrationTech; + } + + /** + * @return The access network transport type that IMS has been registered over. + */ + public @AccessNetworkConstants.TransportType int getTransportType() { + return mTransportType; + } + + /** + * @return A bit-mask containing attributes associated with the IMS registration. + */ + public @ImsAttributeFlag int getAttributeFlags() { + return mImsAttributeFlags; + } + + /** + * Gets the Set of feature tags associated with the current IMS registration, if the IMS + * service supports supplying this information. + * <p> + * The format of the set of feature tags will be one feature tag key and value per entry and + * will potentially contain MMTEL and RCS feature tags, depending the configuration of the IMS + * service associated with the registration indications. Each feature tag will contain the + * feature tag name and string value (if applicable), even if they have the same feature tag + * name. For example, {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg, + * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} will be split + * into three feature tag entries: + * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg", + * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", + * +g.gsma.callcomposer}}. + * @return The Set of feature tags associated with the current IMS registration. + */ + public @NonNull Set<String> getFeatureTags() { + if (mFeatureTags == null) { + return Collections.emptySet(); + } + return new ArraySet<>(mFeatureTags); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mRegistrationTech); + dest.writeInt(mTransportType); + dest.writeInt(mImsAttributeFlags); + dest.writeList(mFeatureTags); + } + + public static final @NonNull Creator<ImsRegistrationAttributes> CREATOR = + new Creator<ImsRegistrationAttributes>() { + @Override + public ImsRegistrationAttributes createFromParcel(Parcel source) { + return new ImsRegistrationAttributes(source); + } + + @Override + public ImsRegistrationAttributes[] newArray(int size) { + return new ImsRegistrationAttributes[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImsRegistrationAttributes that = (ImsRegistrationAttributes) o; + return mRegistrationTech == that.mRegistrationTech + && mTransportType == that.mTransportType + && mImsAttributeFlags == that.mImsAttributeFlags + && Objects.equals(mFeatureTags, that.mFeatureTags); + } + + @Override + public int hashCode() { + return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags); + } + + @Override + public String toString() { + return "ImsRegistrationAttributes { transportType= " + mTransportType + ", attributeFlags=" + + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]}"; + } +} diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index b430befbc024..c682afe8178a 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -72,30 +72,6 @@ public interface RegistrationManager { */ int REGISTRATION_STATE_REGISTERED = 2; - /** - * @hide - */ - // Defines the underlying radio technology type that we have registered for IMS over. - @IntDef(prefix = "ATTR_", - value = { - ATTR_EPDG_OVER_CELL_INTERNET, - }, - flag = true) - @Retention(RetentionPolicy.SOURCE) - public @interface ImsAttributes {} - - /** - * Attribute to specify if EPDG tunnel is setup over cellular internet. - * if EPDG tunnel is setup over cellular internet then this bit will be set else the same will - * not be set. - */ - int ATTR_EPDG_OVER_CELL_INTERNET = 0x00000001; - - //****************************************************************************************** - // Next attribute value: 0x00000002 - //****************************************************************************************** - - /**@hide*/ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN // and WWAN are more accurate constants. @@ -103,7 +79,8 @@ public interface RegistrationManager { new HashMap<Integer, Integer>() {{ // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE // case, since it is defined. - put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1); + put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, + AccessNetworkConstants.TRANSPORT_TYPE_INVALID); put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, @@ -132,6 +109,20 @@ public interface RegistrationManager { } /** + * @param regtech The registration technology. + * @return The Access Network type from registration technology. + * @hide + */ + static int getAccessType(int regtech) { + if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regtech)) { + Log.w("RegistrationManager", "getAccessType - invalid regType returned: " + + regtech); + return AccessNetworkConstants.TRANSPORT_TYPE_INVALID; + } + return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regtech); + } + + /** * Callback class for receiving IMS network Registration callback events. * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) * @see #unregisterImsRegistrationCallback(RegistrationCallback) @@ -149,45 +140,24 @@ public interface RegistrationManager { } @Override - public void onRegistered(int imsRadioTech) { + public void onRegistered(ImsRegistrationAttributes attr) { if (mLocalCallback == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mLocalCallback.onRegistered(getAccessType(imsRadioTech)); - }); - int attributes = 0; - if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { - attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET, - true); - } - final int finalattributes = attributes; - mExecutor.execute(() -> - mLocalCallback.onRegistered(getAccessType(imsRadioTech), - finalattributes)); + mExecutor.execute(() -> mLocalCallback.onRegistered(attr)); } finally { restoreCallingIdentity(callingIdentity); } } @Override - public void onRegistering(int imsRadioTech) { + public void onRegistering(ImsRegistrationAttributes attr) { if (mLocalCallback == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> - mLocalCallback.onRegistering(getAccessType(imsRadioTech))); - int attributes = 0; - if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { - attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET, - true); - } - final int finalattributes = attributes; - mExecutor.execute(() -> - mLocalCallback.onRegistering(getAccessType(imsRadioTech), - finalattributes)); + mExecutor.execute(() -> mLocalCallback.onRegistering(attr)); } finally { restoreCallingIdentity(callingIdentity); } @@ -232,31 +202,6 @@ public interface RegistrationManager { private void setExecutor(Executor executor) { mExecutor = executor; } - - private static int getAccessType(int regType) { - if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) { - Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: " - + regType); - return -1; - } - return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType); - } - - /** - * Changes a attribute bit-mask to add or remove an attribute. - * - * @param bitmask The bit-mask. - * @param bitfield The bit-field to change. - * @param enabled Whether the bit-field should be set or removed. - * @return The bit-mask with the bit-field changed. - */ - private int changeBitmask(int bitmask, int bitfield, boolean enabled) { - if (enabled) { - return bitmask | bitfield; - } else { - return bitmask & ~bitfield; - } - } } private final RegistrationBinder mBinder = new RegistrationBinder(this); @@ -265,7 +210,7 @@ public interface RegistrationManager { * Notifies the framework when the IMS Provider is registered to the IMS network. * * @param imsTransportType the radio access technology. - * @deprecated Use {@link #onRegistered(int, int)} instead. + * @deprecated Use {@link #onRegistered(ImsRegistrationAttributes)} instead. */ @Deprecated public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) { @@ -273,25 +218,20 @@ public interface RegistrationManager { /** * Notifies the framework when the IMS Provider is registered to the IMS network - * with corresponding attributes - * - * @param imsTransportType the radio access technology. - * @param registrationAttributes IMS registration attributes as a bitmap of attributes. - * Possible attributes are following - * <ul> - * <li>{@link #ATTR_EPDG_OVER_CELL_INTERNET}</li> - * </ul> + * with corresponding attributes. * + * @param attributes The attributes associated with this IMS registration. */ - public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType, - @ImsAttributes int registrationAttributes) { + public void onRegistered(@NonNull ImsRegistrationAttributes attributes) { + // Default impl to keep backwards compatibility with old implementations + onRegistered(attributes.getTransportType()); } /** * Notifies the framework when the IMS Provider is trying to register the IMS network. * * @param imsTransportType the radio access technology. - * @deprecated Use {@link #onRegistering(int, int)} instead. + * @deprecated Use {@link #onRegistering(ImsRegistrationAttributes)} instead. */ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) { } @@ -299,12 +239,11 @@ public interface RegistrationManager { /** * Notifies the framework when the IMS Provider is trying to register the IMS network. * - * @param imsTransportType the radio access technology. - * @param registrationAttributes IMS registration attributes as a bitmap of attributes. - * Possible attributes are following + * @param attributes The attributes associated with this IMS registration. */ - public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType, - @ImsAttributes int registrationAttributes) { + public void onRegistering(@NonNull ImsRegistrationAttributes attributes) { + // Default impl to keep backwards compatibility with old implementations + onRegistering(attributes.getTransportType()); } /** diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl index 749b1916962e..179407c983e5 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl @@ -21,6 +21,7 @@ import android.net.Uri; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; /** * See {@link ImsManager#RegistrationCallback} for more information. @@ -28,8 +29,8 @@ import android.telephony.ims.ImsReasonInfo; * {@hide} */ oneway interface IImsRegistrationCallback { - void onRegistered(int imsRadioTech); - void onRegistering(int imsRadioTech); + void onRegistered(in ImsRegistrationAttributes attr); + void onRegistering(in ImsRegistrationAttributes attr); void onDeregistered(in ImsReasonInfo info); void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info); void onSubscriberAssociatedUriChanged(in Uri[] uris); diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 4f753c308f7e..39994be34865 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -18,17 +18,18 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.IntRange; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.RegistrationManager; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.util.RemoteCallbackListExt; import com.android.internal.util.ArrayUtils; @@ -89,7 +90,10 @@ public class ImsRegistrationImplBase { @Override public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { - return getConnectionType(); + synchronized (mLock) { + return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE + : mRegistrationAttributes.getRegistrationTechnology(); + } } @Override @@ -122,8 +126,7 @@ public class ImsRegistrationImplBase { new RemoteCallbackListExt<>(); private final Object mLock = new Object(); // Locked on mLock - private @ImsRegistrationTech - int mConnectionType = REGISTRATION_TECH_NONE; + private ImsRegistrationAttributes mRegistrationAttributes; // Locked on mLock private int mRegistrationState = REGISTRATION_STATE_UNKNOWN; // Locked on mLock, create unspecified disconnect cause. @@ -201,18 +204,24 @@ public class ImsRegistrationImplBase { /** * Notify the framework that the device is connected to the IMS network. * - * @param imsRadioTech the radio access technology. Valid values are defined as - * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and - * {@link #REGISTRATION_TECH_CROSS_SIM}. + * @param imsRadioTech the radio access technology. */ public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { - updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED); + onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); + } + + /** + * Notify the framework that the device is connected to the IMS network. + * + * @param attributes The attributes associated with the IMS registration. + */ + public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) { + updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); mCallbacks.broadcastAction((c) -> { try { - c.onRegistered(imsRadioTech); + c.onRegistered(attributes); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback."); } }); } @@ -220,18 +229,24 @@ public class ImsRegistrationImplBase { /** * Notify the framework that the device is trying to connect the IMS network. * - * @param imsRadioTech the radio access technology. Valid values are defined as - * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and - * {@link #REGISTRATION_TECH_CROSS_SIM}. + * @param imsRadioTech the radio access technology. */ public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { - updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING); + onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); + } + + /** + * Notify the framework that the device is trying to connect the IMS network. + * + * @param attributes The attributes associated with the IMS registration. + */ + public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) { + updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); mCallbacks.broadcastAction((c) -> { try { - c.onRegistering(imsRadioTech); + c.onRegistering(attributes); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback."); } }); } @@ -260,8 +275,7 @@ public class ImsRegistrationImplBase { try { c.onDeregistered(reasonInfo); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); } }); } @@ -281,8 +295,7 @@ public class ImsRegistrationImplBase { try { c.onTechnologyChangeFailed(imsRadioTech, reasonInfo); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " + - "callback."); + Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback."); } }); } @@ -306,14 +319,13 @@ public class ImsRegistrationImplBase { try { callback.onSubscriberAssociatedUriChanged(uris); } catch (RemoteException e) { - Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " - + "callback."); + Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback."); } } - private void updateToState(@ImsRegistrationTech int connType, int newState) { + private void updateToState(ImsRegistrationAttributes attributes, int newState) { synchronized (mLock) { - mConnectionType = connType; + mRegistrationAttributes = attributes; mRegistrationState = newState; mLastDisconnectCause = null; } @@ -325,7 +337,7 @@ public class ImsRegistrationImplBase { mUrisSet = false; mUris = null; - updateToState(REGISTRATION_TECH_NONE, + updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(), RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); if (info != null) { mLastDisconnectCause = info; @@ -337,30 +349,19 @@ public class ImsRegistrationImplBase { } /** - * @return the current registration connection type. Valid values are - * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and - * {@link #REGISTRATION_TECH_CROSS_SIM}. - * @hide - */ - @VisibleForTesting - public final @ImsRegistrationTech int getConnectionType() { - synchronized (mLock) { - return mConnectionType; - } - } - - /** * @param c the newly registered callback that will be updated with the current registration * state. */ private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException { int state; + ImsRegistrationAttributes attributes; ImsReasonInfo disconnectInfo; boolean urisSet; Uri[] uris; synchronized (mLock) { state = mRegistrationState; + attributes = mRegistrationAttributes; disconnectInfo = mLastDisconnectCause; urisSet = mUrisSet; uris = mUris; @@ -371,11 +372,11 @@ public class ImsRegistrationImplBase { break; } case RegistrationManager.REGISTRATION_STATE_REGISTERING: { - c.onRegistering(getConnectionType()); + c.onRegistering(attributes); break; } case RegistrationManager.REGISTRATION_STATE_REGISTERED: { - c.onRegistered(getConnectionType()); + c.onRegistered(attributes); break; } case REGISTRATION_STATE_UNKNOWN: { diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 0cd17da3c0c5..1d049530ba77 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2387,4 +2387,18 @@ interface ITelephony { * Gets the current phone capability. */ PhoneCapability getPhoneCapability(); + + /** + * Prepare TelephonyManager for an unattended reboot. The reboot is + * required to be done shortly after the API is invoked. + * + * Requires system privileges. + * + * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success. + * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains + * at least one SIM card for which the user needs to manually enter the PIN + * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case + * of error. + */ + int prepareForUnattendedReboot(); } diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp index 39dc9c286d5f..e2d2ecaf1684 100644 --- a/tests/ApkVerityTest/Android.bp +++ b/tests/ApkVerityTest/Android.bp @@ -16,7 +16,10 @@ java_test_host { name: "ApkVerityTest", srcs: ["src/**/*.java"], libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"], - static_libs: ["frameworks-base-hostutils"], + static_libs: [ + "block_device_writer_jar", + "frameworks-base-hostutils", + ], test_suites: ["general-tests", "vts"], target_required: [ "block_device_writer_module", diff --git a/tests/ApkVerityTest/OWNERS b/tests/ApkVerityTest/OWNERS new file mode 100644 index 000000000000..d67285ede44a --- /dev/null +++ b/tests/ApkVerityTest/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 36824 + +victorhsieh@google.com diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp index 37fbc29470f6..8f2d4bc70fa0 100644 --- a/tests/ApkVerityTest/block_device_writer/Android.bp +++ b/tests/ApkVerityTest/block_device_writer/Android.bp @@ -51,3 +51,9 @@ cc_test { test_suites: ["general-tests", "pts", "vts"], gtest: false, } + +java_library_host { + name: "block_device_writer_jar", + srcs: ["src/**/*.java"], + libs: ["tradefed", "junit"], +} diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java new file mode 100644 index 000000000000..5c2c15b22bb0 --- /dev/null +++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.blockdevicewriter; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; + +import java.util.ArrayList; + +/** + * Wrapper for block_device_writer command. + * + * <p>To use this class, please push block_device_writer binary to /data/local/tmp. + * 1. In Android.bp, add: + * <pre> + * target_required: ["block_device_writer_module"], + * </pre> + * 2. In AndroidText.xml, add: + * <pre> + * <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + * <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" /> + * </target_preparer> + * </pre> + */ +public final class BlockDeviceWriter { + private static final String EXECUTABLE = "/data/local/tmp/block_device_writer"; + + /** + * Modifies a byte of the file directly against the backing block storage. + * + * The effect can only be observed when the page cache is read from disk again. See + * {@link #dropCaches} for details. + */ + public static void damageFileAgainstBlockDevice(ITestDevice device, String path, + long offsetOfTargetingByte) + throws DeviceNotAvailableException { + assertThat(path).startsWith("/data/"); + ITestDevice.MountPointInfo mountPoint = device.getMountPointInfo("/data"); + ArrayList<String> args = new ArrayList<>(); + args.add(EXECUTABLE); + if ("f2fs".equals(mountPoint.type)) { + args.add("--use-f2fs-pinning"); + } + args.add(mountPoint.filesystem); + args.add(path); + args.add(Long.toString(offsetOfTargetingByte)); + CommandResult result = device.executeShellV2Command(String.join(" ", args)); + assertWithMessage( + String.format("stdout=%s\nstderr=%s", result.getStdout(), result.getStderr())) + .that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS); + } + + /** + * Drops file caches so that the result of {@link #damageFileAgainstBlockDevice} can be + * observed. If a process has an open FD or memory map of the damaged file, cache eviction won't + * happen and the damage cannot be observed. + */ + public static void dropCaches(ITestDevice device) throws DeviceNotAvailableException { + CommandResult result = device.executeShellV2Command( + "sync && echo 1 > /proc/sys/vm/drop_caches"); + assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS); + } + + public static void assertFileNotOpen(ITestDevice device, String path) + throws DeviceNotAvailableException { + CommandResult result = device.executeShellV2Command("lsof " + path); + assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS); + assertThat(result.getStdout()).isEmpty(); + } + + /** + * Checks if the give offset of a file can be read. + * This method will return false if the file has fs-verity enabled and is damaged at the offset. + */ + public static boolean canReadByte(ITestDevice device, String filePath, long offset) + throws DeviceNotAvailableException { + CommandResult result = device.executeShellV2Command( + "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset)); + return result.getStatus() == CommandStatus.SUCCESS; + } +} diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java index d0eb9befbdee..ab3572ba2173 100644 --- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java +++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.fail; import android.platform.test.annotations.RootPermissionTest; +import com.android.blockdevicewriter.BlockDeviceWriter; import com.android.fsverity.AddFsVerityCertRule; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; @@ -334,22 +335,23 @@ public class ApkVerityTest extends BaseHostJUnit4Test { long offsetFirstByte = 0; // The first two pages should be both readable at first. - assertTrue(canReadByte(apkPath, offsetFirstByte)); + assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte)); if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) { - assertTrue(canReadByte(apkPath, offsetFirstByte + FSVERITY_PAGE_SIZE)); + assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, + offsetFirstByte + FSVERITY_PAGE_SIZE)); } // Damage the file directly against the block device. damageFileAgainstBlockDevice(apkPath, offsetFirstByte); // Expect actual read from disk to fail but only at damaged page. - dropCaches(); - assertFalse(canReadByte(apkPath, offsetFirstByte)); + BlockDeviceWriter.dropCaches(mDevice); + assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte)); if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) { long lastByteOfTheSamePage = offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1; - assertFalse(canReadByte(apkPath, lastByteOfTheSamePage)); - assertTrue(canReadByte(apkPath, lastByteOfTheSamePage + 1)); + assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage)); + assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1)); } } @@ -362,21 +364,22 @@ public class ApkVerityTest extends BaseHostJUnit4Test { long offsetOfLastByte = apkSize - 1; // The first two pages should be both readable at first. - assertTrue(canReadByte(apkPath, offsetOfLastByte)); + assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte)); if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) { - assertTrue(canReadByte(apkPath, offsetOfLastByte - FSVERITY_PAGE_SIZE)); + assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, + offsetOfLastByte - FSVERITY_PAGE_SIZE)); } // Damage the file directly against the block device. damageFileAgainstBlockDevice(apkPath, offsetOfLastByte); // Expect actual read from disk to fail but only at damaged page. - dropCaches(); - assertFalse(canReadByte(apkPath, offsetOfLastByte)); + BlockDeviceWriter.dropCaches(mDevice); + assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte)); if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) { long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE; - assertFalse(canReadByte(apkPath, firstByteOfTheSamePage)); - assertTrue(canReadByte(apkPath, firstByteOfTheSamePage - 1)); + assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage)); + assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1)); } } @@ -395,8 +398,8 @@ public class ApkVerityTest extends BaseHostJUnit4Test { // from filesystem cache. Forcing GC workarounds the problem. int retry = 5; for (; retry > 0; retry--) { - dropCaches(); - if (!canReadByte(path, kTargetOffset)) { + BlockDeviceWriter.dropCaches(mDevice); + if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) { break; } try { @@ -451,16 +454,6 @@ public class ApkVerityTest extends BaseHostJUnit4Test { return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim()); } - private void dropCaches() throws DeviceNotAvailableException { - expectRemoteCommandToSucceed("sync && echo 1 > /proc/sys/vm/drop_caches"); - } - - private boolean canReadByte(String filePath, long offset) throws DeviceNotAvailableException { - CommandResult result = mDevice.executeShellV2Command( - "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset)); - return result.getStatus() == CommandStatus.SUCCESS; - } - private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException { CommandResult result = mDevice.executeShellV2Command(cmd); assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS, diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp index d809fe8dad56..43a5078c3c24 100644 --- a/tests/UpdatableSystemFontTest/Android.bp +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -16,8 +16,14 @@ java_test_host { name: "UpdatableSystemFontTest", srcs: ["src/**/*.java"], libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"], - static_libs: ["frameworks-base-hostutils"], + static_libs: [ + "block_device_writer_jar", + "frameworks-base-hostutils", + ], test_suites: ["general-tests", "vts"], + target_required: [ + "block_device_writer_module", + ], data: [ ":NotoColorEmojiTtf", ":UpdatableSystemFontTestCertDer", diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml index efe5d703880c..7b919bd4b114 100644 --- a/tests/UpdatableSystemFontTest/AndroidTest.xml +++ b/tests/UpdatableSystemFontTest/AndroidTest.xml @@ -21,6 +21,7 @@ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> + <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" /> <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" /> <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" /> <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" /> diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java index 6d161a5d7b3a..e249f8a99b0c 100644 --- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java +++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java @@ -21,7 +21,9 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.platform.test.annotations.RootPermissionTest; +import com.android.blockdevicewriter.BlockDeviceWriter; import com.android.fsverity.AddFsVerityCertRule; +import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -34,6 +36,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -126,6 +130,44 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG)); } + @Test + public void reboot() throws Exception { + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG)); + String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(fontPath).startsWith("/data/fonts/files/"); + + expectRemoteCommandToSucceed("stop"); + expectRemoteCommandToSucceed("start"); + waitUntilFontCommandIsReady(); + String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(fontPathAfterReboot).isEqualTo(fontPath); + } + + @Test + public void reboot_clearDamagedFiles() throws Exception { + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG)); + String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(fontPath).startsWith("/data/fonts/files/"); + assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isTrue(); + + BlockDeviceWriter.damageFileAgainstBlockDevice(getDevice(), fontPath, 0); + expectRemoteCommandToSucceed("stop"); + // We have to make sure system_server is gone before dropping caches, because system_server + // process holds font memory maps and prevents cache eviction. + waitUntilSystemServerIsGone(); + BlockDeviceWriter.assertFileNotOpen(getDevice(), fontPath); + BlockDeviceWriter.dropCaches(getDevice()); + assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isFalse(); + + expectRemoteCommandToSucceed("start"); + waitUntilFontCommandIsReady(); + String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertWithMessage("Damaged file should be deleted") + .that(fontPathAfterReboot).startsWith("/system"); + } + private String getFontPath(String fontFileName) throws Exception { // TODO: add a dedicated command for testing. String lines = expectRemoteCommandToSucceed("cmd font dump"); @@ -153,4 +195,39 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { .that(result.getStatus()) .isNotEqualTo(CommandStatus.SUCCESS); } + + private void waitUntilFontCommandIsReady() { + waitUntil(TimeUnit.SECONDS.toMillis(30), () -> { + try { + return getDevice().executeShellV2Command("cmd font status").getStatus() + == CommandStatus.SUCCESS; + } catch (DeviceNotAvailableException e) { + return false; + } + }); + } + + private void waitUntilSystemServerIsGone() { + waitUntil(TimeUnit.SECONDS.toMillis(30), () -> { + try { + return getDevice().executeShellV2Command("pid system_server").getStatus() + == CommandStatus.FAILED; + } catch (DeviceNotAvailableException e) { + return false; + } + }); + } + + private void waitUntil(long timeoutMillis, Supplier<Boolean> func) { + long untilMillis = System.currentTimeMillis() + timeoutMillis; + do { + if (func.get()) return; + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new AssertionError("Interrupted", e); + } + } while (System.currentTimeMillis() < untilMillis); + throw new AssertionError("Timed out"); + } } diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index fcfb4aa9b864..6a09b0237a38 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -35,6 +35,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.REQUEST; import static android.net.NetworkRequest.Type.TRACK_DEFAULT; +import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -329,6 +330,9 @@ public class ConnectivityManagerTest { mustFail(() -> { manager.registerDefaultNetworkCallback(null, handler); }); mustFail(() -> { manager.registerDefaultNetworkCallback(callback, null); }); + mustFail(() -> { manager.registerSystemDefaultNetworkCallback(null, handler); }); + mustFail(() -> { manager.registerSystemDefaultNetworkCallback(callback, null); }); + mustFail(() -> { manager.unregisterNetworkCallback(nullCallback); }); mustFail(() -> { manager.unregisterNetworkCallback(nullIntent); }); mustFail(() -> { manager.releaseNetworkRequest(nullIntent); }); @@ -377,6 +381,13 @@ public class ConnectivityManagerTest { eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), eq(testPkgName), eq(testAttributionTag)); reset(mService); + + Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + manager.registerSystemDefaultNetworkCallback(callback, handler); + verify(mService).requestNetwork(eq(null), + eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(testPkgName), eq(testAttributionTag)); + reset(mService); } static Message makeMessage(NetworkRequest req, int messageType) { diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java new file mode 100644 index 000000000000..2fd5e3861cef --- /dev/null +++ b/tests/net/java/android/net/VpnTransportInfoTest.java @@ -0,0 +1,51 @@ +/* + * 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 android.net; + +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VpnTransportInfoTest { + + @Test + public void testParceling() { + VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); + assertParcelSane(v, 1 /* fieldCount */); + assertParcelingIsLossless(v); + } + + @Test + public void testEqualsAndHashCode() { + VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); + VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE); + VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); + assertNotEquals(v1, v2); + assertEquals(v1, v3); + assertEquals(v1.hashCode(), v3.hashCode()); + } +}
\ No newline at end of file diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index bf6163438ba2..e1fd8f0d7d1d 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -205,6 +205,7 @@ import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; +import android.net.VpnTransportInfo; import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; @@ -1110,7 +1111,7 @@ public class ConnectivityServiceTest { } @Override - public int getActiveAppVpnType() { + public int getActiveVpnType() { return mVpnType; } @@ -1123,10 +1124,12 @@ public class ConnectivityServiceTest { private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp) throws Exception { if (mAgentRegistered) throw new IllegalStateException("already registered"); + updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent"); mConfig = new VpnConfig(); setUids(uids); if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); mInterface = VPN_IFNAME; + mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType())); mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, mNetworkCapabilities); mMockNetworkAgent.waitForIdle(TIMEOUT_MS); @@ -3649,10 +3652,19 @@ public class ConnectivityServiceTest { @Test public void testRegisterDefaultNetworkCallback() throws Exception { + // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_GRANTED); + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); + mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, handler); + systemDefaultCallback.assertNoCallback(); + // Create a TRANSPORT_CELLULAR request to keep the mobile interface up // whenever Wi-Fi is up. Without this, the mobile network agent is // reaped before any other activity can take place. @@ -3667,27 +3679,35 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + systemDefaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring up wifi and expect CALLBACK_AVAILABLE. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); cellNetworkCallback.assertNoCallback(); defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring down cell. Expect no default network callback, since it wasn't the default. mCellNetworkAgent.disconnect(); cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); defaultNetworkCallback.assertNoCallback(); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring up cell. Expect no default network callback, since it won't be the default. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.assertNoCallback(); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring down wifi. Expect the default network callback to notified of LOST wifi // followed by AVAILABLE cell. @@ -3695,19 +3715,25 @@ public class ConnectivityServiceTest { cellNetworkCallback.assertNoCallback(); defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); mCellNetworkAgent.disconnect(); cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); mMockVpn.establishForMyUid(); assertUidRangesUpdatedForMyUid(true); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(null, systemDefaultCallback.getLastAvailableNetwork()); mMockVpn.disconnect(); defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + systemDefaultCallback.assertNoCallback(); waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); } @@ -6134,6 +6160,10 @@ public class ConnectivityServiceTest { @Test public void testVpnNetworkActive() throws Exception { + // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_GRANTED); + final int uid = Process.myUid(); final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); @@ -6141,6 +6171,7 @@ public class ConnectivityServiceTest { final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build(); final NetworkRequest genericRequest = new NetworkRequest.Builder() .removeCapability(NET_CAPABILITY_NOT_VPN).build(); @@ -6154,6 +6185,8 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); mCm.registerDefaultNetworkCallback(defaultCallback); + mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper())); defaultCallback.assertNoCallback(); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); @@ -6163,6 +6196,7 @@ public class ConnectivityServiceTest { genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); vpnNetworkCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -6183,7 +6217,10 @@ public class ConnectivityServiceTest { wifiNetworkCallback.assertNoCallback(); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(mWiFiNetworkAgent.getNetwork(), + systemDefaultCallback.getLastAvailableNetwork()); ranges.clear(); mMockVpn.setUids(ranges); @@ -6200,6 +6237,7 @@ public class ConnectivityServiceTest { // much, but that is the reason the test here has to check for an update to the // capabilities instead of the expected LOST then AVAILABLE. defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + systemDefaultCallback.assertNoCallback(); ranges.add(new UidRange(uid, uid)); mMockVpn.setUids(ranges); @@ -6211,6 +6249,7 @@ public class ConnectivityServiceTest { // TODO : Here like above, AVAILABLE would be correct, but because this can't actually // happen outside of the test, ConnectivityService does not rematch callbacks. defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + systemDefaultCallback.assertNoCallback(); mWiFiNetworkAgent.disconnect(); @@ -6219,6 +6258,7 @@ public class ConnectivityServiceTest { wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); vpnNetworkCallback.assertNoCallback(); defaultCallback.assertNoCallback(); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); mMockVpn.disconnect(); @@ -6227,12 +6267,14 @@ public class ConnectivityServiceTest { wifiNetworkCallback.assertNoCallback(); vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + systemDefaultCallback.assertNoCallback(); assertEquals(null, mCm.getActiveNetwork()); mCm.unregisterNetworkCallback(genericNetworkCallback); mCm.unregisterNetworkCallback(wifiNetworkCallback); mCm.unregisterNetworkCallback(vpnNetworkCallback); mCm.unregisterNetworkCallback(defaultCallback); + mCm.unregisterNetworkCallback(systemDefaultCallback); } @Test @@ -7283,6 +7325,7 @@ public class ConnectivityServiceTest { } private void establishLegacyLockdownVpn() throws Exception { + mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY); // The legacy lockdown VPN only supports userId 0. final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.registerAgent(ranges); @@ -7395,6 +7438,9 @@ public class ConnectivityServiceTest { assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI)); assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); + VpnTransportInfo ti = (VpnTransportInfo) vpnNc.getTransportInfo(); + assertNotNull(ti); + assertEquals(VpnManager.TYPE_VPN_LEGACY, ti.type); // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect. final LinkProperties wifiLp = new LinkProperties(); @@ -8521,11 +8567,7 @@ public class ConnectivityServiceTest { final int myUid = Process.myUid(); setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM); - try { - mService.getConnectionOwnerUid(getTestConnectionInfo()); - fail("Expected SecurityException for non-VpnService app"); - } catch (SecurityException expected) { - } + assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); } @Test @@ -8533,11 +8575,7 @@ public class ConnectivityServiceTest { final int myUid = Process.myUid(); setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE); - try { - mService.getConnectionOwnerUid(getTestConnectionInfo()); - fail("Expected SecurityException for non-VpnService app"); - } catch (SecurityException expected) { - } + assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); } @Test diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 73cc9f129e79..cffd2d1d428f 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.NetworkCallback; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -74,6 +75,7 @@ import android.net.UidRange; import android.net.UidRangeParcel; import android.net.VpnManager; import android.net.VpnService; +import android.net.VpnTransportInfo; import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.os.Build.VERSION_CODES; @@ -984,6 +986,13 @@ public class VpnTest { startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve } + private void assertTransportInfoMatches(NetworkCapabilities nc, int type) { + assertNotNull(nc); + VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo(); + assertNotNull(ti); + assertEquals(type, ti.type); + } + public void startRacoon(final String serverAddr, final String expectedAddr) throws Exception { final ConditionVariable legacyRunnerReady = new ConditionVariable(); @@ -1020,8 +1029,10 @@ public class VpnTest { // Now wait for the runner to be ready before testing for the route. ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class); + ArgumentCaptor<NetworkCapabilities> ncCaptor = + ArgumentCaptor.forClass(NetworkCapabilities.class); verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), - lpCaptor.capture(), any(), anyInt(), any(), anyInt()); + lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt()); // In this test the expected address is always v4 so /32. // Note that the interface needs to be specified because RouteInfo objects stored in @@ -1031,6 +1042,8 @@ public class VpnTest { final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes(); assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes, actualRoutes.contains(expectedRoute)); + + assertTransportInfoMatches(ncCaptor.getValue(), VpnManager.TYPE_VPN_LEGACY); } finally { // Now interrupt the thread, unblock the runner and clean up. vpn.mVpnRunner.exitVpnRunner(); diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java index f9db408462b7..7dada9d1b6d4 100644 --- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -65,7 +65,7 @@ public class VcnManagerTest { ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class); verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture()); - assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + assertTrue(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener)); IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue(); listenerWrapper.onPolicyChanged(); @@ -78,7 +78,7 @@ public class VcnManagerTest { mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener)); verify(mMockVcnManagementService) .addVcnUnderlyingNetworkPolicyListener( any(IVcnUnderlyingNetworkPolicyListener.class)); @@ -88,7 +88,7 @@ public class VcnManagerTest { public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception { mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener)); verify(mMockVcnManagementService, never()) .addVcnUnderlyingNetworkPolicyListener( any(IVcnUnderlyingNetworkPolicyListener.class)); diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 485964487fda..c290bff188c1 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -16,6 +16,10 @@ package com.android.server; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; import static com.android.server.vcn.VcnTestUtils.setupSystemService; @@ -106,6 +110,7 @@ public class VcnManagementServiceTest { Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG)); private static final int TEST_SUBSCRIPTION_ID = 1; + private static final int TEST_SUBSCRIPTION_ID_2 = 2; private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO = new SubscriptionInfo( TEST_SUBSCRIPTION_ID /* id */, @@ -537,59 +542,121 @@ public class VcnManagementServiceTest { Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup)); } - private void verifyMergedNetworkCapabilitiesIsVcnManaged( - NetworkCapabilities mergedCapabilities, @Transport int transportType) { + private void verifyMergedNetworkCapabilities( + NetworkCapabilities mergedCapabilities, + @Transport int transportType, + boolean isVcnManaged, + boolean isRestricted) { assertTrue(mergedCapabilities.hasTransport(transportType)); - assertFalse( + assertEquals( + !isVcnManaged, mergedCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)); + assertEquals( + !isRestricted, + mergedCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)); + } + + private void setupSubscriptionAndStartVcn(int subId, ParcelUuid subGrp, boolean isVcnActive) { + setUpVcnSubscription(subId, subGrp); + final Vcn vcn = startAndGetVcnInstance(subGrp); + doReturn(isVcnActive).when(vcn).isActive(); + } + + private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport( + int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) { + setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive); + + final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder(); + ncBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + if (transport == TRANSPORT_CELLULAR) { + ncBuilder + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)); + } else if (transport == TRANSPORT_WIFI) { + WifiInfo wifiInfo = mock(WifiInfo.class); + when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo); + when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID); + + ncBuilder + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setTransportInfo(wifiInfo); + } else { + throw new IllegalArgumentException("Unknown transport"); + } + + return mVcnMgmtSvc.getUnderlyingNetworkPolicy(ncBuilder.build(), new LinkProperties()); } @Test public void testGetUnderlyingNetworkPolicyCellular() throws Exception { - setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR); - NetworkCapabilities nc = - new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) - .build(); + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), + TRANSPORT_CELLULAR, + true /* isVcnManaged */, + false /* isRestricted */); + } - VcnUnderlyingNetworkPolicy policy = - mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + @Test + public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception { + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, + TEST_UUID_2, + false /* isActive */, + TRANSPORT_CELLULAR); assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilitiesIsVcnManaged( - policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), + NetworkCapabilities.TRANSPORT_CELLULAR, + false /* isVcnManaged */, + false /* isRestricted */); } @Test public void testGetUnderlyingNetworkPolicyWifi() throws Exception { - setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI); - WifiInfo wifiInfo = mock(WifiInfo.class); - when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo); - when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID); - NetworkCapabilities nc = - new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .setTransportInfo(wifiInfo) - .build(); + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), + NetworkCapabilities.TRANSPORT_WIFI, + true /* isVcnManaged */, + true /* isRestricted */); + } - VcnUnderlyingNetworkPolicy policy = - mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + @Test + public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception { + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI); assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilitiesIsVcnManaged( - policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), + NetworkCapabilities.TRANSPORT_WIFI, + false /* isVcnManaged */, + true /* isRestricted */); } @Test public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception { + setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */); + NetworkCapabilities nc = new NetworkCapabilities.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2)) .build(); VcnUnderlyingNetworkPolicy policy = diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py deleted file mode 100755 index 28ff606d0381..000000000000 --- a/tools/hiddenapi/generate_hiddenapi_lists.py +++ /dev/null @@ -1,383 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Generate API lists for non-SDK API enforcement.""" -import argparse -from collections import defaultdict, namedtuple -import functools -import os -import re -import sys - -# Names of flags recognized by the `hiddenapi` tool. -FLAG_SDK = 'sdk' -FLAG_UNSUPPORTED = 'unsupported' -FLAG_BLOCKED = 'blocked' -FLAG_MAX_TARGET_O = 'max-target-o' -FLAG_MAX_TARGET_P = 'max-target-p' -FLAG_MAX_TARGET_Q = 'max-target-q' -FLAG_MAX_TARGET_R = 'max-target-r' -FLAG_CORE_PLATFORM_API = 'core-platform-api' -FLAG_PUBLIC_API = 'public-api' -FLAG_SYSTEM_API = 'system-api' -FLAG_TEST_API = 'test-api' - -# List of all known flags. -FLAGS_API_LIST = [ - FLAG_SDK, - FLAG_UNSUPPORTED, - FLAG_BLOCKED, - FLAG_MAX_TARGET_O, - FLAG_MAX_TARGET_P, - FLAG_MAX_TARGET_Q, - FLAG_MAX_TARGET_R, -] -ALL_FLAGS = FLAGS_API_LIST + [ - FLAG_CORE_PLATFORM_API, - FLAG_PUBLIC_API, - FLAG_SYSTEM_API, - FLAG_TEST_API, -] - -FLAGS_API_LIST_SET = set(FLAGS_API_LIST) -ALL_FLAGS_SET = set(ALL_FLAGS) - -# Option specified after one of FLAGS_API_LIST to indicate that -# only known and otherwise unassigned entries should be assign the -# given flag. -# For example, the max-target-P list is checked in as it was in P, -# but signatures have changes since then. The flag instructs this -# script to skip any entries which do not exist any more. -FLAG_IGNORE_CONFLICTS = "ignore-conflicts" - -# Option specified after one of FLAGS_API_LIST to express that all -# apis within a given set of packages should be assign the given flag. -FLAG_PACKAGES = "packages" - -# Option specified after one of FLAGS_API_LIST to indicate an extra -# tag that should be added to the matching APIs. -FLAG_TAG = "tag" - -# Regex patterns of fields/methods used in serialization. These are -# considered public API despite being hidden. -SERIALIZATION_PATTERNS = [ - r'readObject\(Ljava/io/ObjectInputStream;\)V', - r'readObjectNoData\(\)V', - r'readResolve\(\)Ljava/lang/Object;', - r'serialVersionUID:J', - r'serialPersistentFields:\[Ljava/io/ObjectStreamField;', - r'writeObject\(Ljava/io/ObjectOutputStream;\)V', - r'writeReplace\(\)Ljava/lang/Object;', -] - -# Single regex used to match serialization API. It combines all the -# SERIALIZATION_PATTERNS into a single regular expression. -SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$') - -# Predicates to be used with filter_apis. -HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags) -IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api) - - -class StoreOrderedOptions(argparse.Action): - """An argparse action that stores a number of option arguments in the order that - they were specified. - """ - def __call__(self, parser, args, values, option_string = None): - items = getattr(args, self.dest, None) - if items is None: - items = [] - items.append([option_string.lstrip('-'), values]) - setattr(args, self.dest, items) - -def get_args(): - """Parses command line arguments. - - Returns: - Namespace: dictionary of parsed arguments - """ - parser = argparse.ArgumentParser() - parser.add_argument('--output', required=True) - parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE', - help='CSV files to be merged into output') - - for flag in ALL_FLAGS: - parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE', - action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"') - parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0, - action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned ' - 'entries should be assign the given flag. Must follow a list of entries and applies ' - 'to the preceding such list.') - parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0, - action=StoreOrderedOptions, help='Indicates that the previous list of entries ' - 'is a list of packages. All members in those packages will be given the flag. ' - 'Must follow a list of entries and applies to the preceding such list.') - parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1, - action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. ' - 'Must follow a list of entries and applies to the preceding such list.') - - return parser.parse_args() - - -def read_lines(filename): - """Reads entire file and return it as a list of lines. - - Lines which begin with a hash are ignored. - - Args: - filename (string): Path to the file to read from. - - Returns: - Lines of the file as a list of string. - """ - with open(filename, 'r') as f: - lines = f.readlines(); - lines = filter(lambda line: not line.startswith('#'), lines) - lines = map(lambda line: line.strip(), lines) - return set(lines) - - -def write_lines(filename, lines): - """Writes list of lines into a file, overwriting the file if it exists. - - Args: - filename (string): Path to the file to be writting into. - lines (list): List of strings to write into the file. - """ - lines = map(lambda line: line + '\n', lines) - with open(filename, 'w') as f: - f.writelines(lines) - - -def extract_package(signature): - """Extracts the package from a signature. - - Args: - signature (string): JNI signature of a method or field. - - Returns: - The package name of the class containing the field/method. - """ - full_class_name = signature.split(";->")[0] - # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy - if (full_class_name[0] != "L"): - raise ValueError("Expected to start with 'L': %s" % full_class_name) - full_class_name = full_class_name[1:] - # If full_class_name doesn't contain '/', then package_name will be ''. - package_name = full_class_name.rpartition("/")[0] - return package_name.replace('/', '.') - - -class FlagsDict: - def __init__(self): - self._dict_keyset = set() - self._dict = defaultdict(set) - - def _check_entries_set(self, keys_subset, source): - assert isinstance(keys_subset, set) - assert keys_subset.issubset(self._dict_keyset), ( - "Error: {} specifies signatures not present in code:\n" - "{}" - "Please visit go/hiddenapi for more information.").format( - source, "".join(map(lambda x: " " + str(x) + "\n", keys_subset - self._dict_keyset))) - - def _check_flags_set(self, flags_subset, source): - assert isinstance(flags_subset, set) - assert flags_subset.issubset(ALL_FLAGS_SET), ( - "Error processing: {}\n" - "The following flags were not recognized: \n" - "{}\n" - "Please visit go/hiddenapi for more information.").format( - source, "\n".join(flags_subset - ALL_FLAGS_SET)) - - def filter_apis(self, filter_fn): - """Returns APIs which match a given predicate. - - This is a helper function which allows to filter on both signatures (keys) and - flags (values). The built-in filter() invokes the lambda only with dict's keys. - - Args: - filter_fn : Function which takes two arguments (signature/flags) and returns a boolean. - - Returns: - A set of APIs which match the predicate. - """ - return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset)) - - def get_valid_subset_of_unassigned_apis(self, api_subset): - """Sanitizes a key set input to only include keys which exist in the dictionary - and have not been assigned any API list flags. - - Args: - entries_subset (set/list): Key set to be sanitized. - - Returns: - Sanitized key set. - """ - assert isinstance(api_subset, set) - return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED)) - - def generate_csv(self): - """Constructs CSV entries from a dictionary. - - Old versions of flags are used to generate the file. - - Returns: - List of lines comprising a CSV file. See "parse_and_merge_csv" for format description. - """ - lines = [] - for api in self._dict: - flags = sorted(self._dict[api]) - lines.append(",".join([api] + flags)) - return sorted(lines) - - def parse_and_merge_csv(self, csv_lines, source = "<unknown>"): - """Parses CSV entries and merges them into a given dictionary. - - The expected CSV format is: - <api signature>,<flag1>,<flag2>,...,<flagN> - - Args: - csv_lines (list of strings): Lines read from a CSV file. - source (string): Origin of `csv_lines`. Will be printed in error messages. - - Throws: - AssertionError if parsed flags are invalid. - """ - # Split CSV lines into arrays of values. - csv_values = [ line.split(',') for line in csv_lines ] - - # Update the full set of API signatures. - self._dict_keyset.update([ csv[0] for csv in csv_values ]) - - # Check that all flags are known. - csv_flags = set() - for csv in csv_values: - csv_flags.update(csv[1:]) - self._check_flags_set(csv_flags, source) - - # Iterate over all CSV lines, find entry in dict and append flags to it. - for csv in csv_values: - flags = csv[1:] - if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags): - flags.append(FLAG_SDK) - self._dict[csv[0]].update(flags) - - def assign_flag(self, flag, apis, source="<unknown>", tag = None): - """Assigns a flag to given subset of entries. - - Args: - flag (string): One of ALL_FLAGS. - apis (set): Subset of APIs to receive the flag. - source (string): Origin of `entries_subset`. Will be printed in error messages. - - Throws: - AssertionError if parsed API signatures of flags are invalid. - """ - # Check that all APIs exist in the dict. - self._check_entries_set(apis, source) - - # Check that the flag is known. - self._check_flags_set(set([ flag ]), source) - - # Iterate over the API subset, find each entry in dict and assign the flag to it. - for api in apis: - self._dict[api].add(flag) - if tag: - self._dict[api].add(tag) - - -FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag')) - -def parse_ordered_flags(ordered_flags): - r = [] - currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None - for flag_value in ordered_flags: - flag, value = flag_value[0], flag_value[1] - if flag in ALL_FLAGS_SET: - if currentflag: - r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag)) - ignore_conflicts, packages, tag = False, False, None - currentflag = flag - file = value - else: - if currentflag is None: - raise argparse.ArgumentError('--%s is only allowed after one of %s' % ( - flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET]))) - if flag == FLAG_IGNORE_CONFLICTS: - ignore_conflicts = True - elif flag == FLAG_PACKAGES: - packages = True - elif flag == FLAG_TAG: - tag = value[0] - - - if currentflag: - r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag)) - return r - - -def main(argv): - # Parse arguments. - args = vars(get_args()) - flagfiles = parse_ordered_flags(args['ordered_flags']) - - # Initialize API->flags dictionary. - flags = FlagsDict() - - # Merge input CSV files into the dictionary. - # Do this first because CSV files produced by parsing API stubs will - # contain the full set of APIs. Subsequent additions from text files - # will be able to detect invalid entries, and/or filter all as-yet - # unassigned entries. - for filename in args["csv"]: - flags.parse_and_merge_csv(read_lines(filename), filename) - - # Combine inputs which do not require any particular order. - # (1) Assign serialization API to SDK. - flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION)) - - # (2) Merge text files with a known flag into the dictionary. - for info in flagfiles: - if (not info.ignore_conflicts) and (not info.packages): - flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag) - - # Merge text files where conflicts should be ignored. - # This will only assign the given flag if: - # (a) the entry exists, and - # (b) it has not been assigned any other flag. - # Because of (b), this must run after all strict assignments have been performed. - for info in flagfiles: - if info.ignore_conflicts: - valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file)) - flags.assign_flag(info.flag, valid_entries, filename, info.tag) - - # All members in the specified packages will be assigned the appropriate flag. - for info in flagfiles: - if info.packages: - packages_needing_list = set(read_lines(info.file)) - should_add_signature_to_list = lambda sig,lists: extract_package( - sig) in packages_needing_list and not lists - valid_entries = flags.filter_apis(should_add_signature_to_list) - flags.assign_flag(info.flag, valid_entries, info.file, info.tag) - - # Mark all remaining entries as blocked. - flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED)) - - # Write output. - write_lines(args["output"], flags.generate_csv()) - -if __name__ == "__main__": - main(sys.argv) diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py deleted file mode 100755 index 82d117fbbbab..000000000000 --- a/tools/hiddenapi/generate_hiddenapi_lists_test.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Unit tests for Hidden API list generation.""" -import unittest -from generate_hiddenapi_lists import * - -class TestHiddenapiListGeneration(unittest.TestCase): - - def test_filter_apis(self): - # Initialize flags so that A and B are put on the whitelist and - # C, D, E are left unassigned. Try filtering for the unassigned ones. - flags = FlagsDict() - flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK, - 'C', 'D', 'E']) - filter_set = flags.filter_apis(lambda api, flags: not flags) - self.assertTrue(isinstance(filter_set, set)) - self.assertEqual(filter_set, set([ 'C', 'D', 'E' ])) - - def test_get_valid_subset_of_unassigned_keys(self): - # Create flags where only A is unassigned. - flags = FlagsDict() - flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C']) - flags.assign_flag(FLAG_UNSUPPORTED, set(['C'])) - self.assertEqual(flags.generate_csv(), - [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ]) - - # Check three things: - # (1) B is selected as valid unassigned - # (2) A is not selected because it is assigned 'whitelist' - # (3) D is not selected because it is not a valid key - self.assertEqual( - flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ])) - - def test_parse_and_merge_csv(self): - flags = FlagsDict() - - # Test empty CSV entry. - self.assertEqual(flags.generate_csv(), []) - - # Test new additions. - flags.parse_and_merge_csv([ - 'A,' + FLAG_UNSUPPORTED, - 'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O, - 'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API, - 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API, - 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API, - ]) - self.assertEqual(flags.generate_csv(), [ - 'A,' + FLAG_UNSUPPORTED, - 'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O, - 'C,' + FLAG_SYSTEM_API + ',' + FLAG_SDK, - 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API, - 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API, - ]) - - # Test unknown flag. - with self.assertRaises(AssertionError): - flags.parse_and_merge_csv([ 'Z,foo' ]) - - def test_assign_flag(self): - flags = FlagsDict() - flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B']) - - # Test new additions. - flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ])) - self.assertEqual(flags.generate_csv(), - [ 'A,' + FLAG_UNSUPPORTED + "," + FLAG_SDK, 'B,' + FLAG_UNSUPPORTED ]) - - # Test invalid API signature. - with self.assertRaises(AssertionError): - flags.assign_flag(FLAG_SDK, set([ 'C' ])) - - # Test invalid flag. - with self.assertRaises(AssertionError): - flags.assign_flag('foo', set([ 'A' ])) - - def test_extract_package(self): - signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;' - expected_package = 'com.foo.bar' - self.assertEqual(extract_package(signature), expected_package) - - signature = 'Lcom/foo1/bar/MyClass;->method2()V' - expected_package = 'com.foo1.bar' - self.assertEqual(extract_package(signature), expected_package) - - signature = 'Lcom/foo_bar/baz/MyClass;->method3()V' - expected_package = 'com.foo_bar.baz' - self.assertEqual(extract_package(signature), expected_package) - -if __name__ == '__main__': - unittest.main() diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py deleted file mode 100755 index 6a5b0e1347b2..000000000000 --- a/tools/hiddenapi/merge_csv.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Merge multiple CSV files, possibly with different columns. -""" - -import argparse -import csv -import io - -from zipfile import ZipFile - -args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.') -args_parser.add_argument('--header', help='Comma separated field names; ' - 'if missing determines the header from input files.') -args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.') -args_parser.add_argument('--output', help='Output file for merged CSV.', - default='-', type=argparse.FileType('w')) -args_parser.add_argument('files', nargs=argparse.REMAINDER) -args = args_parser.parse_args() - - -def dict_reader(input): - return csv.DictReader(input, delimiter=',', quotechar='|') - - -if args.zip_input and len(args.files) > 0: - raise ValueError('Expecting either a single ZIP with CSV files' - ' or a list of CSV files as input; not both.') - -csv_readers = [] -if len(args.files) > 0: - for file in args.files: - csv_readers.append(dict_reader(open(file, 'r'))) -elif args.zip_input: - with ZipFile(args.zip_input) as zip: - for entry in zip.namelist(): - if entry.endswith('.uau'): - csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r')))) - -headers = set() -if args.header: - fieldnames = args.header.split(',') -else: - # Build union of all columns from source files: - for reader in csv_readers: - headers = headers.union(reader.fieldnames) - fieldnames = sorted(headers) - -# Concatenate all files to output: -writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL, - dialect='unix', fieldnames=fieldnames) -writer.writeheader() -for reader in csv_readers: - for row in reader: - writer.writerow(row) |